/
WebEngine.py
221 lines (177 loc) · 7.39 KB
/
WebEngine.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import ctk
import qt
import slicer
from slicer.ScriptedLoadableModule import *
#
# WebEngine
#
class WebEngine(ScriptedLoadableModule):
"""Uses ScriptedLoadableModule base class, available at:
https://github.com/Slicer/Slicer/blob/main/Base/Python/slicer/ScriptedLoadableModule.py
"""
def __init__(self, parent):
ScriptedLoadableModule.__init__(self, parent)
parent.title = "WebEngine"
parent.categories = ["Testing.TestCases"]
parent.dependencies = []
parent.contributors = ["Steve Pieper (Isomics)"]
parent.helpText = """
Module to test WebEngine.
"""
parent.acknowledgementText = """
This file was originally developed by Steve Pieper and was partially funded by NSF grant 1759883
""" # replace with organization, grant and thanks.
#
# qWebEngineWidget
#
class WebEngineWidget(ScriptedLoadableModuleWidget):
"""Uses ScriptedLoadableModuleWidget base class, available at:
https://github.com/Slicer/Slicer/blob/main/Base/Python/slicer/ScriptedLoadableModule.py
"""
def __init__(self, parent):
ScriptedLoadableModuleWidget.__init__(self, parent)
self.webWidgets = [] # hold references so windows persist
def setup(self):
ScriptedLoadableModuleWidget.setup(self)
# Instantiate and connect widgets ...
# Collapsible button
sitesCollapsibleButton = ctk.ctkCollapsibleButton()
sitesCollapsibleButton.text = "Sample Sites"
self.layout.addWidget(sitesCollapsibleButton)
# Layout within the collapsible button
sitesFormLayout = qt.QFormLayout(sitesCollapsibleButton)
# site buttons
buttons = []
self.sites = [
{
"label": "Web Console", "url": "http://localhost:1337"
},
{
"label": "Crowds Cure Cancer", "url": "http://cancer.crowds-cure.org"
},
{
"label": "Slicer Home Page", "url": "https://slicer.org"
},
{
"label": "MorphoSource", "url": "https://www.morphosource.org"
},
{
"label": "Slicer SampleData", "url": "https://www.slicer.org/wiki/SampleData"
},
{
"label": "SlicerMorph", "url": "https://slicermorph.github.io"
},
]
for site in self.sites:
button = qt.QPushButton(site["label"])
button.toolTip = "Open %s" % site["url"]
sitesFormLayout.addWidget(button)
onClick = lambda click, site=site: self.onSiteButtonClicked(site)
button.connect('clicked(bool)', onClick)
buttons.append(button)
button = qt.QPushButton("Close All")
button.toolTip = "Close all the web views"
button.connect('clicked(bool)', self.onCloseAll)
self.layout.addWidget(button)
# Add vertical spacer
self.layout.addStretch(1)
def onSiteButtonClicked(self, site):
webWidget = slicer.qSlicerWebWidget()
slicerGeometry = slicer.util.mainWindow().geometry
webWidget.size = qt.QSize(1536, 1024)
webWidget.pos = qt.QPoint(slicerGeometry.x() + 256, slicerGeometry.y() + 128)
webWidget.url = site["url"]
webWidget.show()
self.webWidgets.append(webWidget)
def onCloseAll(self):
for widget in self.webWidgets:
del widget
self.webWidgets = []
class WebEngineTest(ScriptedLoadableModuleTest):
"""
This is the test case for your scripted module.
Uses ScriptedLoadableModuleTest base class, available at:
https://github.com/Slicer/Slicer/blob/main/Base/Python/slicer/ScriptedLoadableModule.py
"""
def setUp(self):
""" Do whatever is needed to reset the state - typically a scene clear will be enough.
"""
self.gotResponse = False
self.gotCorrectResponse = False
def runTest(self):
"""Run as few or as many tests as needed here.
"""
self.setUp()
self.test_WebEngine1()
def onEvalResult(self, js, result):
if js == "valueFromSlicer;":
self.delayDisplay("Got Slicer result back from JavaScript")
self.gotResponse = True
if result == "42":
self.gotCorrectResponse = True
self.delayDisplay("Got the expected result back from JavaScript")
else:
self.delayDisplay("Got a result back from JavaScript")
print((js, result))
def test_WebEngine1(self):
""" Testing WebEngine
"""
self.delayDisplay("Starting the test")
webWidget = slicer.qSlicerWebWidget()
webWidget.size = qt.QSize(1024, 512)
webWidget.webView().url = qt.QUrl("")
webWidget.show()
self.delayDisplay('Showing widget')
webWidget.evalJS("""
const paragraph = document.createElement('p');
paragraph.innerText = 'Hello from Slicer!';
document.body.appendChild(paragraph);
""")
self.delayDisplay('Slicer should be saying hello!')
#
# Test javascript evaluation + use of "evalResult()" signal
#
webWidget.connect("evalResult(QString,QString)", self.onEvalResult)
self.delayDisplay('Slicer setting a javascript value')
webWidget.evalJS("const valueFromSlicer = 42;")
webWidget.evalJS("valueFromSlicer;")
iteration = 0
while not self.gotResponse and iteration < 3:
# Specify an explicit delay to ensure async execution by the
# webengine has completed.
self.delayDisplay('Waiting for response...', msec=500)
iteration += 1
webWidget.disconnect("evalResult(QString,QString)", self.onEvalResult)
if not self.gotResponse:
raise RuntimeError("Never got response from evalJS")
if not self.gotCorrectResponse:
raise AssertionError("Did not get back expected result!")
#
# Test python evaluation from javascript
#
self.delayDisplay('Call a python method')
slicer.app.settings().setValue("WebEngine/AllowPythonExecution", ctk.ctkMessageBox.AcceptRole)
webWidget.evalJS(r"""
let pythonCode = "dialog = qt.QInputDialog(slicer.util.mainWindow())\n";
pythonCode += "dialog.setLabelText('hello')\n";
pythonCode += "dialog.open()\n";
pythonCode += "qt.QTimer.singleShot(1000, dialog.close)\n";
window.slicerPython.evalPython(pythonCode);
""")
self.delayDisplay('Test access to python via js', msec=500)
if hasattr(slicer.modules, 'slicerPythonValueFromJS'):
del slicer.modules.slicerPythonValueFromJS
webWidget.evalJS("""
window.slicerPython.evalPython("slicer.modules.slicerPythonValueFromJS = 42");
""")
iteration = 0
while iteration < 3 and not hasattr(slicer.modules, 'slicerPythonValueFromJS'):
# Specify an explicit delay to ensure async execution by the
# webengine has completed.
self.delayDisplay('Waiting for python value from JS...', msec=500)
iteration += 1
if iteration >= 3:
raise RuntimeError("Couldn't get python value back from JS")
self.delayDisplay('Value of %d received via javascipt' % slicer.modules.slicerPythonValueFromJS)
del slicer.modules.slicerPythonValueFromJS
self.delayDisplay('Test passed!')