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
|
# Copyright 2006-2007 Nanorex, Inc. See LICENSE file for details.
"""
widget_controllers.py - miscellaneous widget-controller classes
@author: bruce
@version: $Id$
@copyright: 2006-2007 Nanorex, Inc. See LICENSE file for details.
"""
# a widget expr controller for a collapsible GroupBox
# (different default style on Windows/Linux and Mac -- but most of the difference
# is in an earlier setup layer which makes the Qt widgets passed to this)
# wants access to 2 iconsets made from given imagenames in the "usual way for this env"
# (can that just be the program env as a whole?)
from PyQt4.Qt import QIcon, SIGNAL
from utilities.qt4transition import qt4todo
import foundation.env as env
def _env_imagename_to_QIcon(imagename, _cache = {}): ### to be replaced with env.imagename_to_QIcon for global env or an arg env
try:
return _cache[imagename]
except KeyError:
pass
## pixmap_fname = imagename # stub
from utilities.icon_utilities import imagename_to_pixmap
pixmap = imagename_to_pixmap(imagename)
pixmap_fname = pixmap # will this arg work too?
res = QIcon()
res.setPixmap(pixmap_fname, QIcon.Automatic)
_cache[imagename] = res # memoize, and also keep permanent python reference
return res
class CollapsibleGroupController_Qt:
open = True #k maybe not needed
def __init__(self, parent, desc, header_refs, hidethese, groupbutton):
## print parent, header_refs
## print hidethese, groupbutton
self.parent = parent # the QDialog subclass with full logic
self.desc = desc # group_desc
self.header_refs = header_refs # just keep python refs to these qt objects in the group header, or other objs related to the group
self.hidethese = hidethese # what to hide and show (and python refs to other objects in the group)
self.groupbutton = groupbutton # the QPushButton in the group header (used in two ways: signal, and change the icon)
self.style = 'Mac' ### set from env
## self.env #e needs imagename_to_QIcon
self.options = self.desc.options
expanded = self.options.get('expanded', True) # these options were set when the desc was interpreted, e.g. "expanded = True"
##e ideally, actual default would come from env
### kluge for now:
if expanded == 'false':
expanded = False
self.set_open(expanded)
# signal for button
self.parent.connect(groupbutton,SIGNAL("clicked()"),self.toggle_open) # was toggle_nt_parameters_grpbtn
return
# runtime logic
def toggle_open(self):
open = not self.open
self.set_open(open)
self.parent.update() # QWidget.update()
#bruce - see if this speeds it up -- not much, but maybe a little, hard to say.
#e would it work better to just change the height? try later.
return
def set_open(self, open):
self.open = open
#e someday, the state might be externally stored... let this be a property ref to self.stateref[self.openkey]
self.set_openclose_icon( self.open )
self.set_children_shown( self.open )
return
def set_openclose_icon(self, open):
"""
#doc
@param open:
@type open: boolean
"""
#e do we want to add a provision for not being collapsible at all?
collapsed = not open
if self.style == 'Mac':
# on Mac, icon shows current state
if collapsed:
imagename = "mac_collapsed_icon.png"
else:
imagename = "mac_expanded_icon.png"
else:
# on Windows, it shows the state you can change it to
if collapsed:
imagename = "win_expand_icon.png"
else:
imagename = "win_collapse_icon.png"
iconset = _env_imagename_to_QIcon(imagename) # memoized
self.groupbutton.setIcon(iconset)
return
def set_children_shown(self, open):
for child in self.hidethese:
if open:
child.show()
else:
child.hide()
return
pass
class FloatLineeditController_Qt:
def __init__(self, parent, desc, lineedit):
self.parent = parent # the QDialog subclass with full logic (note: not the Group it's in -- we don't need to know that)
self.desc = desc # param_desc
self.lineedit = lineedit # a QLineEdit
param = self.desc
# has suffix, min, max, default
### needs a controller to handle the suffix, min, and max, maybe using keybindings; or needs to be floatspinbox
### may need a precision argument, at least to set format for default value
# note: see Qt docs for inputMask property - lets you set up patterns it should look like
precision = 1
format = "%0.1f"
suffix = param.options.get('suffix', '')
self.suffix = suffix #060601
printer = lambda val, format = format, suffix = suffix: format % val + suffix ##e store this
default = param.options.get('default', 0.0) ### will be gotten through an env or state
text = printer(default)
self.lineedit.setText(self.parent._tr(text)) # was "20.0 A" -- btw i doubt it's right to pass it *all* thru __tr
self.default = default
def get_value(self):
text = str(self.lineedit.text())
# remove suffix, or any initial part of it, or maybe any subsequence of it (except for numbers in it)...
# ideal semantics are not very clear!
removed_suffix = False
text = text.strip()
suffix = self.suffix.strip()
if text.endswith(suffix):
# this case is important to always get right
removed_suffix = True
text = text[:-len(suffix)]
text = text.strip()
# now it's either exactly right, or a lost cause -- forget about supporting partially-remaining suffix.
#e should we visually indicate errors somehow? (yes, ideally by color of the text, and tooltip)
try:
return float(text)
except:
### error message or indicator
return self.default
pass
pass
class realtime_update_controller: #bruce 060705, consolidate code from runSim.py, SimSetup.py, and soon MinimizeEnergyProp.py
"""
#doc
"""
def __init__(self, widgets, checkbox = None, checkbox_prefs_key = None):
"""
Set the data widgets, and if given, the checkbox widget and its prefs key, both optional.
If checkbox and its prefs_key are both there, connect them. If neither is provided, always update.
"""
self.watch_motion_buttongroup, self.update_number_spinbox, self.update_units_combobox = widgets
self.checkbox = checkbox
self.checkbox_prefs_key = checkbox_prefs_key
if checkbox and checkbox_prefs_key:
from widgets.prefs_widgets import connect_checkbox_with_boolean_pref
connect_checkbox_with_boolean_pref(checkbox , checkbox_prefs_key)
def set_widgets_from_update_data(self, update_data):
if update_data:
update_number, update_units, update_as_fast_as_possible_data, enable = update_data
if self.checkbox:
self.checkbox.setChecked(enable)
if self.checkbox_prefs_key: #k could be elif, if we connected them above
env.prefs[self.checkbox_prefs_key] = enable
qt4todo('self.watch_motion_buttongroup.setButton( update_as_fast_as_possible_data')
self.update_number_spinbox.setValue( update_number)
for i in range(self.update_units_combobox.count()):
if self.update_units_combobox.itemText(i) == update_units:
self.update_units_combobox.setCurrentIndex(i)
return
raise Exception("update_units_combobox has no such option: " + update_units)
else:
pass # rely on whatever is already in them (better than guessing here)
return
def get_update_data_from_widgets(self):
update_as_fast_as_possible_data = self.watch_motion_buttongroup.checkedId() # 0 means yes, 1 means no (for now)
# ( or -1 means neither, but that's prevented by how the button group is set up, at least when it's enabled)
update_number = self.update_number_spinbox.value() # 1, 2, etc (or perhaps 0??)
update_units = str(self.update_units_combobox.currentText()) # 'frames', 'seconds', 'minutes', 'hours'
if self.checkbox:
enable = self.checkbox.isChecked()
elif self.checkbox_prefs_key:
enable = env.prefs[self.checkbox_prefs_key]
else:
enable = True
return update_number, update_units, update_as_fast_as_possible_data, enable
def update_cond_from_update_data(self, update_data): #e could be a static method
update_number, update_units, update_as_fast_as_possible_data, enable = update_data
if not enable:
return False #e someday we might do this at the end, if the subsequent code is extended to save some prefs
update_as_fast_as_possible = (update_as_fast_as_possible_data != 1)
if env.debug():
print "debug: using update_as_fast_as_possible = %r, update_number, update_units = %r, %r" % \
( update_as_fast_as_possible, update_number, update_units )
pass
if update_as_fast_as_possible:
# This radiobutton might be misnamed; it really means "use the old code,
# i.e. not worse than 20% slowdown, with threshholds".
# It's also ambiguous -- does "fast" mean "fast progress"
# or "often" (which are opposites)? It sort of means "often".
update_cond = ( lambda simtime, pytime, nframes:
simtime >= max(0.05, min(pytime * 4, 2.0)) )
elif update_units == 'frames':
update_cond = ( lambda simtime, pytime, nframes, _nframes = update_number: nframes >= _nframes )
elif update_units == 'seconds':
update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number: simtime + pytime >= _timelimit )
elif update_units == 'minutes':
update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number * 60: simtime + pytime >= _timelimit )
elif update_units == 'hours':
update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number * 3600: simtime + pytime >= _timelimit )
else:
print "don't know how to set update_cond from (%r, %r)" % (update_number, update_units)
update_cond = None # some callers can tolerate this, though it's always a reportable error
return update_cond
def get_update_cond_from_widgets(self):
update_data = self.get_update_data_from_widgets()
return self.update_cond_from_update_data(update_data)
pass # end of class realtime_update_controller
# end
|