-
Notifications
You must be signed in to change notification settings - Fork 1
/
module_utils.py
314 lines (254 loc) · 12.4 KB
/
module_utils.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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# Copyright (c) Charl P. Botha, TU Delft.
# All rights reserved.
# See COPYRIGHT for details.
"""Collection of module utility functions.
@author: Charl P. Botha <http://cpbotha.net/>
"""
import wx
import resources.graphics.images
def create_eoca_buttons(d3module, viewFrame, viewFramePanel,
ok_default=True,
cancel_hotkey=True):
"""Add Execute, OK, Cancel and Apply buttons to the viewFrame.
d3module is the module for which these buttons are being added.
viewFrame is the actual dialog frame.
viewFramePanel is the top-level panel in the frame, i.e. the panel
completely filling the top-level sizer.
IMPORTANT: viewFrame must have a top-level sizer that contains ONLY
the viewFramePanel. This is the default for wxGlade created dialogs
with a top-level panel. The viewFramePanel's sizer must be a
vertical box sizer that contains ANOTHER sizer with a 7 pixel border
all around. These ECAS buttons will be in a sibling sizer to that
ANOTHER sizer.
The buttons will be created with the viewFramePanel as their
parent. They will be added to a horizontal sizer which will then
be added to viewFramePanel.GetSizer(). The viewFramePanel sizer
will then be used to fit the viewFramePanel.
After this, the viewFrame.GetSizer() will be used to fit and
layout the frame itself.
"""
# create the buttons
viewFrame.executeButtonId = wx.NewId()
viewFrame.executeButton = wx.Button(viewFramePanel,
viewFrame.executeButtonId,
"&Execute")
viewFrame.executeButton.SetToolTip(wx.ToolTip(
"Apply all changes, then execute the whole network "\
"(F5 or Alt-E)."))
viewFrame.id_ok_button = wx.ID_OK
viewFrame.ok_button = wx.Button(
viewFramePanel, viewFrame.id_ok_button, "OK")
viewFrame.ok_button.SetToolTip(wx.ToolTip(
"Apply all changes, then close this dialogue (Enter)."))
viewFrame.id_cancel_button = wx.ID_CANCEL
viewFrame.cancel_button = wx.Button(
viewFramePanel, viewFrame.id_cancel_button, "Cancel")
viewFrame.cancel_button.SetToolTip(wx.ToolTip(
"Cancel all changes, then close this dialogue (Esc)."))
viewFrame.applyButtonId = wx.NewId()
viewFrame.applyButton = wx.Button(viewFramePanel,
viewFrame.applyButtonId,
"Apply")
viewFrame.applyButton.SetToolTip(wx.ToolTip(
"Apply all changes, keep this dialogue open."))
# add them to their own sizer, each with a border of 4 pixels on the right
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
for button in (viewFrame.executeButton, viewFrame.ok_button,
viewFrame.cancel_button):
buttonSizer.Add(button, 0, wx.RIGHT, 7)
# except for the right-most button, which has no border
buttonSizer.Add(viewFrame.applyButton, 0)
# add the buttonSizer to the viewFramePanel sizer with a border of 7 pixels
# on the left, right and bottom... remember, this is below a sizer with
# a 7 pixel border all around!
# (we do it with 8, because the default execute button is quite big!)
viewFramePanel.GetSizer().Add(buttonSizer, 0,
wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_RIGHT, 8)
# force the sizer to calculate new layout with all children (because
# we've just added something)
viewFramePanel.GetSizer().Layout()
# fit and setsizehints (autolayout should remain on)
viewFramePanel.GetSizer().Fit(viewFramePanel)
viewFramePanel.GetSizer().SetSizeHints(viewFramePanel)
# now we have to get the top level sizer to do its thing
# WORKAROUND - if we don't remove and add, the
# viewFrame.GetSizer().Layout() below doesn't do anything.
viewFrame.GetSizer().Remove(viewFramePanel)
viewFrame.GetSizer().Add(viewFramePanel, 1, wx.EXPAND, 0)
# WORKAROUND ENDS
viewFrame.GetSizer().Layout() # this should update the minimum size
viewFrame.GetSizer().Fit(viewFrame)
viewFrame.GetSizer().SetSizeHints(viewFrame)
# EVENT BINDINGS
mm = d3module._module_manager
# call back into the graphEditor, if it exists
ge = mm._devide_app.get_interface()._graph_editor
# execute
wx.EVT_BUTTON(viewFrame, viewFrame.executeButtonId,
lambda e: (mm.apply_module_view_to_logic(d3module),
mm.execute_network(d3module)))
# OK (apply and close)
wx.EVT_BUTTON(viewFrame, viewFrame.id_ok_button,
lambda e, vf=viewFrame:
(mm.apply_module_view_to_logic(d3module),
vf.Show(False)))
# Cancel
def helper_cancel():
mm.syncModuleViewWithLogic(d3module)
viewFrame.Show(False)
wx.EVT_BUTTON(viewFrame, viewFrame.id_cancel_button,
lambda e: helper_cancel())
# Apply
wx.EVT_BUTTON(viewFrame, viewFrame.applyButtonId,
lambda e: mm.apply_module_view_to_logic(d3module))
# make sure that OK is the default button
# unless the user specifies otherwise - in frames where we make
# use of an introspection shell, we don't want Enter to executeh
# or Ctrl-Enter to execute the whole network.
accel_list = []
if cancel_hotkey:
# this doesn't work for frames, but I'm keeping it here just
# in case. We use the EVT_CHAR_HOOK later to capture escape
accel_list.append(
(wx.ACCEL_NORMAL, wx.WXK_ESCAPE,
viewFrame.id_cancel_button))
# add F5 hotkey to this dialogue as well so that we can execute
accel_list.append(
(wx.ACCEL_NORMAL, wx.WXK_F5,
viewFrame.executeButtonId))
if ok_default:
viewFrame.ok_button.SetDefault()
accel_list.append(
(wx.ACCEL_NORMAL, wx.WXK_RETURN,
viewFrame.id_ok_button))
# setup some hotkeys as well
viewFrame.SetAcceleratorTable(wx.AcceleratorTable(accel_list))
def handler_evt_char_hook(event):
if event.KeyCode == wx.WXK_ESCAPE:
helper_cancel()
else:
event.Skip()
if cancel_hotkey:
# this is the only way to capture escape on a frame
viewFrame.Bind(wx.EVT_CHAR_HOOK, handler_evt_char_hook)
def create_standard_object_introspection(d3module,
viewFrame, viewFramePanel,
objectDict,
renderWindow=None):
"""Given a devide module and its viewframe, this will create a
standard wxChoice and wxButton (+ labels) UI for object and
introspection. In addition, it'll call setup_object_introspection
in order to bind events to these controls.
In order to use this, the module HAS to use the
IntrospectModuleMixin.
IMPORTANT: viewFrame must have a top-level sizer that contains ONLY
the viewFramePanel. This is the default for wxGlade created dialogs
with a top-level panel. The viewFramePanel's sizer must be a
vertical box sizer. That sizer must contain yet ANOTHER sizer with a 7
pixel border all around. The introspection controls will be created
as a sibling to the ANOTHER sizer. Also see the moduleWriting guide.
"""
introspect_button_id = wx.NewId()
introspect_button = wx.Button(viewFramePanel, introspect_button_id, "Introspect")
ocLabel = wx.StaticText(viewFramePanel, -1, "the")
objectChoiceId = wx.NewId()
objectChoice = wx.Choice(viewFramePanel, objectChoiceId, choices=[])
objectChoice.SetToolTip(wx.ToolTip(
"Select an object from the drop-down box to introspect it."))
hSizer = wx.BoxSizer(wx.HORIZONTAL)
hSizer.Add(introspect_button, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 4)
hSizer.Add(ocLabel, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 4)
hSizer.Add(objectChoice, 1, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 4)
vSizer = wx.BoxSizer(wx.VERTICAL)
sl = wx.StaticLine(viewFramePanel, -1)
vSizer.Add(sl, 0, wx.CENTER|wx.EXPAND|wx.BOTTOM, 7)
vSizer.Add(hSizer, 0, wx.CENTER|wx.EXPAND)
# this will usually get added right below an existing sizer with 7 points
# border all around. Below us the ECAS buttons will be added and these
# assume that there is a 7 pixel border above them, which is why we
# supply a 7 pixel below us.
viewFramePanel.GetSizer().Add(vSizer, 0,
wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 7)
# force the sizer to calculate new layout with all children (because
# we've just added something)
viewFramePanel.GetSizer().Layout()
# fit and setsizehints (autolayout should remain on)
viewFramePanel.GetSizer().Fit(viewFramePanel)
viewFramePanel.GetSizer().SetSizeHints(viewFramePanel)
# now we have to get the top level sizer to do its thing
# WORKAROUND - if we don't remove and add, the
# viewFrame.GetSizer().Layout() below doesn't do anything.
viewFrame.GetSizer().Remove(viewFramePanel)
viewFrame.GetSizer().Add(viewFramePanel, 1, wx.EXPAND, 0)
# WORKAROUND ENDS
viewFrame.GetSizer().Layout() # this should update the minimum size
viewFrame.GetSizer().Fit(viewFrame)
viewFrame.GetSizer().SetSizeHints(viewFrame)
# finally do the actual event setup
setup_object_introspection(d3module, viewFrame, objectDict,
renderWindow,
objectChoice, introspect_button_id)
def get_module_icon():
icon = wx.EmptyIcon()
icon.CopyFromBitmap(
resources.graphics.images.getdevidelogom32x32Bitmap())
return icon
def create_module_view_frame_title(d3module):
mm = d3module._module_manager
return '%s View (%s)' % \
(d3module.__class__.__name__,
mm.get_instance_name(d3module))
def instantiate_module_view_frame(d3module, module_manager, frameClass):
# instantiate the frame
pw = module_manager.get_module_view_parent_window()
# name becomes the WM_CLASS under X
viewFrame = frameClass(pw, -1, 'dummy', name='DeVIDE')
# make sure that it's only hidden when it's closed
wx.EVT_CLOSE(viewFrame,
lambda e: viewFrame.Show(False))
# set its title (is there not an easier way to get the class name?)
viewFrame.SetTitle(create_module_view_frame_title(d3module))
# set its icon!
viewFrame.SetIcon(get_module_icon())
return viewFrame
def setup_object_introspection(d3module, viewFrame, objectDict,
renderWindow,
objectChoice, introspect_button_id,
):
"""Setup all object introspection for standard module views with a
choice for object introspection. Call this if you have a
wx.Choice and wx.Button ready!
viewFrame is the actual window of the module view.
objectDict is a dictionary with object name strings as keys and object
instances as values.
renderWindow is an optional renderWindow that'll be used for updating,
pass as None if you don't have this.
objectChoice is the object choice widget.
objectChoiceId is the event id connected to the objectChoice widget.
In order to use this, the module HAS to use the
IntrospectModuleMixin.
"""
# fill out the objectChoice with the object names
objectChoice.Clear()
for objectName in objectDict.keys():
objectChoice.Append(objectName)
# default on first object
objectChoice.SetSelection(0)
# setup the two default callbacks
wx.EVT_BUTTON(viewFrame, introspect_button_id,
lambda e: d3module._defaultObjectChoiceCallback(
viewFrame, renderWindow, objectChoice, objectDict))
def setup_vtk_object_progress(d3module, obj, progressText):
# we DON'T use SetProgressMethod, as the handler object then needs
# to store a binding to the vtkProcessObject, which means that
# the objects never die... this way, there are no refs
# in addition, the AddObserver is the standard way for doing this
# we should probably not use ProgressText though...
obj.SetProgressText(progressText)
mm = d3module._module_manager
obj.AddObserver(
'ProgressEvent', lambda vtko, name:
mm.generic_progress_callback(vtko,
vtko.GetClassName(),
vtko.GetProgress(),
progressText))