1 | import math |
||
2 | import os |
||
3 | import re |
||
4 | import itertools |
||
5 | |||
6 | import pkg_resources |
||
7 | import numpy |
||
0 ignored issues
–
show
|
|||
8 | |||
9 | from PyQt4 import QtGui, QtCore, QtWebKit |
||
0 ignored issues
–
show
The import
PyQt4 could not be resolved.
This can be caused by one of the following: 1. Missing DependenciesThis error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands. # .scrutinizer.yml
before_commands:
- sudo pip install abc # Python2
- sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use
the command for the correct version.
2. Missing __init__.py filesThis error could also result from missing ![]() |
|||
10 | from PyQt4.QtCore import Qt, pyqtSignal as Signal |
||
0 ignored issues
–
show
The import
PyQt4.QtCore could not be resolved.
This can be caused by one of the following: 1. Missing DependenciesThis error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands. # .scrutinizer.yml
before_commands:
- sudo pip install abc # Python2
- sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use
the command for the correct version.
2. Missing __init__.py filesThis error could also result from missing ![]() |
|||
11 | from PyQt4.QtGui import QCursor, QApplication |
||
0 ignored issues
–
show
The import
PyQt4.QtGui could not be resolved.
This can be caused by one of the following: 1. Missing DependenciesThis error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands. # .scrutinizer.yml
before_commands:
- sudo pip install abc # Python2
- sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use
the command for the correct version.
2. Missing __init__.py filesThis error could also result from missing ![]() |
|||
12 | |||
13 | import Orange.data |
||
14 | from Orange.widgets.utils import getdeepattr |
||
15 | from Orange.data import ContinuousVariable, StringVariable, DiscreteVariable, Variable |
||
16 | from Orange.widgets.utils import vartype |
||
17 | from Orange.widgets.utils.constants import CONTROLLED_ATTRIBUTES, ATTRIBUTE_CONTROLLERS |
||
18 | |||
19 | YesNo = NoYes = ("No", "Yes") |
||
20 | _enter_icon = None |
||
21 | __re_label = re.compile(r"(^|[^%])%\((?P<value>[a-zA-Z]\w*)\)") |
||
22 | |||
23 | |||
24 | OrangeUserRole = itertools.count(Qt.UserRole) |
||
25 | |||
26 | |||
27 | def resource_filename(path): |
||
28 | """ |
||
29 | Return a resource filename (package data) for path. |
||
30 | """ |
||
31 | return pkg_resources.resource_filename(__name__, path) |
||
32 | |||
33 | |||
34 | class TableWidget(QtGui.QTableWidget): |
||
35 | """ An easy to use, row-oriented table widget """ |
||
36 | |||
37 | ROW_DATA_ROLE = QtCore.Qt.UserRole + 1 |
||
38 | ITEM_DATA_ROLE = ROW_DATA_ROLE + 1 |
||
39 | |||
40 | class TableWidgetNumericItem(QtGui.QTableWidgetItem): |
||
41 | """TableWidgetItem that sorts numbers correctly!""" |
||
42 | def __lt__(self, other): |
||
43 | return (self.data(TableWidget.ITEM_DATA_ROLE) < |
||
44 | other.data(TableWidget.ITEM_DATA_ROLE)) |
||
45 | |||
46 | def selectionChanged(self, selected:[QtGui.QItemSelectionRange], deselected:[QtGui.QItemSelectionRange]): |
||
0 ignored issues
–
show
This method could be written as a function/class method.
If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example class Foo:
def some_method(self, x, y):
return x + y;
could be written as class Foo:
@classmethod
def some_method(cls, x, y):
return x + y;
![]() |
|||
47 | """Override or monkey-patch this method to catch selection changes""" |
||
48 | super().selectionChanged(selected, deselected) |
||
49 | |||
50 | def __setattr__(self, attr, value): |
||
51 | """ |
||
52 | The following selectionChanged magic ensures selectionChanged |
||
53 | slot, when monkey-patched, always calls the super's selectionChanged |
||
54 | first (--> avoids Qt quirks), and the user needs not care about that. |
||
55 | """ |
||
56 | if attr == 'selectionChanged': |
||
57 | func = value |
||
58 | @QtCore.pyqtSlot(QtGui.QItemSelection, QtGui.QItemSelection) |
||
59 | def _f(selected, deselected): |
||
60 | super(self.__class__, self).selectionChanged(selected, deselected) |
||
61 | func(selected, deselected) |
||
62 | value = _f |
||
63 | self.__dict__[attr] = value |
||
64 | |||
65 | def _update_headers(func): |
||
0 ignored issues
–
show
|
|||
66 | """Decorator to update certain table features after method calls""" |
||
67 | def _f(self, *args, **kwargs): |
||
68 | func(self, *args, **kwargs) |
||
69 | if self.col_labels is not None: |
||
70 | self.setHorizontalHeaderLabels(self.col_labels) |
||
71 | if self.row_labels is not None: |
||
72 | self.setVerticalHeaderLabels(self.row_labels) |
||
73 | if self.stretch_last_section: |
||
74 | self.horizontalHeader().setStretchLastSection(True) |
||
75 | return _f |
||
76 | |||
77 | @_update_headers |
||
78 | def __init__(self, |
||
79 | parent=None, |
||
80 | col_labels=None, |
||
81 | row_labels=None, |
||
82 | stretch_last_section=True, |
||
83 | multi_selection=False, |
||
84 | select_rows=False): |
||
85 | """ |
||
86 | Parameters |
||
87 | ---------- |
||
88 | parent: QObject |
||
89 | Parent QObject. If parent has layout(), this widget is added to it. |
||
90 | col_labels: list of str |
||
91 | Labels or [] (sequential numbers) or None (no horizontal header) |
||
92 | row_label: list_of_str |
||
93 | Labels or [] (sequential numbers) or None (no vertical header) |
||
94 | stretch_last_section: bool |
||
95 | multi_selection: bool |
||
96 | Single selection if False |
||
97 | select_rows: bool |
||
98 | If True, select whole rows instead of individual cells. |
||
99 | """ |
||
100 | super().__init__(parent) |
||
101 | self._column_filter = {} |
||
102 | self.col_labels = col_labels |
||
103 | self.row_labels = row_labels |
||
104 | self.stretch_last_section = stretch_last_section |
||
105 | try: parent.layout().addWidget(self) |
||
106 | except (AttributeError, TypeError): pass |
||
0 ignored issues
–
show
|
|||
107 | if col_labels is None: |
||
108 | self.horizontalHeader().setVisible(False) |
||
109 | if row_labels is None: |
||
110 | self.verticalHeader().setVisible(False) |
||
111 | if multi_selection: |
||
112 | self.setSelectionMode(self.MultiSelection) |
||
113 | if select_rows: |
||
114 | self.setSelectionBehavior(self.SelectRows) |
||
115 | self.setHorizontalScrollMode(self.ScrollPerPixel) |
||
116 | self.setVerticalScrollMode(self.ScrollPerPixel) |
||
117 | self.setEditTriggers(self.NoEditTriggers) |
||
118 | self.setAlternatingRowColors(True) |
||
119 | self.setShowGrid(False) |
||
120 | self.setSortingEnabled(True) |
||
121 | |||
122 | @_update_headers |
||
123 | def addRow(self, items:tuple, data=None): |
||
124 | """ |
||
125 | Appends iterable of `items` as the next row, optionally setting row |
||
126 | data to `data`. Each item of `items` can be a string or tuple |
||
127 | (item_name, item_data) if individual, cell-data is required. |
||
128 | """ |
||
129 | row_data = data |
||
130 | row = self.rowCount() |
||
131 | self.insertRow(row) |
||
132 | col_count = max(len(items), self.columnCount()) |
||
133 | if col_count != self.columnCount(): |
||
134 | self.setColumnCount(col_count) |
||
135 | for col, item_data in enumerate(items): |
||
136 | if isinstance(item_data, str): |
||
137 | name = item_data |
||
138 | elif hasattr(item_data, '__iter__') and len(item_data) == 2: |
||
139 | name, item_data = item_data |
||
140 | elif isinstance(item_data, float): |
||
141 | name = '{:.4f}'.format(item_data) |
||
142 | else: |
||
143 | name = str(item_data) |
||
144 | if isinstance(item_data, (float, int, numpy.number)): |
||
145 | item = self.TableWidgetNumericItem(name) |
||
146 | else: |
||
147 | item = QtGui.QTableWidgetItem(name) |
||
148 | item.setData(self.ITEM_DATA_ROLE, item_data) |
||
149 | if col in self._column_filter: |
||
150 | item = self._column_filter[col](item) or item |
||
151 | self.setItem(row, col, item) |
||
152 | self.resizeColumnsToContents() |
||
153 | self.resizeRowsToContents() |
||
154 | if row_data is not None: |
||
155 | self.setRowData(row, row_data) |
||
156 | |||
157 | def rowData(self, row:int): |
||
158 | return self.item(row, 0).data(self.ROW_DATA_ROLE) |
||
159 | |||
160 | def setRowData(self, row:int, data): |
||
161 | self.item(row, 0).setData(self.ROW_DATA_ROLE, data) |
||
162 | |||
163 | def setColumnFilter(self, item_filter_func, columns:int or list): |
||
164 | """ |
||
165 | Pass item(s) at column(s) through `item_filter_func` before |
||
166 | insertion. Useful for setting specific columns to bold or similar. |
||
167 | """ |
||
168 | try: iter(columns) |
||
169 | except TypeError: columns = [columns] |
||
170 | for i in columns: |
||
171 | self._column_filter[i] = item_filter_func |
||
172 | |||
173 | def clear(self): |
||
174 | super().clear() |
||
175 | self.setRowCount(0) |
||
176 | self.setColumnCount(0) |
||
177 | |||
178 | def selectFirstRow(self): |
||
179 | if self.rowCount() > 0: |
||
180 | self.selectRow(0) |
||
181 | |||
182 | def selectRowsWhere(self, col, value, n_hits=-1, |
||
183 | flags=QtCore.Qt.MatchExactly, _select=True): |
||
184 | """ |
||
185 | Select (also return) at most `n_hits` rows where column `col` |
||
186 | has value (``data()``) `value`. |
||
187 | """ |
||
188 | model = self.model() |
||
189 | matches = model.match(model.index(0, col), |
||
190 | self.ITEM_DATA_ROLE, |
||
191 | value, |
||
192 | n_hits, |
||
193 | flags) |
||
194 | model = self.selectionModel() |
||
195 | selection_flag = model.Select if _select else model.Deselect |
||
196 | for index in matches: |
||
197 | if _select ^ model.isSelected(index): |
||
198 | model.select(index, selection_flag | model.Rows) |
||
199 | return matches |
||
200 | |||
201 | def deselectRowsWhere(self, col, value, n_hits=-1, |
||
202 | flags=QtCore.Qt.MatchExactly): |
||
203 | """ |
||
204 | Deselect (also return) at most `n_hits` rows where column `col` |
||
205 | has value (``data()``) `value`. |
||
206 | """ |
||
207 | return self.selectRowsWhere(col, value, n_hits, flags, False) |
||
208 | |||
209 | |||
210 | class WebviewWidget(QtWebKit.QWebView): |
||
211 | """WebKit window in a window""" |
||
212 | def __init__(self, parent=None, bridge=None, html=None, debug=None): |
||
213 | """ |
||
214 | Parameters |
||
215 | ---------- |
||
216 | parent: QObject |
||
217 | Parent QObject. If parent has layout(), this widget is added to it. |
||
218 | bridge: QObject |
||
219 | The "bridge" object exposed as ``window.pybridge`` in JavaScript. |
||
220 | Any bridge methods desired to be accessible from JS need to be |
||
221 | decorated ``@QtCore.pyqtSlot(<*args>, result=<type>)``. |
||
222 | html: str |
||
223 | HTML content to set in the webview. |
||
224 | debug: bool |
||
225 | If True, enable context menu and webkit inspector. |
||
226 | """ |
||
227 | super().__init__(parent) |
||
228 | self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, |
||
229 | QtGui.QSizePolicy.Expanding)) |
||
230 | self._bridge = bridge |
||
231 | try: parent.layout().addWidget(self) |
||
232 | except (AttributeError, TypeError): pass |
||
0 ignored issues
–
show
|
|||
233 | settings = self.settings() |
||
234 | settings.setAttribute(settings.LocalContentCanAccessFileUrls, True) |
||
235 | if debug is None: |
||
236 | import logging |
||
237 | debug = logging.getLogger().level <= logging.DEBUG |
||
238 | if debug: |
||
239 | settings.setAttribute(settings.DeveloperExtrasEnabled, True) |
||
240 | else: |
||
241 | self.setContextMenuPolicy(QtCore.Qt.NoContextMenu) |
||
242 | if html: |
||
243 | self.setHtml(html) |
||
244 | |||
245 | def setContent(self, data, mimetype, url=''): |
||
246 | super().setContent(data, mimetype, QtCore.QUrl(url)) |
||
247 | if self._bridge: |
||
248 | self.page().mainFrame().addToJavaScriptWindowObject('pybridge', self._bridge) |
||
249 | |||
250 | def setHtml(self, html, url=''): |
||
251 | self.setContent(html.encode('utf-8'), 'text/html', url) |
||
252 | |||
253 | def sizeHint(self): |
||
0 ignored issues
–
show
This method could be written as a function/class method.
If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example class Foo:
def some_method(self, x, y):
return x + y;
could be written as class Foo:
@classmethod
def some_method(cls, x, y):
return x + y;
![]() |
|||
254 | return QtCore.QSize(600, 500) |
||
255 | |||
256 | def evalJS(self, javascript): |
||
257 | self.page().mainFrame().evaluateJavaScript(javascript) |
||
258 | |||
259 | |||
260 | class ControlledAttributesDict(dict): |
||
261 | def __init__(self, master): |
||
262 | super().__init__() |
||
263 | self.master = master |
||
264 | |||
265 | def __setitem__(self, key, value): |
||
266 | if key not in self: |
||
267 | dict.__setitem__(self, key, [value]) |
||
268 | else: |
||
269 | dict.__getitem__(self, key).append(value) |
||
270 | set_controllers(self.master, key, self.master, "") |
||
271 | |||
272 | callbacks = lambda obj: getattr(obj, CONTROLLED_ATTRIBUTES, {}) |
||
273 | subcontrollers = lambda obj: getattr(obj, ATTRIBUTE_CONTROLLERS, {}) |
||
274 | |||
275 | |||
276 | def notify_changed(obj, name, value): |
||
277 | if name in callbacks(obj): |
||
278 | for callback in callbacks(obj)[name]: |
||
279 | callback(value) |
||
280 | return |
||
281 | |||
282 | for controller, prefix in list(subcontrollers(obj)): |
||
283 | if getdeepattr(controller, prefix, None) != obj: |
||
284 | del subcontrollers(obj)[(controller, prefix)] |
||
285 | continue |
||
286 | |||
287 | full_name = prefix + "." + name |
||
288 | if full_name in callbacks(controller): |
||
289 | for callback in callbacks(controller)[full_name]: |
||
290 | callback(value) |
||
291 | continue |
||
292 | |||
293 | prefix = full_name + "." |
||
294 | prefix_length = len(prefix) |
||
295 | for controlled in callbacks(controller): |
||
296 | if controlled[:prefix_length] == prefix: |
||
297 | set_controllers(value, controlled[prefix_length:], controller, full_name) |
||
298 | |||
299 | |||
300 | def set_controllers(obj, controlled_name, controller, prefix): |
||
301 | while obj: |
||
302 | if prefix: |
||
303 | if hasattr(obj, ATTRIBUTE_CONTROLLERS): |
||
304 | getattr(obj, ATTRIBUTE_CONTROLLERS)[(controller, prefix)] = True |
||
305 | else: |
||
306 | setattr(obj, ATTRIBUTE_CONTROLLERS, {(controller, prefix): True}) |
||
307 | parts = controlled_name.split(".", 1) |
||
308 | if len(parts) < 2: |
||
309 | break |
||
310 | new_prefix, controlled_name = parts |
||
311 | obj = getattr(obj, new_prefix, None) |
||
312 | if prefix: |
||
313 | prefix += '.' |
||
314 | prefix += new_prefix |
||
315 | |||
316 | |||
317 | class OWComponent: |
||
318 | def __init__(self, widget): |
||
319 | setattr(self, CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self)) |
||
320 | |||
321 | if widget.settingsHandler: |
||
322 | widget.settingsHandler.initialize(self) |
||
323 | |||
324 | def __setattr__(self, key, value): |
||
325 | super().__setattr__(key, value) |
||
326 | notify_changed(self, key, value) |
||
327 | |||
328 | |||
329 | def miscellanea(control, box, parent, |
||
330 | addToLayout=True, stretch=0, sizePolicy=None, addSpace=False, |
||
331 | disabled=False, tooltip=None): |
||
332 | """ |
||
333 | Helper function that sets various properties of the widget using a common |
||
334 | set of arguments. |
||
335 | |||
336 | The function |
||
337 | - sets the `control`'s attribute `box`, if `box` is given and `control.box` |
||
338 | is not yet set, |
||
339 | - attaches a tool tip to the `control` if specified, |
||
340 | - disables the `control`, if `disabled` is set to `True`, |
||
341 | - adds the `box` to the `parent`'s layout unless `addToLayout` is set to |
||
342 | `False`; the stretch factor can be specified, |
||
343 | - adds the control into the box's layout if the box is given (regardless |
||
344 | of `addToLayout`!) |
||
345 | - sets the size policy for the box or the control, if the policy is given, |
||
346 | - adds space in the `parent`'s layout after the `box` if `addSpace` is set |
||
347 | and `addToLayout` is not `False`. |
||
348 | |||
349 | If `box` is the same as `parent` it is set to `None`; this is convenient |
||
350 | because of the way complex controls are inserted. |
||
351 | |||
352 | :param control: the control, e.g. a `QCheckBox` |
||
353 | :type control: PyQt4.QtGui.QWidget |
||
354 | :param box: the box into which the widget was inserted |
||
355 | :type box: PyQt4.QtGui.QWidget or None |
||
356 | :param parent: the parent into whose layout the box or the control will be |
||
357 | inserted |
||
358 | :type parent: PyQt4.QtGui.QWidget |
||
359 | :param addSpace: the amount of space to add after the widget |
||
360 | :type addSpace: bool or int |
||
361 | :param disabled: If set to `True`, the widget is initially disabled |
||
362 | :type disabled: bool |
||
363 | :param addToLayout: If set to `False` the widget is not added to the layout |
||
364 | :type addToLayout: bool |
||
365 | :param stretch: the stretch factor for this widget, used when adding to |
||
366 | the layout (default: 0) |
||
367 | :type stretch: int |
||
368 | :param tooltip: tooltip that is attached to the widget |
||
369 | :type tooltip: str or None |
||
370 | :param sizePolicy: the size policy for the box or the control |
||
371 | :type sizePolicy: PyQt4.QtQui.QSizePolicy |
||
372 | """ |
||
373 | if disabled: |
||
374 | # if disabled==False, do nothing; it can be already disabled |
||
375 | control.setDisabled(disabled) |
||
376 | if tooltip is not None: |
||
377 | control.setToolTip(tooltip) |
||
378 | if box is parent: |
||
379 | box = None |
||
380 | elif box and box is not control and not hasattr(control, "box"): |
||
381 | control.box = box |
||
382 | if box and box.layout() is not None and \ |
||
383 | isinstance(control, QtGui.QWidget) and \ |
||
384 | box.layout().indexOf(control) == -1: |
||
385 | box.layout().addWidget(control) |
||
386 | if sizePolicy is not None: |
||
387 | (box or control).setSizePolicy(sizePolicy) |
||
388 | if addToLayout and parent and parent.layout() is not None: |
||
389 | parent.layout().addWidget(box or control, stretch) |
||
390 | _addSpace(parent, addSpace) |
||
391 | |||
392 | |||
393 | def setLayout(widget, orientation): |
||
394 | """ |
||
395 | Set the layout of the widget according to orientation. Argument |
||
396 | `orientation` can be an instance of :obj:`~PyQt4.QtGui.QLayout`, in which |
||
397 | case is it used as it is. If `orientation` is `'vertical'` or `True`, |
||
398 | the layout is set to :obj:`~PyQt4.QtGui.QVBoxLayout`. If it is |
||
399 | `'horizontal'` or `False`, it is set to :obj:`~PyQt4.QtGui.QVBoxLayout`. |
||
400 | |||
401 | :param widget: the widget for which the layout is being set |
||
402 | :type widget: PyQt4.QtGui.QWidget |
||
403 | :param orientation: orientation for the layout |
||
404 | :type orientation: str or bool or PyQt4.QtGui.QLayout |
||
405 | """ |
||
406 | if isinstance(orientation, QtGui.QLayout): |
||
407 | widget.setLayout(orientation) |
||
408 | elif orientation == 'horizontal' or not orientation: |
||
409 | widget.setLayout(QtGui.QHBoxLayout()) |
||
410 | else: |
||
411 | widget.setLayout(QtGui.QVBoxLayout()) |
||
412 | |||
413 | |||
414 | def _enterButton(parent, control, placeholder=True): |
||
415 | """ |
||
416 | Utility function that returns a button with a symbol for "Enter" and |
||
417 | optionally a placeholder to show when the enter button is hidden. Both |
||
418 | are inserted into the parent's layout, if it has one. If placeholder is |
||
419 | constructed it is shown and the button is hidden. |
||
420 | |||
421 | The height of the button is the same as the height of the widget passed |
||
422 | as argument `control`. |
||
423 | |||
424 | :param parent: parent widget into which the button is inserted |
||
425 | :type parent: PyQt4.QtGui.QWidget |
||
426 | :param control: a widget for determining the height of the button |
||
427 | :type control: PyQt4.QtGui.QWidget |
||
428 | :param placeholder: a flag telling whether to construct a placeholder |
||
429 | (default: True) |
||
430 | :type placeholder: bool |
||
431 | :return: a tuple with a button and a place holder (or `None`) |
||
432 | :rtype: PyQt4.QtGui.QToolButton or tuple |
||
433 | """ |
||
434 | global _enter_icon |
||
435 | if not _enter_icon: |
||
436 | _enter_icon = QtGui.QIcon( |
||
437 | os.path.dirname(__file__) + "/icons/Dlg_enter.png") |
||
438 | button = QtGui.QToolButton(parent) |
||
0 ignored issues
–
show
button is re-defining a name which is already available in the outer-scope (previously defined on line 1150 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
439 | height = control.sizeHint().height() |
||
440 | button.setFixedSize(height, height) |
||
441 | button.setIcon(_enter_icon) |
||
442 | if parent.layout() is not None: |
||
443 | parent.layout().addWidget(button) |
||
444 | if placeholder: |
||
445 | button.hide() |
||
446 | holder = QtGui.QWidget(parent) |
||
447 | holder.setFixedSize(height, height) |
||
448 | if parent.layout() is not None: |
||
449 | parent.layout().addWidget(holder) |
||
450 | else: |
||
451 | holder = None |
||
452 | return button, holder |
||
453 | |||
454 | |||
455 | def _addSpace(widget, space): |
||
456 | """ |
||
457 | A helper function that adds space into the widget, if requested. |
||
458 | The function is called by functions that have the `addSpace` argument. |
||
459 | |||
460 | :param widget: Widget into which to insert the space |
||
461 | :type widget: PyQt4.QtGui.QWidget |
||
462 | :param space: Amount of space to insert. If False, the function does |
||
463 | nothing. If the argument is an `int`, the specified space is inserted. |
||
464 | Otherwise, the default space is inserted by calling a :obj:`separator`. |
||
465 | :type space: bool or int |
||
466 | """ |
||
467 | if space: |
||
468 | if type(space) == int: # distinguish between int and bool! |
||
469 | separator(widget, space, space) |
||
470 | else: |
||
471 | separator(widget) |
||
472 | |||
473 | |||
474 | def separator(widget, width=4, height=4): |
||
475 | """ |
||
476 | Add a separator of the given size into the widget. |
||
477 | |||
478 | :param widget: the widget into whose layout the separator is added |
||
479 | :type widget: PyQt4.QtGui.QWidget |
||
480 | :param width: width of the separator |
||
481 | :type width: int |
||
482 | :param height: height of the separator |
||
483 | :type height: int |
||
484 | :return: separator |
||
485 | :rtype: PyQt4.QtGui.QWidget |
||
486 | """ |
||
487 | sep = QtGui.QWidget(widget) |
||
488 | if widget.layout() is not None: |
||
489 | widget.layout().addWidget(sep) |
||
490 | sep.setFixedSize(width, height) |
||
491 | return sep |
||
492 | |||
493 | |||
494 | def rubber(widget): |
||
495 | """ |
||
496 | Insert a stretch 100 into the widget's layout |
||
497 | """ |
||
498 | widget.layout().addStretch(100) |
||
499 | |||
500 | |||
501 | def widgetBox(widget, box=None, orientation='vertical', margin=None, spacing=4, |
||
502 | **misc): |
||
503 | """ |
||
504 | Construct a box with vertical or horizontal layout, and optionally, |
||
505 | a border with an optional label. |
||
506 | |||
507 | If the widget has a frame, the space after the widget is added unless |
||
508 | explicitly disabled. |
||
509 | |||
510 | :param widget: the widget into which the box is inserted |
||
511 | :type widget: PyQt4.QtGui.QWidget or None |
||
512 | :param box: tells whether the widget has a border, and its label |
||
513 | :type box: int or str or None |
||
514 | :param orientation: orientation for the layout. If the argument is an |
||
515 | instance of :obj:`~PyQt4.QtGui.QLayout`, it is used as a layout. If |
||
516 | "horizontal" or false-ish, the layout is horizontal |
||
517 | (:obj:`~PyQt4.QtGui.QHBoxLayout`), otherwise vertical |
||
518 | (:obj:`~PyQt4.QtGui.QHBoxLayout`). |
||
519 | :type orientation: str, int or :obj:`PyQt4.QtGui.QLayout` |
||
520 | :param sizePolicy: The size policy for the widget (default: None) |
||
521 | :type sizePolicy: :obj:`~PyQt4.QtGui.QSizePolicy` |
||
522 | :param margin: The margin for the layout. Default is 7 if the widget has |
||
523 | a border, and 0 if not. |
||
524 | :type margin: int |
||
525 | :param spacing: Spacing within the layout (default: 4) |
||
526 | :type spacing: int |
||
527 | :return: Constructed box |
||
528 | :rtype: PyQt4.QtGui.QGroupBox or PyQt4.QtGui.QWidget |
||
529 | """ |
||
530 | if box: |
||
531 | b = QtGui.QGroupBox(widget) |
||
532 | if isinstance(box, str): |
||
533 | b.setTitle(" " + box.strip() + " ") |
||
534 | if margin is None: |
||
535 | margin = 7 |
||
536 | else: |
||
537 | b = QtGui.QWidget(widget) |
||
538 | b.setContentsMargins(0, 0, 0, 0) |
||
539 | if margin is None: |
||
540 | margin = 0 |
||
541 | setLayout(b, orientation) |
||
542 | b.layout().setSpacing(spacing) |
||
543 | b.layout().setMargin(margin) |
||
544 | misc.setdefault('addSpace', bool(box)) |
||
545 | miscellanea(b, None, widget, **misc) |
||
546 | return b |
||
547 | |||
548 | |||
549 | def indentedBox(widget, sep=20, orientation="vertical", **misc): |
||
550 | """ |
||
551 | Creates an indented box. The function can also be used "on the fly":: |
||
552 | |||
553 | gui.checkBox(gui.indentedBox(box), self, "spam", "Enable spam") |
||
554 | |||
555 | To align the control with a check box, use :obj:`checkButtonOffsetHint`:: |
||
556 | |||
557 | gui.hSlider(gui.indentedBox(self.interBox), self, "intervals") |
||
558 | |||
559 | :param widget: the widget into which the box is inserted |
||
560 | :type widget: PyQt4.QtGui.QWidget |
||
561 | :param sep: Indent size (default: 20) |
||
562 | :type sep: int |
||
563 | :param orientation: layout of the inserted box; see :obj:`widgetBox` for |
||
564 | details |
||
565 | :type orientation: str, int or PyQt4.QtGui.QLayout |
||
566 | :return: Constructed box |
||
567 | :rtype: PyQt4.QtGui.QGroupBox or PyQt4.QtGui.QWidget |
||
568 | """ |
||
569 | outer = widgetBox(widget, orientation=False, spacing=0) |
||
570 | separator(outer, sep, 0) |
||
571 | indented = widgetBox(outer, orientation=orientation) |
||
572 | miscellanea(indented, outer, widget, **misc) |
||
573 | return indented |
||
574 | |||
575 | |||
576 | def widgetLabel(widget, label="", labelWidth=None, **misc): |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
577 | """ |
||
578 | Construct a simple, constant label. |
||
579 | |||
580 | :param widget: the widget into which the box is inserted |
||
581 | :type widget: PyQt4.QtGui.QWidget or None |
||
582 | :param label: The text of the label (default: None) |
||
583 | :type label: str |
||
584 | :param labelWidth: The width of the label (default: None) |
||
585 | :type labelWidth: int |
||
586 | :return: Constructed label |
||
587 | :rtype: PyQt4.QtGui.QLabel |
||
588 | """ |
||
589 | lbl = QtGui.QLabel(label, widget) |
||
590 | if labelWidth: |
||
591 | lbl.setFixedSize(labelWidth, lbl.sizeHint().height()) |
||
592 | miscellanea(lbl, None, widget, **misc) |
||
593 | return lbl |
||
594 | |||
595 | |||
596 | |||
597 | def label(widget, master, label, labelWidth=None, box=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
598 | orientation="vertical", **misc): |
||
0 ignored issues
–
show
|
|||
599 | """ |
||
600 | Construct a label that contains references to the master widget's |
||
601 | attributes; when their values change, the label is updated. |
||
602 | |||
603 | Argument :obj:`label` is a format string following Python's syntax |
||
604 | (see the corresponding Python documentation): the label's content is |
||
605 | rendered as `label % master.__dict__`. For instance, if the |
||
606 | :obj:`label` is given as "There are %(mm)i monkeys", the value of |
||
607 | `master.mm` (which must be an integer) will be inserted in place of |
||
608 | `%(mm)i`. |
||
609 | |||
610 | :param widget: the widget into which the box is inserted |
||
611 | :type widget: PyQt4.QtGui.QWidget or None |
||
612 | :param master: master widget |
||
613 | :type master: OWWidget or OWComponent |
||
614 | :param label: The text of the label, including attribute names |
||
615 | :type label: str |
||
616 | :param labelWidth: The width of the label (default: None) |
||
617 | :type labelWidth: int |
||
618 | :return: label |
||
619 | :rtype: PyQt4.QtGui.QLabel |
||
620 | """ |
||
621 | if box: |
||
622 | b = widgetBox(widget, box, orientation=None, addToLayout=False) |
||
623 | else: |
||
624 | b = widget |
||
625 | |||
626 | lbl = QtGui.QLabel("", b) |
||
627 | reprint = CallFrontLabel(lbl, label, master) |
||
628 | for mo in __re_label.finditer(label): |
||
629 | getattr(master, CONTROLLED_ATTRIBUTES)[mo.group("value")] = reprint |
||
630 | reprint() |
||
631 | if labelWidth: |
||
632 | lbl.setFixedSize(labelWidth, lbl.sizeHint().height()) |
||
633 | miscellanea(lbl, b, widget, **misc) |
||
634 | return lbl |
||
635 | |||
636 | |||
637 | class SpinBoxWFocusOut(QtGui.QSpinBox): |
||
638 | """ |
||
639 | A class derived from QtGui.QSpinBox, which postpones the synchronization |
||
640 | of the control's value with the master's attribute until the user presses |
||
641 | Enter or clicks an icon that appears beside the spin box when the value |
||
642 | is changed. |
||
643 | |||
644 | The class overloads :obj:`onChange` event handler to show the commit button, |
||
645 | and :obj:`onEnter` to commit the change when enter is pressed. |
||
646 | |||
647 | .. attribute:: enterButton |
||
648 | |||
649 | A widget (usually an icon) that is shown when the value is changed. |
||
650 | |||
651 | .. attribute:: placeHolder |
||
652 | |||
653 | A placeholder which is shown when the button is hidden |
||
654 | |||
655 | .. attribute:: inSetValue |
||
656 | |||
657 | A flag that is set when the value is being changed through |
||
658 | :obj:`setValue` to prevent the programmatic changes from showing the |
||
659 | commit button. |
||
660 | """ |
||
661 | |||
662 | def __init__(self, minv, maxv, step, parent=None): |
||
663 | """ |
||
664 | Construct the object and set the range (`minv`, `maxv`) and the step. |
||
665 | :param minv: Minimal value |
||
666 | :type minv: int |
||
667 | :param maxv: Maximal value |
||
668 | :type maxv: int |
||
669 | :param step: Step |
||
670 | :type step: int |
||
671 | :param parent: Parent widget |
||
672 | :type parent: PyQt4.QtGui.QWidget |
||
673 | """ |
||
674 | super().__init__(parent) |
||
675 | self.setRange(minv, maxv) |
||
676 | self.setSingleStep(step) |
||
677 | self.inSetValue = False |
||
678 | self.enterButton = None |
||
679 | self.placeHolder = None |
||
680 | |||
681 | def onChange(self, _): |
||
682 | """ |
||
683 | Hides the place holder and shows the commit button unless |
||
684 | :obj:`inSetValue` is set. |
||
685 | """ |
||
686 | if not self.inSetValue: |
||
687 | self.placeHolder.hide() |
||
688 | self.enterButton.show() |
||
689 | |||
690 | def onEnter(self): |
||
691 | """ |
||
692 | If the commit button is visible, the overload event handler commits |
||
693 | the change by calling the appropriate callbacks. It also hides the |
||
694 | commit button and shows the placeHolder. |
||
695 | """ |
||
696 | if self.enterButton.isVisible(): |
||
697 | self.enterButton.hide() |
||
698 | self.placeHolder.show() |
||
699 | if self.cback: |
||
700 | self.cback(int(str(self.text()))) |
||
701 | if self.cfunc: |
||
702 | self.cfunc() |
||
703 | |||
704 | # doesn't work: it's probably LineEdit's focusOut that we should |
||
705 | # (but can't) catch |
||
706 | def focusOutEvent(self, *e): |
||
707 | """ |
||
708 | This handler was intended to catch the focus out event and reintepret |
||
709 | it as if enter was pressed. It does not work, though. |
||
710 | """ |
||
711 | super().focusOutEvent(*e) |
||
712 | if self.enterButton and self.enterButton.isVisible(): |
||
713 | self.onEnter() |
||
714 | |||
715 | def setValue(self, value): |
||
716 | """ |
||
717 | Set the :obj:`inSetValue` flag and call the inherited method. |
||
718 | """ |
||
719 | self.inSetValue = True |
||
720 | super().setValue(value) |
||
721 | self.inSetValue = False |
||
722 | |||
723 | |||
724 | class DoubleSpinBoxWFocusOut(QtGui.QDoubleSpinBox): |
||
725 | """ |
||
726 | Same as :obj:`SpinBoxWFocusOut`, except that it is derived from |
||
727 | :obj:`~PyQt4.QtGui.QDoubleSpinBox`""" |
||
728 | def __init__(self, minv, maxv, step, parent): |
||
729 | super().__init__(parent) |
||
730 | self.setDecimals(math.ceil(-math.log10(step))) |
||
731 | self.setRange(minv, maxv) |
||
732 | self.setSingleStep(step) |
||
733 | self.inSetValue = False |
||
734 | self.enterButton = None |
||
735 | self.placeHolder = None |
||
736 | |||
737 | def onChange(self, _): |
||
738 | if not self.inSetValue: |
||
739 | self.placeHolder.hide() |
||
740 | self.enterButton.show() |
||
741 | |||
742 | def onEnter(self): |
||
743 | if self.enterButton.isVisible(): |
||
744 | self.enterButton.hide() |
||
745 | self.placeHolder.show() |
||
746 | if self.cback: |
||
747 | self.cback(float(str(self.text()).replace(",", "."))) |
||
748 | if self.cfunc: |
||
749 | self.cfunc() |
||
750 | |||
751 | # doesn't work: it's probably LineEdit's focusOut that we should |
||
752 | # (and can't) catch |
||
753 | def focusOutEvent(self, *e): |
||
754 | super().focusOutEvent(*e) |
||
755 | if self.enterButton and self.enterButton.isVisible(): |
||
756 | self.onEnter() |
||
757 | |||
758 | def setValue(self, value): |
||
759 | self.inSetValue = True |
||
760 | super().setValue(value) |
||
761 | self.inSetValue = False |
||
762 | |||
763 | |||
764 | def spin(widget, master, value, minv, maxv, step=1, box=None, label=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
765 | labelWidth=None, orientation=None, callback=None, |
||
766 | controlWidth=None, callbackOnReturn=False, checked=None, |
||
767 | checkCallback=None, posttext=None, disabled=False, |
||
768 | alignment=Qt.AlignLeft, keyboardTracking=True, |
||
769 | decimals=None, spinType=int, **misc): |
||
770 | """ |
||
771 | A spinbox with lots of bells and whistles, such as a checkbox and various |
||
772 | callbacks. It constructs a control of type :obj:`SpinBoxWFocusOut` or |
||
773 | :obj:`DoubleSpinBoxWFocusOut`. |
||
774 | |||
775 | :param widget: the widget into which the box is inserted |
||
776 | :type widget: PyQt4.QtGui.QWidget or None |
||
777 | :param master: master widget |
||
778 | :type master: OWWidget or OWComponent |
||
779 | :param value: the master's attribute with which the value is synchronized |
||
780 | :type value: str |
||
781 | :param minv: minimal value |
||
782 | :type minv: int |
||
783 | :param maxv: maximal value |
||
784 | :type maxv: int |
||
785 | :param step: step (default: 1) |
||
786 | :type step: int |
||
787 | :param box: tells whether the widget has a border, and its label |
||
788 | :type box: int or str or None |
||
789 | :param label: label that is put in above or to the left of the spin box |
||
790 | :type label: str |
||
791 | :param labelWidth: optional label width (default: None) |
||
792 | :type labelWidth: int |
||
793 | :param orientation: tells whether to put the label above (`"vertical"` or |
||
794 | `True`) or to the left (`"horizontal"` or `False`) |
||
795 | :type orientation: int or bool or str |
||
796 | :param callback: a function that is called when the value is entered; if |
||
797 | :obj:`callbackOnReturn` is `True`, the function is called when the |
||
798 | user commits the value by pressing Enter or clicking the icon |
||
799 | :type callback: function |
||
800 | :param controlWidth: the width of the spin box |
||
801 | :type controlWidth: int |
||
802 | :param callbackOnReturn: if `True`, the spin box has an associated icon |
||
803 | that must be clicked to confirm the value (default: False) |
||
804 | :type callbackOnReturn: bool |
||
805 | :param checked: if not None, a check box is put in front of the spin box; |
||
806 | when unchecked, the spin box is disabled. Argument `checked` gives the |
||
807 | name of the master's attribute given whose value is synchronized with |
||
808 | the check box's state (default: None). |
||
809 | :type checked: str |
||
810 | :param checkCallback: a callback function that is called when the check |
||
811 | box's state is changed |
||
812 | :type checkCallback: function |
||
813 | :param posttext: a text that is put to the right of the spin box |
||
814 | :type posttext: str |
||
815 | :param alignment: alignment of the spin box (e.g. `QtCore.Qt.AlignLeft`) |
||
816 | :type alignment: PyQt4.QtCore.Qt.Alignment |
||
817 | :param keyboardTracking: If `True`, the valueChanged signal is emitted |
||
818 | when the user is typing (default: True) |
||
819 | :type keyboardTracking: bool |
||
820 | :param spinType: determines whether to use QSpinBox (int) or |
||
821 | QDoubleSpinBox (float) |
||
822 | :type spinType: type |
||
823 | :param decimals: number of decimals (if `spinType` is `float`) |
||
824 | :type decimals: int |
||
825 | :return: Tuple `(spin box, check box) if `checked` is `True`, otherwise |
||
826 | the spin box |
||
827 | :rtype: tuple or gui.SpinBoxWFocusOut |
||
828 | """ |
||
829 | |||
830 | # b is the outermost box or the widget if there are no boxes; |
||
831 | # b is the widget that is inserted into the layout |
||
832 | # bi is the box that contains the control or the checkbox and the control; |
||
833 | # bi can be the widget itself, if there are no boxes |
||
834 | # cbox is the checkbox (or None) |
||
835 | # sbox is the spinbox itself |
||
836 | if box or label and not checked: |
||
837 | b = widgetBox(widget, box, orientation, addToLayout=False) |
||
838 | hasHBox = orientation == 'horizontal' or not orientation |
||
839 | else: |
||
840 | b = widget |
||
841 | hasHBox = False |
||
842 | if not hasHBox and (checked or callback and callbackOnReturn or posttext): |
||
843 | bi = widgetBox(b, orientation=0, addToLayout=False) |
||
844 | else: |
||
845 | bi = b |
||
846 | |||
847 | cbox = None |
||
848 | if checked is not None: |
||
849 | cbox = checkBox(bi, master, checked, label, labelWidth=labelWidth, |
||
850 | callback=checkCallback) |
||
851 | elif label: |
||
852 | b.label = widgetLabel(b, label, labelWidth) |
||
853 | if posttext: |
||
854 | widgetLabel(bi, posttext) |
||
855 | |||
856 | isDouble = spinType == float |
||
857 | sbox = bi.control = \ |
||
858 | (SpinBoxWFocusOut, DoubleSpinBoxWFocusOut)[isDouble](minv, maxv, |
||
859 | step, bi) |
||
860 | if bi is not widget: |
||
861 | bi.setDisabled(disabled) |
||
862 | else: |
||
863 | sbox.setDisabled(disabled) |
||
864 | |||
865 | if decimals is not None: |
||
866 | sbox.setDecimals(decimals) |
||
867 | sbox.setAlignment(alignment) |
||
868 | sbox.setKeyboardTracking(keyboardTracking) |
||
869 | if controlWidth: |
||
870 | sbox.setFixedWidth(controlWidth) |
||
871 | if value: |
||
872 | sbox.setValue(getdeepattr(master, value)) |
||
873 | |||
874 | cfront, sbox.cback, sbox.cfunc = connectControl( |
||
0 ignored issues
–
show
|
|||
875 | master, value, callback, |
||
876 | not (callback and callbackOnReturn) and |
||
877 | sbox.valueChanged[(int, float)[isDouble]], |
||
878 | (CallFrontSpin, CallFrontDoubleSpin)[isDouble](sbox)) |
||
879 | if checked: |
||
880 | cbox.disables = [sbox] |
||
881 | cbox.makeConsistent() |
||
882 | if callback and callbackOnReturn: |
||
883 | sbox.enterButton, sbox.placeHolder = _enterButton(bi, sbox) |
||
884 | sbox.valueChanged[str].connect(sbox.onChange) |
||
885 | sbox.editingFinished.connect(sbox.onEnter) |
||
886 | sbox.enterButton.clicked.connect(sbox.onEnter) |
||
887 | if hasattr(sbox, "upButton"): |
||
888 | sbox.upButton().clicked.connect( |
||
889 | lambda c=sbox.editor(): c.setFocus()) |
||
890 | sbox.downButton().clicked.connect( |
||
891 | lambda c=sbox.editor(): c.setFocus()) |
||
892 | |||
893 | miscellanea(sbox, b if b is not widget else bi, widget, **misc) |
||
894 | if checked: |
||
895 | if isDouble and b == widget: |
||
896 | # TODO Backward compatilibity; try to find and eliminate |
||
0 ignored issues
–
show
|
|||
897 | sbox.control = b.control |
||
898 | return sbox |
||
899 | return cbox, sbox |
||
900 | else: |
||
901 | return sbox |
||
902 | |||
903 | |||
904 | |||
905 | # noinspection PyTypeChecker |
||
906 | def doubleSpin(widget, master, value, minv, maxv, step=1, box=None, label=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
907 | labelWidth=None, orientation=None, callback=None, |
||
908 | controlWidth=None, callbackOnReturn=False, checked=None, |
||
909 | checkCallback=None, posttext=None, |
||
910 | alignment=Qt.AlignLeft, keyboardTracking=True, |
||
911 | decimals=None, **misc): |
||
912 | """ |
||
913 | Backward compatilibity function: calls :obj:`spin` with `spinType=float`. |
||
914 | """ |
||
915 | return spin(widget, master, value, minv, maxv, step, box=box, label=label, |
||
916 | labelWidth=labelWidth, orientation=orientation, |
||
917 | callback=callback, controlWidth=controlWidth, |
||
918 | callbackOnReturn=callbackOnReturn, checked=checked, |
||
919 | checkCallback=checkCallback, posttext=posttext, |
||
920 | alignment=alignment, keyboardTracking=keyboardTracking, |
||
921 | decimals=decimals, spinType=float, **misc) |
||
922 | |||
923 | |||
924 | def checkBox(widget, master, value, label, box=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
925 | callback=None, getwidget=False, id_=None, labelWidth=None, |
||
926 | disables=None, **misc): |
||
927 | """ |
||
928 | A simple checkbox. |
||
929 | |||
930 | :param widget: the widget into which the box is inserted |
||
931 | :type widget: PyQt4.QtGui.QWidget or None |
||
932 | :param master: master widget |
||
933 | :type master: OWWidget or OWComponent |
||
934 | :param value: the master's attribute with which the value is synchronized |
||
935 | :type value: str |
||
936 | :param label: label |
||
937 | :type label: str |
||
938 | :param box: tells whether the widget has a border, and its label |
||
939 | :type box: int or str or None |
||
940 | :param callback: a function that is called when the check box state is |
||
941 | changed |
||
942 | :type callback: function |
||
943 | :param getwidget: If set `True`, the callback function will get a keyword |
||
944 | argument `widget` referencing the check box |
||
945 | :type getwidget: bool |
||
946 | :param id_: If present, the callback function will get a keyword argument |
||
947 | `id` with this value |
||
948 | :type id_: any |
||
949 | :param labelWidth: the width of the label |
||
950 | :type labelWidth: int |
||
951 | :param disables: a list of widgets that are disabled if the check box is |
||
952 | unchecked |
||
953 | :type disables: list or PyQt4.QtGui.QWidget or None |
||
954 | :return: constructed check box; if is is placed within a box, the box is |
||
955 | return in the attribute `box` |
||
956 | :rtype: PyQt4.QtGui.QCheckBox |
||
957 | """ |
||
958 | if box: |
||
959 | b = widgetBox(widget, box, orientation=None, addToLayout=False) |
||
960 | else: |
||
961 | b = widget |
||
962 | cbox = QtGui.QCheckBox(label, b) |
||
963 | |||
964 | if labelWidth: |
||
965 | cbox.setFixedSize(labelWidth, cbox.sizeHint().height()) |
||
966 | cbox.setChecked(getdeepattr(master, value)) |
||
967 | |||
968 | connectControl(master, value, None, cbox.toggled[bool], |
||
969 | CallFrontCheckBox(cbox), |
||
970 | cfunc=callback and FunctionCallback( |
||
971 | master, callback, widget=cbox, getwidget=getwidget, |
||
972 | id=id_)) |
||
973 | if isinstance(disables, QtGui.QWidget): |
||
974 | disables = [disables] |
||
975 | cbox.disables = disables or [] |
||
976 | cbox.makeConsistent = Disabler(cbox, master, value) |
||
977 | cbox.toggled[bool].connect(cbox.makeConsistent) |
||
978 | cbox.makeConsistent(value) |
||
979 | miscellanea(cbox, b, widget, **misc) |
||
980 | return cbox |
||
981 | |||
982 | |||
983 | class LineEditWFocusOut(QtGui.QLineEdit): |
||
984 | """ |
||
985 | A class derived from QtGui.QLineEdit, which postpones the synchronization |
||
986 | of the control's value with the master's attribute until the user leaves |
||
987 | the line edit, presses Enter or clicks an icon that appears beside the |
||
988 | line edit when the value is changed. |
||
989 | |||
990 | The class also allows specifying a callback function for focus-in event. |
||
991 | |||
992 | .. attribute:: enterButton |
||
993 | |||
994 | A widget (usually an icon) that is shown when the value is changed. |
||
995 | |||
996 | .. attribute:: placeHolder |
||
997 | |||
998 | A placeholder which is shown when the button is hidden |
||
999 | |||
1000 | .. attribute:: inSetValue |
||
1001 | |||
1002 | A flag that is set when the value is being changed through |
||
1003 | :obj:`setValue` to prevent the programmatic changes from showing the |
||
1004 | commit button. |
||
1005 | |||
1006 | .. attribute:: callback |
||
1007 | |||
1008 | Callback that is called when the change is confirmed |
||
1009 | |||
1010 | .. attribute:: focusInCallback |
||
1011 | |||
1012 | Callback that is called on the focus-in event |
||
1013 | """ |
||
1014 | |||
1015 | def __init__(self, parent, callback, focusInCallback=None, |
||
1016 | placeholder=False): |
||
1017 | super().__init__(parent) |
||
1018 | if parent.layout() is not None: |
||
1019 | parent.layout().addWidget(self) |
||
1020 | self.callback = callback |
||
1021 | self.focusInCallback = focusInCallback |
||
1022 | self.enterButton, self.placeHolder = \ |
||
1023 | _enterButton(parent, self, placeholder) |
||
1024 | self.enterButton.clicked.connect(self.returnPressedHandler) |
||
1025 | self.textChanged[str].connect(self.markChanged) |
||
1026 | self.returnPressed.connect(self.returnPressedHandler) |
||
1027 | |||
1028 | def markChanged(self, *_): |
||
1029 | if self.placeHolder: |
||
1030 | self.placeHolder.hide() |
||
1031 | self.enterButton.show() |
||
1032 | |||
1033 | def markUnchanged(self, *_): |
||
1034 | self.enterButton.hide() |
||
1035 | if self.placeHolder: |
||
1036 | self.placeHolder.show() |
||
1037 | |||
1038 | def returnPressedHandler(self): |
||
1039 | if self.enterButton.isVisible(): |
||
1040 | self.markUnchanged() |
||
1041 | if hasattr(self, "cback") and self.cback: |
||
1042 | self.cback(self.text()) |
||
1043 | if self.callback: |
||
1044 | self.callback() |
||
1045 | |||
1046 | def setText(self, t): |
||
1047 | super().setText(t) |
||
1048 | if self.enterButton: |
||
1049 | self.markUnchanged() |
||
1050 | |||
1051 | def focusOutEvent(self, *e): |
||
1052 | super().focusOutEvent(*e) |
||
1053 | self.returnPressedHandler() |
||
1054 | |||
1055 | def focusInEvent(self, *e): |
||
1056 | if self.focusInCallback: |
||
1057 | self.focusInCallback() |
||
1058 | return super().focusInEvent(*e) |
||
1059 | |||
1060 | |||
1061 | def lineEdit(widget, master, value, label=None, labelWidth=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1062 | orientation='vertical', box=None, callback=None, |
||
1063 | valueType=str, validator=None, controlWidth=None, |
||
1064 | callbackOnType=False, focusInCallback=None, |
||
1065 | enterPlaceholder=False, **misc): |
||
1066 | """ |
||
1067 | Insert a line edit. |
||
1068 | |||
1069 | :param widget: the widget into which the box is inserted |
||
1070 | :type widget: PyQt4.QtGui.QWidget or None |
||
1071 | :param master: master widget |
||
1072 | :type master: OWWidget or OWComponent |
||
1073 | :param value: the master's attribute with which the value is synchronized |
||
1074 | :type value: str |
||
1075 | :param label: label |
||
1076 | :type label: str |
||
1077 | :param labelWidth: the width of the label |
||
1078 | :type labelWidth: int |
||
1079 | :param orientation: tells whether to put the label above (`"vertical"` or |
||
1080 | `True`) or to the left (`"horizontal"` or `False`) |
||
1081 | :type orientation: int or bool or str |
||
1082 | :param box: tells whether the widget has a border, and its label |
||
1083 | :type box: int or str or None |
||
1084 | :param callback: a function that is called when the check box state is |
||
1085 | changed |
||
1086 | :type callback: function |
||
1087 | :param valueType: the type into which the entered string is converted |
||
1088 | when synchronizing to `value` |
||
1089 | :type valueType: type |
||
1090 | :param validator: the validator for the input |
||
1091 | :type validator: PyQt4.QtGui.QValidator |
||
1092 | :param controlWidth: the width of the line edit |
||
1093 | :type controlWidth: int |
||
1094 | :param callbackOnType: if set to `True`, the callback is called at each |
||
1095 | key press (default: `False`) |
||
1096 | :type callbackOnType: bool |
||
1097 | :param focusInCallback: a function that is called when the line edit |
||
1098 | receives focus |
||
1099 | :type focusInCallback: function |
||
1100 | :param enterPlaceholder: if set to `True`, space of appropriate width is |
||
1101 | left empty to the right for the icon that shows that the value is |
||
1102 | changed but has not been committed yet |
||
1103 | :type enterPlaceholder: bool |
||
1104 | :rtype: PyQt4.QtGui.QLineEdit or a box |
||
1105 | """ |
||
1106 | if box or label: |
||
1107 | b = widgetBox(widget, box, orientation, addToLayout=False) |
||
1108 | if label is not None: |
||
1109 | widgetLabel(b, label, labelWidth) |
||
1110 | hasHBox = orientation == 'horizontal' or not orientation |
||
1111 | else: |
||
1112 | b = widget |
||
1113 | hasHBox = False |
||
1114 | |||
1115 | baseClass = misc.pop("baseClass", None) |
||
1116 | if baseClass: |
||
1117 | ledit = baseClass(b) |
||
1118 | ledit.enterButton = None |
||
1119 | if b is not widget: |
||
1120 | b.layout().addWidget(ledit) |
||
1121 | elif focusInCallback or callback and not callbackOnType: |
||
1122 | if not hasHBox: |
||
1123 | outer = widgetBox(b, "", 0, addToLayout=(b is not widget)) |
||
1124 | else: |
||
1125 | outer = b |
||
1126 | ledit = LineEditWFocusOut(outer, callback, focusInCallback, |
||
1127 | enterPlaceholder) |
||
1128 | else: |
||
1129 | ledit = QtGui.QLineEdit(b) |
||
1130 | ledit.enterButton = None |
||
1131 | if b is not widget: |
||
1132 | b.layout().addWidget(ledit) |
||
1133 | |||
1134 | if value: |
||
1135 | ledit.setText(str(getdeepattr(master, value))) |
||
1136 | if controlWidth: |
||
1137 | ledit.setFixedWidth(controlWidth) |
||
1138 | if validator: |
||
1139 | ledit.setValidator(validator) |
||
1140 | if value: |
||
1141 | ledit.cback = connectControl( |
||
0 ignored issues
–
show
|
|||
1142 | master, value, |
||
1143 | callbackOnType and callback, ledit.textChanged[str], |
||
1144 | CallFrontLineEdit(ledit), fvcb=value and valueType)[1] |
||
1145 | |||
1146 | miscellanea(ledit, b, widget, **misc) |
||
1147 | return ledit |
||
1148 | |||
1149 | |||
1150 | def button(widget, master, label, callback=None, width=None, height=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1151 | toggleButton=False, value="", default=False, autoDefault=True, |
||
1152 | buttonType=QtGui.QPushButton, **misc): |
||
1153 | """ |
||
1154 | Insert a button (QPushButton, by default) |
||
1155 | |||
1156 | :param widget: the widget into which the button is inserted |
||
1157 | :type widget: PyQt4.QtGui.QWidget or None |
||
1158 | :param master: master widget |
||
1159 | :type master: OWWidget or OWComponent |
||
1160 | :param label: label |
||
1161 | :type label: str |
||
1162 | :param callback: a function that is called when the button is pressed |
||
1163 | :type callback: function |
||
1164 | :param width: the width of the button |
||
1165 | :type width: int |
||
1166 | :param height: the height of the button |
||
1167 | :type height: int |
||
1168 | :param toggleButton: if set to `True`, the button is checkable, but it is |
||
1169 | not synchronized with any attribute unless the `value` is given |
||
1170 | :type toggleButton: bool |
||
1171 | :param value: the master's attribute with which the value is synchronized |
||
1172 | (the argument is optional; if present, it makes the button "checkable", |
||
1173 | even if `toggleButton` is not set) |
||
1174 | :type value: str |
||
1175 | :param default: if `True` it makes the button the default button; this is |
||
1176 | the button that is activated when the user presses Enter unless some |
||
1177 | auto default button has current focus |
||
1178 | :type default: bool |
||
1179 | :param autoDefault: all buttons are auto default: they are activated if |
||
1180 | they have focus (or are the next in the focus chain) when the user |
||
1181 | presses enter. By setting `autoDefault` to `False`, the button is not |
||
1182 | activated on pressing Return. |
||
1183 | :type autoDefault: bool |
||
1184 | :param buttonType: the button type (default: `QPushButton`) |
||
1185 | :type buttonType: PyQt4.QtGui.QAbstractButton |
||
1186 | :rtype: PyQt4.QtGui.QAbstractButton |
||
1187 | """ |
||
1188 | button = buttonType(widget) |
||
0 ignored issues
–
show
button is re-defining a name which is already available in the outer-scope (previously defined on line 1150 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1189 | if label: |
||
1190 | button.setText(label) |
||
1191 | if width: |
||
1192 | button.setFixedWidth(width) |
||
1193 | if height: |
||
1194 | button.setFixedHeight(height) |
||
1195 | if toggleButton or value: |
||
1196 | button.setCheckable(True) |
||
1197 | if buttonType == QtGui.QPushButton: |
||
1198 | button.setDefault(default) |
||
1199 | button.setAutoDefault(autoDefault) |
||
1200 | |||
1201 | if value: |
||
1202 | button.setChecked(getdeepattr(master, value)) |
||
1203 | connectControl( |
||
1204 | master, value, None, button.toggled[bool], |
||
1205 | CallFrontButton(button), |
||
1206 | cfunc=callback and FunctionCallback(master, callback, |
||
1207 | widget=button)) |
||
1208 | elif callback: |
||
1209 | button.clicked.connect(callback) |
||
1210 | |||
1211 | miscellanea(button, None, widget, **misc) |
||
1212 | return button |
||
1213 | |||
1214 | |||
1215 | def toolButton(widget, master, label="", callback=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1216 | width=None, height=None, tooltip=None): |
||
1217 | """ |
||
1218 | Insert a tool button. Calls :obj:`button` |
||
1219 | |||
1220 | :param widget: the widget into which the button is inserted |
||
1221 | :type widget: PyQt4.QtGui.QWidget or None |
||
1222 | :param master: master widget |
||
1223 | :type master: OWWidget or OWComponent |
||
1224 | :param label: label |
||
1225 | :type label: str |
||
1226 | :param callback: a function that is called when the button is pressed |
||
1227 | :type callback: function |
||
1228 | :param width: the width of the button |
||
1229 | :type width: int |
||
1230 | :param height: the height of the button |
||
1231 | :type height: int |
||
1232 | :rtype: PyQt4.QtGui.QToolButton |
||
1233 | """ |
||
1234 | return button(widget, master, label, callback, width, height, |
||
1235 | buttonType=QtGui.QToolButton, tooltip=tooltip) |
||
1236 | |||
1237 | |||
1238 | def createAttributePixmap(char, background=Qt.black, color=Qt.white): |
||
1239 | """ |
||
1240 | Create a QIcon with a given character. The icon is 13 pixels high and wide. |
||
1241 | |||
1242 | :param char: The character that is printed in the icon |
||
1243 | :type char: str |
||
1244 | :param background: the background color (default: black) |
||
1245 | :type background: PyQt4.QtGui.QColor |
||
1246 | :param color: the character color (default: white) |
||
1247 | :type color: PyQt4.QtGui.QColor |
||
1248 | :rtype: PyQt4.QtGui.QIcon |
||
1249 | """ |
||
1250 | pixmap = QtGui.QPixmap(13, 13) |
||
1251 | pixmap.fill(QtGui.QColor(0, 0, 0, 0)) |
||
1252 | painter = QtGui.QPainter() |
||
1253 | painter.begin(pixmap) |
||
1254 | painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing | |
||
1255 | painter.SmoothPixmapTransform) |
||
1256 | painter.setPen(background) |
||
1257 | painter.setBrush(background) |
||
1258 | rect = QtCore.QRectF(0, 0, 13, 13) |
||
1259 | painter.drawRoundedRect(rect, 4, 4) |
||
1260 | painter.setPen(color) |
||
1261 | painter.drawText(2, 11, char) |
||
1262 | painter.end() |
||
1263 | return QtGui.QIcon(pixmap) |
||
1264 | |||
1265 | |||
1266 | class __AttributeIconDict(dict): |
||
1267 | def __getitem__(self, key): |
||
1268 | if not self: |
||
1269 | for tpe, char, col in ((vartype(ContinuousVariable()), |
||
1270 | "C", (202, 0, 32)), |
||
1271 | (vartype(DiscreteVariable()), |
||
1272 | "D", (26, 150, 65)), |
||
1273 | (vartype(StringVariable()), |
||
1274 | "S", (0, 0, 0)), |
||
1275 | (-1, "?", (128, 128, 128))): |
||
1276 | self[tpe] = createAttributePixmap(char, QtGui.QColor(*col)) |
||
1277 | if key not in self: |
||
1278 | key = vartype(key) if isinstance(key, Variable) else -1 |
||
1279 | return super().__getitem__(key) |
||
1280 | |||
1281 | #: A dict that returns icons for different attribute types. The dict is |
||
1282 | #: constructed on first use since icons cannot be created before initializing |
||
1283 | #: the application. |
||
1284 | #: |
||
1285 | #: Accepted keys are variable type codes and instances |
||
1286 | #: of :obj:`Orange.data.variable`: `attributeIconDict[var]` will give the |
||
1287 | #: appropriate icon for variable `var` or a question mark if the type is not |
||
1288 | #: recognized |
||
1289 | attributeIconDict = __AttributeIconDict() |
||
1290 | |||
1291 | |||
1292 | def attributeItem(var): |
||
1293 | """ |
||
1294 | Construct a pair (icon, name) for inserting a variable into a combo or |
||
1295 | list box |
||
1296 | |||
1297 | :param var: variable |
||
1298 | :type var: Orange.data.Variable |
||
1299 | :rtype: tuple with PyQt4.QtGui.QIcon and str |
||
1300 | """ |
||
1301 | return attributeIconDict[var], var.name |
||
1302 | |||
1303 | |||
1304 | def listBox(widget, master, value=None, labels=None, box=None, callback=None, |
||
1305 | selectionMode=QtGui.QListWidget.SingleSelection, |
||
1306 | enableDragDrop=False, dragDropCallback=None, |
||
1307 | dataValidityCallback=None, sizeHint=None, **misc): |
||
1308 | """ |
||
1309 | Insert a list box. |
||
1310 | |||
1311 | The value with which the box's value synchronizes (`master.<value>`) |
||
1312 | is a list of indices of selected items. |
||
1313 | |||
1314 | :param widget: the widget into which the box is inserted |
||
1315 | :type widget: PyQt4.QtGui.QWidget or None |
||
1316 | :param master: master widget |
||
1317 | :type master: OWWidget or OWComponent |
||
1318 | :param value: the name of the master's attribute with which the value is |
||
1319 | synchronized (list of ints - indices of selected items) |
||
1320 | :type value: str |
||
1321 | :param labels: the name of the master's attribute with the list of items |
||
1322 | (as strings or tuples with icon and string) |
||
1323 | :type labels: str |
||
1324 | :param box: tells whether the widget has a border, and its label |
||
1325 | :type box: int or str or None |
||
1326 | :param callback: a function that is called when the selection state is |
||
1327 | changed |
||
1328 | :type callback: function |
||
1329 | :param selectionMode: selection mode - single, multiple etc |
||
1330 | :type selectionMode: PyQt4.QtGui.QAbstractItemView.SelectionMode |
||
1331 | :param enableDragDrop: flag telling whether drag and drop is available |
||
1332 | :type enableDragDrop: bool |
||
1333 | :param dragDropCallback: callback function on drop event |
||
1334 | :type dragDropCallback: function |
||
1335 | :param dataValidityCallback: function that check the validity on enter |
||
1336 | and move event; it should return either `ev.accept()` or `ev.ignore()`. |
||
1337 | :type dataValidityCallback: function |
||
1338 | :param sizeHint: size hint |
||
1339 | :type sizeHint: PyQt4.QtGui.QSize |
||
1340 | :rtype: OrangeListBox |
||
1341 | """ |
||
1342 | if box: |
||
1343 | bg = widgetBox(widget, box, |
||
1344 | orientation="horizontal", addToLayout=False) |
||
1345 | else: |
||
1346 | bg = widget |
||
1347 | lb = OrangeListBox(master, enableDragDrop, dragDropCallback, |
||
1348 | dataValidityCallback, sizeHint, bg) |
||
1349 | lb.setSelectionMode(selectionMode) |
||
1350 | lb.ogValue = value |
||
0 ignored issues
–
show
|
|||
1351 | lb.ogLabels = labels |
||
0 ignored issues
–
show
|
|||
1352 | lb.ogMaster = master |
||
0 ignored issues
–
show
|
|||
1353 | |||
1354 | if value is not None: |
||
1355 | clist = getdeepattr(master, value) |
||
1356 | if not isinstance(clist, ControlledList): |
||
1357 | clist = ControlledList(clist, lb) |
||
1358 | master.__setattr__(value, clist) |
||
1359 | if labels is not None: |
||
1360 | setattr(master, labels, getdeepattr(master, labels)) |
||
1361 | if hasattr(master, CONTROLLED_ATTRIBUTES): |
||
1362 | getattr(master, CONTROLLED_ATTRIBUTES)[labels] = CallFrontListBoxLabels(lb) |
||
1363 | if value is not None: |
||
1364 | setattr(master, value, getdeepattr(master, value)) |
||
1365 | connectControl(master, value, callback, lb.itemSelectionChanged, |
||
1366 | CallFrontListBox(lb), CallBackListBox(lb, master)) |
||
1367 | |||
1368 | misc.setdefault('addSpace', True) |
||
1369 | miscellanea(lb, bg, widget, **misc) |
||
1370 | return lb |
||
1371 | |||
1372 | |||
1373 | # btnLabels is a list of either char strings or pixmaps |
||
1374 | def radioButtons(widget, master, value, btnLabels=(), tooltips=None, |
||
1375 | box=None, label=None, orientation='vertical', |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1376 | callback=None, **misc): |
||
1377 | """ |
||
1378 | Construct a button group and add radio buttons, if they are given. |
||
1379 | The value with which the buttons synchronize is the index of selected |
||
1380 | button. |
||
1381 | |||
1382 | :param widget: the widget into which the box is inserted |
||
1383 | :type widget: PyQt4.QtGui.QWidget or None |
||
1384 | :param master: master widget |
||
1385 | :type master: OWWidget or OWComponent |
||
1386 | :param value: the master's attribute with which the value is synchronized |
||
1387 | :type value: str |
||
1388 | :param btnLabels: a list of labels or icons for radio buttons |
||
1389 | :type btnLabels: list of str or pixmaps |
||
1390 | :param tooltips: a list of tool tips of the same length as btnLabels |
||
1391 | :type tooltips: list of str |
||
1392 | :param box: tells whether the widget has a border, and its label |
||
1393 | :type box: int or str or None |
||
1394 | :param label: a label that is inserted into the box |
||
1395 | :type label: str |
||
1396 | :param callback: a function that is called when the selection is changed |
||
1397 | :type callback: function |
||
1398 | :param orientation: orientation of the layout in the box |
||
1399 | :type orientation: int or str or QLayout |
||
1400 | :rtype: PyQt4.QtQui.QButtonGroup |
||
1401 | """ |
||
1402 | bg = widgetBox(widget, box, orientation, addToLayout=False) |
||
1403 | if not label is None: |
||
1404 | widgetLabel(bg, label) |
||
1405 | |||
1406 | rb = QtGui.QButtonGroup(bg) |
||
1407 | if bg is not widget: |
||
1408 | bg.group = rb |
||
1409 | bg.buttons = [] |
||
1410 | bg.ogValue = value |
||
1411 | bg.ogMaster = master |
||
1412 | for i, lab in enumerate(btnLabels): |
||
1413 | appendRadioButton(bg, lab, tooltip=tooltips and tooltips[i]) |
||
1414 | connectControl(master, value, callback, bg.group.buttonClicked[int], |
||
1415 | CallFrontRadioButtons(bg), CallBackRadioButton(bg, master)) |
||
1416 | misc.setdefault('addSpace', bool(box)) |
||
1417 | miscellanea(bg.group, bg, widget, **misc) |
||
1418 | return bg |
||
1419 | |||
1420 | |||
1421 | radioButtonsInBox = radioButtons |
||
1422 | |||
1423 | def appendRadioButton(group, label, insertInto=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1424 | disabled=False, tooltip=None, sizePolicy=None, |
||
1425 | addToLayout=True, stretch=0, addSpace=False): |
||
1426 | """ |
||
1427 | Construct a radio button and add it to the group. The group must be |
||
1428 | constructed with :obj:`radioButtonsInBox` since it adds additional |
||
1429 | attributes need for the call backs. |
||
1430 | |||
1431 | The radio button is inserted into `insertInto` or, if omitted, into the |
||
1432 | button group. This is useful for more complex groups, like those that have |
||
1433 | radio buttons in several groups, divided by labels and inside indented |
||
1434 | boxes. |
||
1435 | |||
1436 | :param group: the button group |
||
1437 | :type group: PyQt4.QtCore.QButtonGroup |
||
1438 | :param label: string label or a pixmap for the button |
||
1439 | :type label: str or PyQt4.QtGui.QPixmap |
||
1440 | :param insertInto: the widget into which the radio button is inserted |
||
1441 | :type insertInto: PyQt4.QtGui.QWidget |
||
1442 | :rtype: PyQt4.QtGui.QRadioButton |
||
1443 | """ |
||
1444 | i = len(group.buttons) |
||
1445 | if isinstance(label, str): |
||
1446 | w = QtGui.QRadioButton(label) |
||
1447 | else: |
||
1448 | w = QtGui.QRadioButton(str(i)) |
||
1449 | w.setIcon(QtGui.QIcon(label)) |
||
1450 | if not hasattr(group, "buttons"): |
||
1451 | group.buttons = [] |
||
1452 | group.buttons.append(w) |
||
1453 | group.group.addButton(w) |
||
1454 | w.setChecked(getdeepattr(group.ogMaster, group.ogValue) == i) |
||
1455 | |||
1456 | # miscellanea for this case is weird, so we do it here |
||
1457 | if disabled: |
||
1458 | w.setDisabled(disabled) |
||
1459 | if tooltip is not None: |
||
1460 | w.setToolTip(tooltip) |
||
1461 | if sizePolicy: |
||
1462 | w.setSizePolicy(sizePolicy) |
||
1463 | if addToLayout: |
||
1464 | dest = insertInto or group |
||
1465 | dest.layout().addWidget(w, stretch) |
||
1466 | _addSpace(dest, addSpace) |
||
1467 | return w |
||
1468 | |||
1469 | |||
1470 | def hSlider(widget, master, value, box=None, minValue=0, maxValue=10, step=1, |
||
1471 | callback=None, label=None, labelFormat=" %d", ticks=False, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1472 | divideFactor=1.0, vertical=False, createLabel=True, width=None, |
||
1473 | intOnly=True, **misc): |
||
1474 | """ |
||
1475 | Construct a slider. |
||
1476 | |||
1477 | :param widget: the widget into which the box is inserted |
||
1478 | :type widget: PyQt4.QtGui.QWidget or None |
||
1479 | :param master: master widget |
||
1480 | :type master: OWWidget or OWComponent |
||
1481 | :param value: the master's attribute with which the value is synchronized |
||
1482 | :type value: str |
||
1483 | :param box: tells whether the widget has a border, and its label |
||
1484 | :type box: int or str or None |
||
1485 | :param label: a label that is inserted into the box |
||
1486 | :type label: str |
||
1487 | :param callback: a function that is called when the value is changed |
||
1488 | :type callback: function |
||
1489 | |||
1490 | :param minValue: minimal value |
||
1491 | :type minValue: int or float |
||
1492 | :param maxValue: maximal value |
||
1493 | :type maxValue: int or float |
||
1494 | :param step: step size |
||
1495 | :type step: int or float |
||
1496 | :param labelFormat: the label format; default is `" %d"` |
||
1497 | :type labelFormat: str |
||
1498 | :param ticks: if set to `True`, ticks are added below the slider |
||
1499 | :type ticks: bool |
||
1500 | :param divideFactor: a factor with which the displayed value is divided |
||
1501 | :type divideFactor: float |
||
1502 | :param vertical: if set to `True`, the slider is vertical |
||
1503 | :type vertical: bool |
||
1504 | :param createLabel: unless set to `False`, labels for minimal, maximal |
||
1505 | and the current value are added to the widget |
||
1506 | :type createLabel: bool |
||
1507 | :param width: the width of the slider |
||
1508 | :type width: int |
||
1509 | :param intOnly: if `True`, the slider value is integer (the slider is |
||
1510 | of type :obj:`PyQt4.QtGui.QSlider`) otherwise it is float |
||
1511 | (:obj:`FloatSlider`, derived in turn from :obj:`PyQt4.QtQui.QSlider`). |
||
1512 | :type intOnly: bool |
||
1513 | :rtype: :obj:`PyQt4.QtGui.QSlider` or :obj:`FloatSlider` |
||
1514 | """ |
||
1515 | sliderBox = widgetBox(widget, box, orientation="horizontal", |
||
1516 | addToLayout=False) |
||
1517 | if label: |
||
1518 | widgetLabel(sliderBox, label) |
||
1519 | sliderOrient = Qt.Vertical if vertical else Qt.Horizontal |
||
1520 | if intOnly: |
||
1521 | slider = QtGui.QSlider(sliderOrient, sliderBox) |
||
1522 | slider.setRange(minValue, maxValue) |
||
1523 | if step: |
||
1524 | slider.setSingleStep(step) |
||
1525 | slider.setPageStep(step) |
||
1526 | slider.setTickInterval(step) |
||
1527 | signal = slider.valueChanged[int] |
||
1528 | else: |
||
1529 | slider = FloatSlider(sliderOrient, minValue, maxValue, step) |
||
1530 | signal = slider.valueChangedFloat[float] |
||
1531 | sliderBox.layout().addWidget(slider) |
||
1532 | slider.setValue(getdeepattr(master, value)) |
||
1533 | if width: |
||
1534 | slider.setFixedWidth(width) |
||
1535 | if ticks: |
||
1536 | slider.setTickPosition(QtGui.QSlider.TicksBelow) |
||
1537 | slider.setTickInterval(ticks) |
||
1538 | |||
1539 | if createLabel: |
||
1540 | label = QtGui.QLabel(sliderBox) |
||
1541 | sliderBox.layout().addWidget(label) |
||
1542 | label.setText(labelFormat % minValue) |
||
1543 | width1 = label.sizeHint().width() |
||
1544 | label.setText(labelFormat % maxValue) |
||
1545 | width2 = label.sizeHint().width() |
||
1546 | label.setFixedSize(max(width1, width2), label.sizeHint().height()) |
||
1547 | txt = labelFormat % (getdeepattr(master, value) / divideFactor) |
||
1548 | label.setText(txt) |
||
1549 | label.setLbl = lambda x: \ |
||
1550 | label.setText(labelFormat % (x / divideFactor)) |
||
1551 | signal.connect(label.setLbl) |
||
1552 | |||
1553 | connectControl(master, value, callback, signal, CallFrontHSlider(slider)) |
||
1554 | |||
1555 | miscellanea(slider, sliderBox, widget, **misc) |
||
1556 | return slider |
||
1557 | |||
1558 | |||
1559 | def labeledSlider(widget, master, value, box=None, |
||
1560 | label=None, labels=(), labelFormat=" %d", ticks=False, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1561 | callback=None, vertical=False, width=None, **misc): |
||
1562 | """ |
||
1563 | Construct a slider with labels instead of numbers. |
||
1564 | |||
1565 | :param widget: the widget into which the box is inserted |
||
1566 | :type widget: PyQt4.QtGui.QWidget or None |
||
1567 | :param master: master widget |
||
1568 | :type master: OWWidget or OWComponent |
||
1569 | :param value: the master's attribute with which the value is synchronized |
||
1570 | :type value: str |
||
1571 | :param box: tells whether the widget has a border, and its label |
||
1572 | :type box: int or str or None |
||
1573 | :param label: a label that is inserted into the box |
||
1574 | :type label: str |
||
1575 | :param labels: labels shown at different slider positions |
||
1576 | :type labels: tuple of str |
||
1577 | :param callback: a function that is called when the value is changed |
||
1578 | :type callback: function |
||
1579 | |||
1580 | :param ticks: if set to `True`, ticks are added below the slider |
||
1581 | :type ticks: bool |
||
1582 | :param vertical: if set to `True`, the slider is vertical |
||
1583 | :type vertical: bool |
||
1584 | :param width: the width of the slider |
||
1585 | :type width: int |
||
1586 | :rtype: :obj:`PyQt4.QtGui.QSlider` |
||
1587 | """ |
||
1588 | sliderBox = widgetBox(widget, box, orientation="horizontal", |
||
1589 | addToLayout=False) |
||
1590 | if label: |
||
1591 | widgetLabel(sliderBox, label) |
||
1592 | sliderOrient = Qt.Vertical if vertical else Qt.Horizontal |
||
1593 | slider = QtGui.QSlider(sliderOrient, sliderBox) |
||
1594 | slider.ogValue = value |
||
1595 | slider.setRange(0, len(labels) - 1) |
||
1596 | slider.setSingleStep(1) |
||
1597 | slider.setPageStep(1) |
||
1598 | slider.setTickInterval(1) |
||
1599 | sliderBox.layout().addWidget(slider) |
||
1600 | slider.setValue(labels.index(getdeepattr(master, value))) |
||
1601 | if width: |
||
1602 | slider.setFixedWidth(width) |
||
1603 | if ticks: |
||
1604 | slider.setTickPosition(QtGui.QSlider.TicksBelow) |
||
1605 | slider.setTickInterval(ticks) |
||
1606 | |||
1607 | max_label_size = 0 |
||
1608 | slider.value_label = value_label = QtGui.QLabel(sliderBox) |
||
1609 | value_label.setAlignment(Qt.AlignRight) |
||
1610 | sliderBox.layout().addWidget(value_label) |
||
1611 | for lb in labels: |
||
1612 | value_label.setText(labelFormat % lb) |
||
1613 | max_label_size = max(max_label_size, value_label.sizeHint().width()) |
||
1614 | value_label.setFixedSize(max_label_size, value_label.sizeHint().height()) |
||
1615 | value_label.setText(getdeepattr(master, value)) |
||
1616 | if isinstance(labelFormat, str): |
||
1617 | value_label.set_label = lambda x: \ |
||
1618 | value_label.setText(labelFormat % x) |
||
1619 | else: |
||
1620 | value_label.set_label = lambda x: value_label.setText(labelFormat(x)) |
||
1621 | slider.valueChanged[int].connect(value_label.set_label) |
||
1622 | |||
1623 | connectControl(master, value, callback, slider.valueChanged[int], |
||
1624 | CallFrontLabeledSlider(slider, labels), |
||
1625 | CallBackLabeledSlider(slider, master, labels)) |
||
1626 | |||
1627 | miscellanea(slider, sliderBox, widget, **misc) |
||
1628 | return slider |
||
1629 | |||
1630 | |||
1631 | def valueSlider(widget, master, value, box=None, label=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1632 | values=(), labelFormat=" %d", ticks=False, |
||
1633 | callback=None, vertical=False, width=None, **misc): |
||
1634 | """ |
||
1635 | Construct a slider with different values. |
||
1636 | |||
1637 | :param widget: the widget into which the box is inserted |
||
1638 | :type widget: PyQt4.QtGui.QWidget or None |
||
1639 | :param master: master widget |
||
1640 | :type master: OWWidget or OWComponent |
||
1641 | :param value: the master's attribute with which the value is synchronized |
||
1642 | :type value: str |
||
1643 | :param box: tells whether the widget has a border, and its label |
||
1644 | :type box: int or str or None |
||
1645 | :param label: a label that is inserted into the box |
||
1646 | :type label: str |
||
1647 | :param values: values at different slider positions |
||
1648 | :type values: list of int |
||
1649 | :param labelFormat: label format; default is `" %d"`; can also be a function |
||
1650 | :type labelFormat: str or func |
||
1651 | :param callback: a function that is called when the value is changed |
||
1652 | :type callback: function |
||
1653 | |||
1654 | :param ticks: if set to `True`, ticks are added below the slider |
||
1655 | :type ticks: bool |
||
1656 | :param vertical: if set to `True`, the slider is vertical |
||
1657 | :type vertical: bool |
||
1658 | :param width: the width of the slider |
||
1659 | :type width: int |
||
1660 | :rtype: :obj:`PyQt4.QtGui.QSlider` |
||
1661 | """ |
||
1662 | if isinstance(labelFormat, str): |
||
1663 | labelFormat = lambda x, f=labelFormat: f(x) |
||
1664 | |||
1665 | sliderBox = widgetBox(widget, box, orientation="horizontal", |
||
1666 | addToLayout=False) |
||
1667 | if label: |
||
1668 | widgetLabel(sliderBox, label) |
||
1669 | slider_orient = Qt.Vertical if vertical else Qt.Horizontal |
||
1670 | slider = QtGui.QSlider(slider_orient, sliderBox) |
||
1671 | slider.ogValue = value |
||
1672 | slider.setRange(0, len(values) - 1) |
||
1673 | slider.setSingleStep(1) |
||
1674 | slider.setPageStep(1) |
||
1675 | slider.setTickInterval(1) |
||
1676 | sliderBox.layout().addWidget(slider) |
||
1677 | slider.setValue(values.index(getdeepattr(master, value))) |
||
1678 | if width: |
||
1679 | slider.setFixedWidth(width) |
||
1680 | if ticks: |
||
1681 | slider.setTickPosition(QtGui.QSlider.TicksBelow) |
||
1682 | slider.setTickInterval(ticks) |
||
1683 | |||
1684 | max_label_size = 0 |
||
1685 | slider.value_label = value_label = QtGui.QLabel(sliderBox) |
||
1686 | value_label.setAlignment(Qt.AlignRight) |
||
1687 | sliderBox.layout().addWidget(value_label) |
||
1688 | for lb in values: |
||
1689 | value_label.setText(labelFormat(lb)) |
||
1690 | max_label_size = max(max_label_size, value_label.sizeHint().width()) |
||
1691 | value_label.setFixedSize(max_label_size, value_label.sizeHint().height()) |
||
1692 | value_label.setText(labelFormat(getdeepattr(master, value))) |
||
1693 | value_label.set_label = lambda x: value_label.setText(labelFormat(values[x])) |
||
1694 | slider.valueChanged[int].connect(value_label.set_label) |
||
1695 | |||
1696 | connectControl(master, value, callback, slider.valueChanged[int], |
||
1697 | CallFrontLabeledSlider(slider, values), |
||
1698 | CallBackLabeledSlider(slider, master, values)) |
||
1699 | |||
1700 | miscellanea(slider, sliderBox, widget, **misc) |
||
1701 | return slider |
||
1702 | |||
1703 | |||
1704 | class OrangeComboBox(QtGui.QComboBox): |
||
1705 | """ |
||
1706 | A QtGui.QComboBox subclass extened to support bounded contents width hint. |
||
1707 | """ |
||
1708 | def __init__(self, parent=None, maximumContentsLength=-1, **kwargs): |
||
1709 | super().__init__(parent, **kwargs) |
||
1710 | self.__maximumContentsLength = maximumContentsLength |
||
1711 | |||
1712 | def setMaximumContentsLength(self, length): |
||
1713 | """ |
||
1714 | Set the maximum contents length hint. |
||
1715 | |||
1716 | The hint specifies the upper bound on the `sizeHint` and |
||
1717 | `minimumSizeHint` width specified in character length. |
||
1718 | Set to 0 or negative value to disable. |
||
1719 | |||
1720 | .. note:: |
||
1721 | This property does not affect the widget's `maximumSize`. |
||
1722 | The widget can still grow depending in it's sizePolicy. |
||
1723 | |||
1724 | Parameters |
||
1725 | ---------- |
||
1726 | lenght : int |
||
1727 | Maximum contents length hint. |
||
1728 | """ |
||
1729 | if self.__maximumContentsLength != length: |
||
1730 | self.__maximumContentsLength = length |
||
1731 | self.updateGeometry() |
||
1732 | |||
1733 | def maximumContentsLength(self): |
||
1734 | """ |
||
1735 | Return the maximum contents length hint. |
||
1736 | """ |
||
1737 | return self.__maximumContentsLength |
||
1738 | |||
1739 | def sizeHint(self): |
||
1740 | # reimplemented |
||
1741 | sh = super().sizeHint() |
||
1742 | if self.__maximumContentsLength > 0: |
||
1743 | width = (self.fontMetrics().width("X") * self.__maximumContentsLength |
||
1744 | + self.iconSize().width() + 4) |
||
1745 | sh = sh.boundedTo(QtCore.QSize(width, sh.height())) |
||
1746 | return sh |
||
1747 | |||
1748 | def minimumSizeHint(self): |
||
1749 | # reimplemented |
||
1750 | sh = super().minimumSizeHint() |
||
1751 | if self.__maximumContentsLength > 0: |
||
1752 | width = (self.fontMetrics().width("X") * self.__maximumContentsLength |
||
1753 | + self.iconSize().width() + 4) |
||
1754 | sh = sh.boundedTo(QtCore.QSize(width, sh.height())) |
||
1755 | return sh |
||
1756 | |||
1757 | |||
1758 | # TODO comboBox looks overly complicated: |
||
0 ignored issues
–
show
|
|||
1759 | # - is the argument control2attributeDict needed? doesn't emptyString do the |
||
1760 | # job? |
||
1761 | # - can valueType be anything else than str? |
||
1762 | # - sendSelectedValue is not a great name |
||
1763 | def comboBox(widget, master, value, box=None, label=None, labelWidth=None, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
1764 | orientation='vertical', items=(), callback=None, |
||
1765 | sendSelectedValue=False, valueType=str, |
||
1766 | control2attributeDict=None, emptyString=None, editable=False, |
||
1767 | contentsLength=None, maximumContentsLength=25, |
||
1768 | **misc): |
||
1769 | """ |
||
1770 | Construct a combo box. |
||
1771 | |||
1772 | The `value` attribute of the `master` contains either the index of the |
||
1773 | selected row (if `sendSelected` is left at default, `False`) or a value |
||
1774 | converted to `valueType` (`str` by default). |
||
1775 | |||
1776 | Furthermore, the value is converted by looking up into dictionary |
||
1777 | `control2attributeDict`. |
||
1778 | |||
1779 | :param widget: the widget into which the box is inserted |
||
1780 | :type widget: PyQt4.QtGui.QWidget or None |
||
1781 | :param master: master widget |
||
1782 | :type master: OWWidget or OWComponent |
||
1783 | :param value: the master's attribute with which the value is synchronized |
||
1784 | :type value: str |
||
1785 | :param box: tells whether the widget has a border, and its label |
||
1786 | :type box: int or str or None |
||
1787 | :param orientation: orientation of the layout in the box |
||
1788 | :type orientation: str or int or bool |
||
1789 | :param label: a label that is inserted into the box |
||
1790 | :type label: str |
||
1791 | :param labelWidth: the width of the label |
||
1792 | :type labelWidth: int |
||
1793 | :param callback: a function that is called when the value is changed |
||
1794 | :type callback: function |
||
1795 | :param items: items (optionally with data) that are put into the box |
||
1796 | :type items: tuple of str or tuples |
||
1797 | :param sendSelectedValue: flag telling whether to store/retrieve indices |
||
1798 | or string values from `value` |
||
1799 | :type sendSelectedValue: bool |
||
1800 | :param valueType: the type into which the selected value is converted |
||
1801 | if sentSelectedValue is `False` |
||
1802 | :type valueType: type |
||
1803 | :param control2attributeDict: a dictionary through which the value is |
||
1804 | converted |
||
1805 | :type control2attributeDict: dict or None |
||
1806 | :param emptyString: the string value in the combo box that gets stored as |
||
1807 | an empty string in `value` |
||
1808 | :type emptyString: str |
||
1809 | :param editable: a flag telling whether the combo is editable |
||
1810 | :type editable: bool |
||
1811 | :param int contentsLength: Contents character length to use as a |
||
1812 | fixed size hint. When not None, equivalent to:: |
||
1813 | |||
1814 | combo.setSizeAdjustPolicy( |
||
1815 | QComboBox.AdjustToMinimumContentsLengthWithIcon) |
||
1816 | combo.setMinimumContentsLength(contentsLength) |
||
1817 | :param int maximumContentsLength: Specifies the upper bound on the |
||
1818 | `sizeHint` and `minimumSizeHint` width specified in character |
||
1819 | length (default: 25, use 0 to disable) |
||
1820 | :rtype: PyQt4.QtGui.QComboBox |
||
1821 | """ |
||
1822 | if box or label: |
||
1823 | hb = widgetBox(widget, box, orientation, addToLayout=False) |
||
1824 | if label is not None: |
||
1825 | widgetLabel(hb, label, labelWidth) |
||
1826 | else: |
||
1827 | hb = widget |
||
1828 | |||
1829 | combo = OrangeComboBox( |
||
1830 | hb, maximumContentsLength=maximumContentsLength, |
||
1831 | editable=editable) |
||
1832 | |||
1833 | if contentsLength is not None: |
||
1834 | combo.setSizeAdjustPolicy( |
||
1835 | QtGui.QComboBox.AdjustToMinimumContentsLengthWithIcon) |
||
1836 | combo.setMinimumContentsLength(contentsLength) |
||
1837 | |||
1838 | combo.box = hb |
||
0 ignored issues
–
show
|
|||
1839 | for item in items: |
||
1840 | if isinstance(item, (tuple, list)): |
||
1841 | combo.addItem(*item) |
||
1842 | else: |
||
1843 | combo.addItem(str(item)) |
||
1844 | |||
1845 | if value: |
||
1846 | cindex = getdeepattr(master, value) |
||
1847 | if isinstance(cindex, str): |
||
1848 | if items and cindex in items: |
||
1849 | cindex = items.index(getdeepattr(master, value)) |
||
1850 | else: |
||
1851 | cindex = 0 |
||
1852 | if cindex > combo.count() - 1: |
||
1853 | cindex = 0 |
||
1854 | combo.setCurrentIndex(cindex) |
||
1855 | |||
1856 | if sendSelectedValue: |
||
1857 | if control2attributeDict is None: |
||
1858 | control2attributeDict = {} |
||
1859 | if emptyString: |
||
1860 | control2attributeDict[emptyString] = "" |
||
1861 | connectControl( |
||
1862 | master, value, callback, combo.activated[str], |
||
1863 | CallFrontComboBox(combo, valueType, control2attributeDict), |
||
1864 | ValueCallbackCombo(master, value, valueType, |
||
1865 | control2attributeDict)) |
||
1866 | else: |
||
1867 | connectControl( |
||
1868 | master, value, callback, combo.activated[int], |
||
1869 | CallFrontComboBox(combo, None, control2attributeDict)) |
||
1870 | miscellanea(combo, hb, widget, **misc) |
||
1871 | return combo |
||
1872 | |||
1873 | |||
1874 | class OrangeListBox(QtGui.QListWidget): |
||
1875 | """ |
||
1876 | List box with drag and drop functionality. Function :obj:`listBox` |
||
1877 | constructs instances of this class; do not use the class directly. |
||
1878 | |||
1879 | .. attribute:: master |
||
1880 | |||
1881 | The widget into which the listbox is inserted. |
||
1882 | |||
1883 | .. attribute:: ogLabels |
||
1884 | |||
1885 | The name of the master's attribute that holds the strings with items |
||
1886 | in the list box. |
||
1887 | |||
1888 | .. attribute:: ogValue |
||
1889 | |||
1890 | The name of the master's attribute that holds the indices of selected |
||
1891 | items. |
||
1892 | |||
1893 | .. attribute:: enableDragDrop |
||
1894 | |||
1895 | A flag telling whether drag-and-drop is enabled. |
||
1896 | |||
1897 | .. attribute:: dragDropCallback |
||
1898 | |||
1899 | A callback that is called at the end of drop event. |
||
1900 | |||
1901 | .. attribute:: dataValidityCallback |
||
1902 | |||
1903 | A callback that is called on dragEnter and dragMove events and returns |
||
1904 | either `ev.accept()` or `ev.ignore()`. |
||
1905 | |||
1906 | .. attribute:: defaultSizeHint |
||
1907 | |||
1908 | The size returned by the `sizeHint` method. |
||
1909 | """ |
||
1910 | def __init__(self, master, enableDragDrop=False, dragDropCallback=None, |
||
1911 | dataValidityCallback=None, sizeHint=None, *args): |
||
1912 | """ |
||
1913 | :param master: the master widget |
||
1914 | :type master: OWWidget or OWComponent |
||
1915 | :param enableDragDrop: flag telling whether drag and drop is enabled |
||
1916 | :type enableDragDrop: bool |
||
1917 | :param dragDropCallback: callback for the end of drop event |
||
1918 | :type dragDropCallback: function |
||
1919 | :param dataValidityCallback: callback that accepts or ignores dragEnter |
||
1920 | and dragMove events |
||
1921 | :type dataValidityCallback: function with one argument (event) |
||
1922 | :param sizeHint: size hint |
||
1923 | :type sizeHint: PyQt4.QtGui.QSize |
||
1924 | :param args: optional arguments for the inherited constructor |
||
1925 | """ |
||
1926 | self.master = master |
||
1927 | super().__init__(*args) |
||
1928 | self.drop_callback = dragDropCallback |
||
1929 | self.valid_data_callback = dataValidityCallback |
||
1930 | if not sizeHint: |
||
1931 | self.size_hint = QtCore.QSize(150, 100) |
||
1932 | else: |
||
1933 | self.size_hint = sizeHint |
||
1934 | if enableDragDrop: |
||
1935 | self.setDragEnabled(True) |
||
1936 | self.setAcceptDrops(True) |
||
1937 | self.setDropIndicatorShown(True) |
||
1938 | |||
1939 | def sizeHint(self): |
||
1940 | return self.size_hint |
||
1941 | |||
1942 | def dragEnterEvent(self, ev): |
||
1943 | super().dragEnterEvent(ev) |
||
1944 | if self.valid_data_callback: |
||
1945 | self.valid_data_callback(ev) |
||
1946 | elif isinstance(ev.source(), OrangeListBox): |
||
1947 | ev.setDropAction(Qt.MoveAction) |
||
1948 | ev.accept() |
||
1949 | else: |
||
1950 | ev.ignore() |
||
1951 | |||
1952 | def dropEvent(self, ev): |
||
1953 | ev.setDropAction(Qt.MoveAction) |
||
1954 | super().dropEvent(ev) |
||
1955 | |||
1956 | items = self.update_master() |
||
1957 | if ev.source() is not self: |
||
1958 | ev.source().update_master(exclude=items) |
||
1959 | |||
1960 | if self.drop_callback: |
||
1961 | self.drop_callback() |
||
1962 | |||
1963 | def update_master(self, exclude=()): |
||
1964 | control_list = [self.item(i).data(Qt.UserRole) for i in range(self.count()) if self.item(i).data(Qt.UserRole) not in exclude] |
||
0 ignored issues
–
show
|
|||
1965 | if self.ogLabels: |
||
1966 | master_list = getattr(self.master, self.ogLabels) |
||
1967 | |||
1968 | if master_list != control_list: |
||
1969 | setattr(self.master, self.ogLabels, control_list) |
||
1970 | return control_list |
||
1971 | |||
1972 | def updateGeometries(self): |
||
1973 | # A workaround for a bug in Qt |
||
1974 | # (see: http://bugreports.qt.nokia.com/browse/QTBUG-14412) |
||
1975 | if getattr(self, "_updatingGeometriesNow", False): |
||
1976 | return |
||
1977 | self._updatingGeometriesNow = True |
||
0 ignored issues
–
show
|
|||
1978 | try: |
||
1979 | return super().updateGeometries() |
||
1980 | finally: |
||
1981 | self._updatingGeometriesNow = False |
||
0 ignored issues
–
show
|
|||
1982 | |||
1983 | |||
1984 | # TODO: SmallWidgetButton is used only in OWkNNOptimization.py. (Re)Move. |
||
0 ignored issues
–
show
|
|||
1985 | # eliminated? |
||
1986 | class SmallWidgetButton(QtGui.QPushButton): |
||
1987 | def __init__(self, widget, text="", pixmap=None, box=None, |
||
1988 | orientation='vertical', autoHideWidget=None, **misc): |
||
1989 | #self.parent = parent |
||
1990 | if pixmap is not None: |
||
1991 | iconDir = os.path.join(os.path.dirname(__file__), "icons") |
||
1992 | name = "" |
||
1993 | if isinstance(pixmap, str): |
||
1994 | if os.path.exists(pixmap): |
||
1995 | name = pixmap |
||
1996 | elif os.path.exists(os.path.join(iconDir, pixmap)): |
||
1997 | name = os.path.join(iconDir, pixmap) |
||
1998 | elif isinstance(pixmap, (QtGui.QPixmap, QtGui.QIcon)): |
||
1999 | name = pixmap |
||
2000 | name = name or os.path.join(iconDir, "arrow_down.png") |
||
2001 | super().__init__(QtGui.QIcon(name), text, widget) |
||
2002 | else: |
||
2003 | super().__init__(text, widget) |
||
2004 | if widget.layout() is not None: |
||
2005 | widget.layout().addWidget(self) |
||
2006 | # create autohide widget and set a layout |
||
2007 | self.widget = self.autohideWidget = \ |
||
2008 | (autoHideWidget or AutoHideWidget)(None, Qt.Popup) |
||
2009 | setLayout(self.widget, orientation) |
||
2010 | if box: |
||
2011 | self.widget = widgetBox(self.widget, box, orientation) |
||
2012 | self.autohideWidget.hide() |
||
2013 | miscellanea(self, self.widget, widget, **misc) |
||
2014 | |||
2015 | def mousePressEvent(self, ev): |
||
2016 | super().mousePressEvent(ev) |
||
2017 | if self.autohideWidget.isVisible(): |
||
2018 | self.autohideWidget.hide() |
||
2019 | else: |
||
2020 | self.autohideWidget.move( |
||
2021 | self.mapToGlobal(QtCore.QPoint(0, self.height()))) |
||
2022 | self.autohideWidget.show() |
||
2023 | |||
2024 | |||
2025 | class SmallWidgetLabel(QtGui.QLabel): |
||
2026 | def __init__(self, widget, text="", pixmap=None, box=None, |
||
2027 | orientation='vertical', **misc): |
||
2028 | super().__init__(widget) |
||
2029 | if text: |
||
2030 | self.setText("<font color=\"#C10004\">" + text + "</font>") |
||
2031 | elif pixmap is not None: |
||
2032 | iconDir = os.path.join(os.path.dirname(__file__), "icons") |
||
2033 | name = "" |
||
2034 | if isinstance(pixmap, str): |
||
2035 | if os.path.exists(pixmap): |
||
2036 | name = pixmap |
||
2037 | elif os.path.exists(os.path.join(iconDir, pixmap)): |
||
2038 | name = os.path.join(iconDir, pixmap) |
||
2039 | elif isinstance(pixmap, (QtGui.QPixmap, QtGui.QIcon)): |
||
2040 | name = pixmap |
||
2041 | name = name or os.path.join(iconDir, "arrow_down.png") |
||
2042 | self.setPixmap(QtGui.QPixmap(name)) |
||
2043 | self.autohideWidget = self.widget = AutoHideWidget(None, Qt.Popup) |
||
2044 | setLayout(self.widget, orientation) |
||
2045 | if box: |
||
2046 | self.widget = widgetBox(self.widget, box, orientation) |
||
2047 | self.autohideWidget.hide() |
||
2048 | miscellanea(self, self.widget, widget, **misc) |
||
2049 | |||
2050 | def mousePressEvent(self, ev): |
||
2051 | super().mousePressEvent(ev) |
||
2052 | if self.autohideWidget.isVisible(): |
||
2053 | self.autohideWidget.hide() |
||
2054 | else: |
||
2055 | self.autohideWidget.move( |
||
2056 | self.mapToGlobal(QtCore.QPoint(0, self.height()))) |
||
2057 | self.autohideWidget.show() |
||
2058 | |||
2059 | |||
2060 | class AutoHideWidget(QtGui.QWidget): |
||
2061 | def leaveEvent(self, _): |
||
2062 | self.hide() |
||
2063 | |||
2064 | |||
2065 | # TODO Class SearchLineEdit: it doesn't seem to be used anywhere |
||
0 ignored issues
–
show
|
|||
2066 | # see widget DataDomain |
||
2067 | class SearchLineEdit(QtGui.QLineEdit): |
||
2068 | """ |
||
2069 | QLineEdit for quick searches |
||
2070 | """ |
||
2071 | def __init__(self, t, searcher): |
||
2072 | super().__init__(self, t) |
||
2073 | self.searcher = searcher |
||
2074 | |||
2075 | def keyPressEvent(self, e): |
||
2076 | """ |
||
2077 | Handles keys up and down by selecting the previous and the next item |
||
2078 | in the list, and the escape key, which hides the searcher. |
||
2079 | """ |
||
2080 | k = e.key() |
||
2081 | if k == Qt.Key_Down: |
||
2082 | curItem = self.searcher.lb.currentItem() |
||
2083 | if curItem + 1 < self.searcher.lb.count(): |
||
2084 | self.searcher.lb.setCurrentItem(curItem + 1) |
||
2085 | elif k == Qt.Key_Up: |
||
2086 | curItem = self.searcher.lb.currentItem() |
||
2087 | if curItem: |
||
2088 | self.searcher.lb.setCurrentItem(curItem - 1) |
||
2089 | elif k == Qt.Key_Escape: |
||
2090 | self.searcher.window.hide() |
||
2091 | else: |
||
2092 | return super().keyPressEvent(e) |
||
2093 | |||
2094 | |||
2095 | # TODO Class Searcher: it doesn't seem to be used anywhere |
||
0 ignored issues
–
show
|
|||
2096 | # see widget DataDomain |
||
2097 | class Searcher: |
||
2098 | """ |
||
2099 | The searcher class for :obj:`SearchLineEdit`. |
||
2100 | """ |
||
2101 | def __init__(self, control, master): |
||
2102 | self.control = control |
||
2103 | self.master = master |
||
2104 | |||
2105 | def __call__(self): |
||
2106 | _s = QtGui.QStyle |
||
2107 | self.window = t = QtGui.QFrame( |
||
0 ignored issues
–
show
|
|||
2108 | self.master, |
||
2109 | _s.WStyle_Dialog + _s.WStyle_Tool + _s.WStyle_Customize + |
||
2110 | _s.WStyle_NormalBorder) |
||
2111 | QtGui.QVBoxLayout(t).setAutoAdd(1) |
||
2112 | gs = self.master.mapToGlobal(QtCore.QPoint(0, 0)) |
||
2113 | gl = self.control.mapToGlobal(QtCore.QPoint(0, 0)) |
||
2114 | t.move(gl.x() - gs.x(), gl.y() - gs.y()) |
||
2115 | self.allItems = [self.control.text(i) |
||
0 ignored issues
–
show
|
|||
2116 | for i in range(self.control.count())] |
||
2117 | le = SearchLineEdit(t, self) |
||
2118 | self.lb = QtGui.QListWidget(t) |
||
0 ignored issues
–
show
|
|||
2119 | for i in self.allItems: |
||
2120 | self.lb.insertItem(i) |
||
2121 | t.setFixedSize(self.control.width(), 200) |
||
2122 | t.show() |
||
2123 | le.setFocus() |
||
2124 | le.textChanged.connect(self.textChanged) |
||
2125 | le.returnPressed.connect(self.returnPressed) |
||
2126 | self.lb.itemClicked.connect(self.mouseClicked) |
||
2127 | |||
2128 | def textChanged(self, s): |
||
2129 | s = str(s) |
||
2130 | self.lb.clear() |
||
2131 | for i in self.allItems: |
||
2132 | if s.lower() in i.lower(): |
||
2133 | self.lb.insertItem(i) |
||
2134 | |||
2135 | def returnPressed(self): |
||
2136 | if self.lb.count(): |
||
2137 | self.conclude(self.lb.text(max(0, self.lb.currentItem()))) |
||
2138 | else: |
||
2139 | self.window.hide() |
||
2140 | |||
2141 | def mouseClicked(self, item): |
||
2142 | self.conclude(item.text()) |
||
2143 | |||
2144 | def conclude(self, value): |
||
2145 | index = self.allItems.index(value) |
||
2146 | self.control.setCurrentItem(index) |
||
2147 | if self.control.cback: |
||
2148 | if self.control.sendSelectedValue: |
||
2149 | self.control.cback(value) |
||
2150 | else: |
||
2151 | self.control.cback(index) |
||
2152 | if self.control.cfunc: |
||
2153 | self.control.cfunc() |
||
2154 | self.window.hide() |
||
2155 | |||
2156 | |||
2157 | # creates a widget box with a button in the top right edge that shows/hides all |
||
2158 | # widgets in the box and collapse the box to its minimum height |
||
2159 | # TODO collapsableWidgetBox is used only in OWMosaicDisplay.py; (re)move |
||
0 ignored issues
–
show
|
|||
2160 | class collapsableWidgetBox(QtGui.QGroupBox): |
||
2161 | def __init__(self, widget, box="", master=None, value="", |
||
2162 | orientation="vertical", callback=None): |
||
2163 | super().__init__(widget) |
||
2164 | self.setFlat(1) |
||
2165 | setLayout(self, orientation) |
||
2166 | if widget.layout() is not None: |
||
2167 | widget.layout().addWidget(self) |
||
2168 | if isinstance(box, str): |
||
2169 | self.setTitle(" " + box.strip() + " ") |
||
2170 | self.setCheckable(True) |
||
2171 | self.master = master |
||
2172 | self.value = value |
||
2173 | self.callback = callback |
||
2174 | self.clicked.connect(self.toggled) |
||
2175 | |||
2176 | def toggled(self, _=0): |
||
2177 | if self.value: |
||
2178 | self.master.__setattr__(self.value, self.isChecked()) |
||
2179 | self.updateControls() |
||
2180 | if self.callback is not None: |
||
2181 | self.callback() |
||
2182 | |||
2183 | def updateControls(self): |
||
2184 | val = getdeepattr(self.master, self.value) |
||
2185 | width = self.width() |
||
2186 | self.setChecked(val) |
||
2187 | self.setFlat(not val) |
||
2188 | self.setMinimumSize(QtCore.QSize(width if not val else 0, 0)) |
||
2189 | for c in self.children(): |
||
2190 | if isinstance(c, QtGui.QLayout): |
||
2191 | continue |
||
2192 | if val: |
||
2193 | c.show() |
||
2194 | else: |
||
2195 | c.hide() |
||
2196 | |||
2197 | |||
2198 | # creates an icon that allows you to show/hide the widgets in the widgets list |
||
2199 | # TODO Class widgetHider doesn't seem to be used anywhere; remove? |
||
0 ignored issues
–
show
|
|||
2200 | class widgetHider(QtGui.QWidget): |
||
2201 | def __init__(self, widget, master, value, _=(19, 19), widgets=None, |
||
2202 | tooltip=None): |
||
2203 | super().__init__(widget) |
||
2204 | if widget.layout() is not None: |
||
2205 | widget.layout().addWidget(self) |
||
2206 | self.value = value |
||
2207 | self.master = master |
||
2208 | if tooltip: |
||
2209 | self.setToolTip(tooltip) |
||
2210 | iconDir = os.path.join(os.path.dirname(__file__), "icons") |
||
2211 | icon1 = os.path.join(iconDir, "arrow_down.png") |
||
2212 | icon2 = os.path.join(iconDir, "arrow_up.png") |
||
2213 | self.pixmaps = [QtGui.QPixmap(icon1), QtGui.QPixmap(icon2)] |
||
2214 | self.setFixedSize(self.pixmaps[0].size()) |
||
2215 | self.disables = list(widgets or []) |
||
2216 | self.makeConsistent = Disabler(self, master, value, type=HIDER) |
||
2217 | if widgets: |
||
2218 | self.setWidgets(widgets) |
||
2219 | |||
2220 | def mousePressEvent(self, ev): |
||
0 ignored issues
–
show
|
|||
2221 | self.master.__setattr__(self.value, |
||
2222 | not getdeepattr(self.master, self.value)) |
||
2223 | self.makeConsistent() |
||
2224 | |||
2225 | def setWidgets(self, widgets): |
||
2226 | self.disables = list(widgets) |
||
2227 | self.makeConsistent() |
||
2228 | |||
2229 | def paintEvent(self, ev): |
||
2230 | super().paintEvent(ev) |
||
2231 | if self.pixmaps: |
||
2232 | pix = self.pixmaps[getdeepattr(self.master, self.value)] |
||
2233 | painter = QtGui.QPainter(self) |
||
2234 | painter.drawPixmap(0, 0, pix) |
||
2235 | |||
2236 | |||
2237 | ############################################################################## |
||
2238 | # callback handlers |
||
2239 | |||
2240 | |||
2241 | def auto_commit(widget, master, value, label, auto_label=None, box=True, |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
2242 | checkbox_label=None, orientation=None, **misc): |
||
2243 | """ |
||
2244 | Add a commit button with auto-commit check box. |
||
2245 | |||
2246 | The widget must have a commit method and a setting that stores whether |
||
2247 | auto-commit is on. |
||
2248 | |||
2249 | The function replaces the commit method with a new commit method that |
||
2250 | checks whether auto-commit is on. If it is, it passes the call to the |
||
2251 | original commit, otherwise it sets the dirty flag. |
||
2252 | |||
2253 | The checkbox controls the auto-commit. When auto-commit is switched on, the |
||
2254 | checkbox callback checks whether the dirty flag is on and calls the original |
||
2255 | commit. |
||
2256 | |||
2257 | Important! Do not connect any signals to the commit before calling |
||
2258 | auto_commit. |
||
2259 | |||
2260 | :param widget: the widget into which the box with the button is inserted |
||
2261 | :type widget: PyQt4.QtGui.QWidget or None |
||
2262 | :param value: the master's attribute which stores whether the auto-commit |
||
2263 | is on |
||
2264 | :type value: str |
||
2265 | :param master: master widget |
||
2266 | :type master: OWWidget or OWComponent |
||
2267 | :param label: The button label |
||
2268 | :type label: str |
||
2269 | :param label: The label used when auto-commit is on; default is |
||
2270 | `"Auto " + label` |
||
2271 | :type label: str |
||
2272 | :param box: tells whether the widget has a border, and its label |
||
2273 | :type box: int or str or None |
||
2274 | :return: the box |
||
2275 | """ |
||
2276 | def u(): |
||
2277 | if getattr(master, value): |
||
2278 | btn.setText(auto_label) |
||
2279 | btn.setEnabled(False) |
||
2280 | if dirty: |
||
2281 | do_commit() |
||
2282 | else: |
||
2283 | btn.setText(label) |
||
2284 | btn.setEnabled(True) |
||
2285 | |||
2286 | def commit(): |
||
2287 | nonlocal dirty |
||
2288 | if getattr(master, value): |
||
2289 | do_commit() |
||
2290 | else: |
||
2291 | dirty = True |
||
2292 | |||
2293 | def do_commit(): |
||
2294 | nonlocal dirty |
||
2295 | QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
||
2296 | master.unconditional_commit() |
||
2297 | QApplication.restoreOverrideCursor() |
||
2298 | dirty = False |
||
2299 | |||
2300 | dirty = False |
||
2301 | master.unconditional_commit = master.commit |
||
2302 | if not auto_label: |
||
2303 | if checkbox_label: |
||
2304 | auto_label = label |
||
2305 | else: |
||
2306 | auto_label = "Auto " + label.lower() + " is on" |
||
2307 | if isinstance(box, QtGui.QWidget): |
||
2308 | b = box |
||
2309 | else: |
||
2310 | if orientation is None: |
||
2311 | orientation = bool(checkbox_label) |
||
2312 | b = widgetBox(widget, box=box, orientation=orientation, |
||
2313 | addToLayout=False) |
||
2314 | b.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Maximum) |
||
2315 | |||
2316 | b.checkbox = cb = checkBox(b, master, value, checkbox_label or " ", |
||
2317 | callback=u, tooltip=auto_label) |
||
2318 | cb.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) |
||
2319 | b.button = btn = button(b, master, label, callback=do_commit) |
||
2320 | if not checkbox_label: |
||
2321 | btn.setSizePolicy(QtGui.QSizePolicy.Expanding, |
||
2322 | QtGui.QSizePolicy.Preferred) |
||
2323 | u() |
||
2324 | master.commit = commit |
||
2325 | miscellanea(b, widget, widget, |
||
2326 | addToLayout=not isinstance(box, QtGui.QWidget), **misc) |
||
2327 | return b |
||
2328 | |||
2329 | |||
2330 | class ControlledList(list): |
||
2331 | """ |
||
2332 | A class derived from a list that is connected to a |
||
2333 | :obj:`PyQt4.QtGui.QListBox`: the list contains indices of items that are |
||
2334 | selected in the list box. Changing the list content changes the |
||
2335 | selection in the list box. |
||
2336 | """ |
||
2337 | def __init__(self, content, listBox=None): |
||
0 ignored issues
–
show
listBox is re-defining a name which is already available in the outer-scope (previously defined on line 1304 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
2338 | super().__init__(content) |
||
2339 | self.listBox = listBox |
||
2340 | |||
2341 | def __reduce__(self): |
||
2342 | # cannot pickle self.listBox, but can't discard it |
||
2343 | # (ControlledList may live on) |
||
2344 | import copyreg |
||
2345 | return copyreg._reconstructor, (list, list, ()), None, self.__iter__() |
||
0 ignored issues
–
show
It seems like
_reconstructor was declared protected and should not be accessed from this context.
Prefixing a member variable class MyParent:
def __init__(self):
self._x = 1;
self.y = 2;
class MyChild(MyParent):
def some_method(self):
return self._x # Ok, since accessed from a child class
class AnotherClass:
def some_method(self, instance_of_my_child):
return instance_of_my_child._x # Would be flagged as AnotherClass is not
# a child class of MyParent
![]() |
|||
2346 | |||
2347 | # TODO ControllgedList.item2name is probably never used |
||
0 ignored issues
–
show
|
|||
2348 | def item2name(self, item): |
||
2349 | item = self.listBox.labels[item] |
||
2350 | if type(item) is tuple: |
||
2351 | return item[1] |
||
2352 | else: |
||
2353 | return item |
||
2354 | |||
2355 | def __setitem__(self, index, item): |
||
2356 | if isinstance(index, int): |
||
2357 | self.listBox.item(self[index]).setSelected(0) |
||
2358 | item.setSelected(1) |
||
2359 | else: |
||
2360 | for i in self[index]: |
||
2361 | self.listBox.item(i).setSelected(0) |
||
2362 | for i in item: |
||
2363 | self.listBox.item(i).setSelected(1) |
||
2364 | super().__setitem__(index, item) |
||
2365 | |||
2366 | def __delitem__(self, index): |
||
2367 | if isinstance(index, int): |
||
2368 | self.listBox.item(self[index]).setSelected(0) |
||
2369 | else: |
||
2370 | for i in self[index]: |
||
2371 | self.listBox.item(i).setSelected(0) |
||
2372 | super().__delitem__(index) |
||
2373 | |||
2374 | def append(self, item): |
||
2375 | super().append(item) |
||
2376 | item.setSelected(1) |
||
2377 | |||
2378 | def extend(self, items): |
||
2379 | super().extend(items) |
||
2380 | for i in items: |
||
2381 | self.listBox.item(i).setSelected(1) |
||
2382 | |||
2383 | def insert(self, index, item): |
||
2384 | item.setSelected(1) |
||
2385 | super().insert(index, item) |
||
2386 | |||
2387 | def pop(self, index=-1): |
||
2388 | i = super().pop(index) |
||
2389 | self.listBox.item(i).setSelected(0) |
||
2390 | |||
2391 | def remove(self, item): |
||
2392 | item.setSelected(0) |
||
2393 | super().remove(item) |
||
2394 | |||
2395 | |||
2396 | def connectControl(master, value, f, signal, |
||
2397 | cfront, cback=None, cfunc=None, fvcb=None): |
||
2398 | cback = cback or value and ValueCallback(master, value, fvcb) |
||
2399 | if cback: |
||
2400 | if signal: |
||
2401 | signal.connect(cback) |
||
2402 | cback.opposite = cfront |
||
2403 | if value and cfront and hasattr(master, CONTROLLED_ATTRIBUTES): |
||
2404 | getattr(master, CONTROLLED_ATTRIBUTES)[value] = cfront |
||
2405 | cfunc = cfunc or f and FunctionCallback(master, f) |
||
2406 | if cfunc: |
||
2407 | if signal: |
||
2408 | signal.connect(cfunc) |
||
2409 | cfront.opposite = tuple(filter(None, (cback, cfunc))) |
||
2410 | return cfront, cback, cfunc |
||
2411 | |||
2412 | |||
2413 | class ControlledCallback: |
||
2414 | def __init__(self, widget, attribute, f=None): |
||
2415 | self.widget = widget |
||
2416 | self.attribute = attribute |
||
2417 | self.f = f |
||
2418 | self.disabled = 0 |
||
2419 | if isinstance(widget, dict): |
||
2420 | return # we can't assign attributes to dict |
||
2421 | if not hasattr(widget, "callbackDeposit"): |
||
2422 | widget.callbackDeposit = [] |
||
2423 | widget.callbackDeposit.append(self) |
||
2424 | |||
2425 | def acyclic_setattr(self, value): |
||
2426 | if self.disabled: |
||
2427 | return |
||
2428 | if self.f: |
||
2429 | if self.f in (int, float) and ( |
||
2430 | not value or isinstance(value, str) and value in "+-"): |
||
2431 | value = self.f(0) |
||
2432 | else: |
||
2433 | value = self.f(value) |
||
2434 | opposite = getattr(self, "opposite", None) |
||
2435 | if opposite: |
||
2436 | try: |
||
2437 | opposite.disabled += 1 |
||
2438 | if type(self.widget) is dict: |
||
2439 | self.widget[self.attribute] = value |
||
2440 | else: |
||
2441 | setattr(self.widget, self.attribute, value) |
||
2442 | finally: |
||
2443 | opposite.disabled -= 1 |
||
2444 | else: |
||
2445 | if isinstance(self.widget, dict): |
||
2446 | self.widget[self.attribute] = value |
||
2447 | else: |
||
2448 | setattr(self.widget, self.attribute, value) |
||
2449 | |||
2450 | |||
2451 | class ValueCallback(ControlledCallback): |
||
2452 | # noinspection PyBroadException |
||
2453 | def __call__(self, value): |
||
2454 | if value is None: |
||
2455 | return |
||
2456 | try: |
||
2457 | self.acyclic_setattr(value) |
||
2458 | except: |
||
0 ignored issues
–
show
General except handlers without types should be used sparingly.
Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of. ![]() |
|||
2459 | print("gui.ValueCallback: %s" % value) |
||
2460 | import traceback |
||
2461 | import sys |
||
2462 | traceback.print_exception(*sys.exc_info()) |
||
2463 | |||
2464 | |||
2465 | class ValueCallbackCombo(ValueCallback): |
||
2466 | def __init__(self, widget, attribute, f=None, control2attributeDict=None): |
||
2467 | super().__init__(widget, attribute, f) |
||
2468 | self.control2attributeDict = control2attributeDict or {} |
||
2469 | |||
2470 | def __call__(self, value): |
||
2471 | value = str(value) |
||
2472 | return super().__call__(self.control2attributeDict.get(value, value)) |
||
2473 | |||
2474 | |||
2475 | class ValueCallbackLineEdit(ControlledCallback): |
||
2476 | def __init__(self, control, widget, attribute, f=None): |
||
2477 | ControlledCallback.__init__(self, widget, attribute, f) |
||
2478 | self.control = control |
||
2479 | |||
2480 | # noinspection PyBroadException |
||
2481 | def __call__(self, value): |
||
2482 | if value is None: |
||
2483 | return |
||
2484 | try: |
||
2485 | pos = self.control.cursorPosition() |
||
2486 | self.acyclic_setattr(value) |
||
2487 | self.control.setCursorPosition(pos) |
||
2488 | except: |
||
0 ignored issues
–
show
General except handlers without types should be used sparingly.
Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of. ![]() |
|||
2489 | print("invalid value ", value, type(value)) |
||
2490 | import traceback |
||
2491 | import sys |
||
2492 | traceback.print_exception(*sys.exc_info()) |
||
2493 | |||
2494 | |||
2495 | class SetLabelCallback: |
||
2496 | def __init__(self, widget, label, format="%5.2f", f=None): |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
2497 | self.widget = widget |
||
2498 | self.label = label |
||
2499 | self.format = format |
||
2500 | self.f = f |
||
2501 | if hasattr(widget, "callbackDeposit"): |
||
2502 | widget.callbackDeposit.append(self) |
||
2503 | self.disabled = 0 |
||
2504 | |||
2505 | def __call__(self, value): |
||
2506 | if not self.disabled and value is not None: |
||
2507 | if self.f: |
||
2508 | value = self.f(value) |
||
2509 | self.label.setText(self.format % value) |
||
2510 | |||
2511 | |||
2512 | class FunctionCallback: |
||
2513 | def __init__(self, master, f, widget=None, id=None, getwidget=False): |
||
0 ignored issues
–
show
|
|||
2514 | self.master = master |
||
2515 | self.widget = widget |
||
2516 | self.f = f |
||
2517 | self.id = id |
||
2518 | self.getwidget = getwidget |
||
2519 | if hasattr(master, "callbackDeposit"): |
||
2520 | master.callbackDeposit.append(self) |
||
2521 | self.disabled = 0 |
||
2522 | |||
2523 | def __call__(self, *value): |
||
2524 | if not self.disabled and value is not None: |
||
2525 | kwds = {} |
||
2526 | if self.id is not None: |
||
2527 | kwds['id'] = self.id |
||
2528 | if self.getwidget: |
||
2529 | kwds['widget'] = self.widget |
||
2530 | if isinstance(self.f, list): |
||
2531 | for f in self.f: |
||
2532 | f(**kwds) |
||
2533 | else: |
||
2534 | self.f(**kwds) |
||
2535 | |||
2536 | |||
2537 | class CallBackListBox: |
||
2538 | def __init__(self, control, widget): |
||
2539 | self.control = control |
||
2540 | self.widget = widget |
||
2541 | self.disabled = 0 |
||
2542 | |||
2543 | def __call__(self, *_): # triggered by selectionChange() |
||
2544 | if not self.disabled and self.control.ogValue is not None: |
||
2545 | clist = getdeepattr(self.widget, self.control.ogValue) |
||
2546 | # skip the overloaded method to avoid a cycle |
||
2547 | list.__delitem__(clist, slice(0, len(clist))) |
||
2548 | control = self.control |
||
2549 | for i in range(control.count()): |
||
2550 | if control.item(i).isSelected(): |
||
2551 | list.append(clist, i) |
||
2552 | self.widget.__setattr__(self.control.ogValue, clist) |
||
2553 | |||
2554 | |||
2555 | class CallBackRadioButton: |
||
2556 | def __init__(self, control, widget): |
||
2557 | self.control = control |
||
2558 | self.widget = widget |
||
2559 | self.disabled = False |
||
2560 | |||
2561 | def __call__(self, *_): # triggered by toggled() |
||
2562 | if not self.disabled and self.control.ogValue is not None: |
||
2563 | arr = [butt.isChecked() for butt in self.control.buttons] |
||
2564 | self.widget.__setattr__(self.control.ogValue, arr.index(1)) |
||
2565 | |||
2566 | |||
2567 | class CallBackLabeledSlider: |
||
2568 | def __init__(self, control, widget, lookup): |
||
2569 | self.control = control |
||
2570 | self.widget = widget |
||
2571 | self.lookup = lookup |
||
2572 | self.disabled = False |
||
2573 | |||
2574 | def __call__(self, *_): |
||
2575 | if not self.disabled and self.control.ogValue is not None: |
||
2576 | self.widget.__setattr__(self.control.ogValue, |
||
2577 | self.lookup[self.control.value()]) |
||
2578 | |||
2579 | |||
2580 | ############################################################################## |
||
2581 | # call fronts (change of the attribute value changes the related control) |
||
2582 | |||
2583 | |||
2584 | class ControlledCallFront: |
||
2585 | def __init__(self, control): |
||
2586 | self.control = control |
||
2587 | self.disabled = 0 |
||
2588 | |||
2589 | def action(self, *_): |
||
2590 | pass |
||
2591 | |||
2592 | def __call__(self, *args): |
||
2593 | if not self.disabled: |
||
2594 | opposite = getattr(self, "opposite", None) |
||
2595 | if opposite: |
||
2596 | try: |
||
2597 | for op in opposite: |
||
2598 | op.disabled += 1 |
||
2599 | self.action(*args) |
||
2600 | finally: |
||
2601 | for op in opposite: |
||
2602 | op.disabled -= 1 |
||
0 ignored issues
–
show
|
|||
2603 | else: |
||
2604 | self.action(*args) |
||
2605 | |||
2606 | |||
2607 | class CallFrontSpin(ControlledCallFront): |
||
2608 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2609 | if value is not None: |
||
2610 | self.control.setValue(value) |
||
2611 | |||
2612 | |||
2613 | class CallFrontDoubleSpin(ControlledCallFront): |
||
2614 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2615 | if value is not None: |
||
2616 | self.control.setValue(value) |
||
2617 | |||
2618 | |||
2619 | class CallFrontCheckBox(ControlledCallFront): |
||
2620 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2621 | if value is not None: |
||
2622 | values = [Qt.Unchecked, Qt.Checked, Qt.PartiallyChecked] |
||
2623 | self.control.setCheckState(values[value]) |
||
2624 | |||
2625 | |||
2626 | class CallFrontButton(ControlledCallFront): |
||
2627 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2628 | if value is not None: |
||
2629 | self.control.setChecked(bool(value)) |
||
2630 | |||
2631 | |||
2632 | class CallFrontComboBox(ControlledCallFront): |
||
2633 | def __init__(self, control, valType=None, control2attributeDict=None): |
||
2634 | super().__init__(control) |
||
2635 | self.valType = valType |
||
2636 | if control2attributeDict is None: |
||
2637 | self.attribute2controlDict = {} |
||
2638 | else: |
||
2639 | self.attribute2controlDict = \ |
||
2640 | {y: x for x, y in control2attributeDict.items()} |
||
2641 | |||
2642 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2643 | if value is not None: |
||
2644 | value = self.attribute2controlDict.get(value, value) |
||
2645 | if self.valType: |
||
2646 | for i in range(self.control.count()): |
||
2647 | if self.valType(str(self.control.itemText(i))) == value: |
||
2648 | self.control.setCurrentIndex(i) |
||
2649 | return |
||
2650 | values = "" |
||
2651 | for i in range(self.control.count()): |
||
2652 | values += str(self.control.itemText(i)) + \ |
||
2653 | (i < self.control.count() - 1 and ", " or ".") |
||
2654 | print("unable to set %s to value '%s'. Possible values are %s" |
||
2655 | % (self.control, value, values)) |
||
2656 | else: |
||
2657 | if value < self.control.count(): |
||
2658 | self.control.setCurrentIndex(value) |
||
2659 | |||
2660 | |||
2661 | class CallFrontHSlider(ControlledCallFront): |
||
2662 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2663 | if value is not None: |
||
2664 | self.control.setValue(value) |
||
2665 | |||
2666 | |||
2667 | class CallFrontLabeledSlider(ControlledCallFront): |
||
2668 | def __init__(self, control, lookup): |
||
2669 | super().__init__(control) |
||
2670 | self.lookup = lookup |
||
2671 | |||
2672 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2673 | if value is not None: |
||
2674 | self.control.setValue(self.lookup.index(value)) |
||
2675 | |||
2676 | |||
2677 | class CallFrontLogSlider(ControlledCallFront): |
||
2678 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2679 | if value is not None: |
||
2680 | if value < 1e-30: |
||
2681 | print("unable to set %s to %s (value too small)" % |
||
2682 | (self.control, value)) |
||
2683 | else: |
||
2684 | self.control.setValue(math.log10(value)) |
||
2685 | |||
2686 | |||
2687 | class CallFrontLineEdit(ControlledCallFront): |
||
2688 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2689 | self.control.setText(str(value)) |
||
2690 | |||
2691 | |||
2692 | class CallFrontRadioButtons(ControlledCallFront): |
||
2693 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2694 | if value < 0 or value >= len(self.control.buttons): |
||
2695 | value = 0 |
||
2696 | self.control.buttons[value].setChecked(1) |
||
2697 | |||
2698 | |||
2699 | class CallFrontListBox(ControlledCallFront): |
||
2700 | def action(self, value): |
||
0 ignored issues
–
show
|
|||
2701 | if value is not None: |
||
2702 | if not isinstance(value, ControlledList): |
||
2703 | setattr(self.control.ogMaster, self.control.ogValue, |
||
2704 | ControlledList(value, self.control)) |
||
2705 | for i in range(self.control.count()): |
||
2706 | shouldBe = i in value |
||
2707 | if shouldBe != self.control.item(i).isSelected(): |
||
2708 | self.control.item(i).setSelected(shouldBe) |
||
2709 | |||
2710 | |||
2711 | class CallFrontListBoxLabels(ControlledCallFront): |
||
2712 | unknownType = None |
||
2713 | |||
2714 | def action(self, values): |
||
0 ignored issues
–
show
|
|||
2715 | self.control.clear() |
||
2716 | if values: |
||
2717 | for value in values: |
||
2718 | if isinstance(value, tuple): |
||
2719 | text, icon = value |
||
2720 | if isinstance(icon, int): |
||
2721 | item = QtGui.QListWidgetItem(attributeIconDict[icon], text) |
||
2722 | else: |
||
2723 | item = QtGui.QListWidgetItem(icon, text) |
||
2724 | elif isinstance(value, Variable): |
||
2725 | item = QtGui.QListWidgetItem(*attributeItem(value)) |
||
2726 | else: |
||
2727 | item = QtGui.QListWidgetItem(value) |
||
2728 | |||
2729 | item.setData(Qt.UserRole, value) |
||
2730 | self.control.addItem(item) |
||
2731 | |||
2732 | |||
2733 | class CallFrontLabel: |
||
2734 | def __init__(self, control, label, master): |
||
0 ignored issues
–
show
label is re-defining a name which is already available in the outer-scope (previously defined on line 597 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
2735 | self.control = control |
||
2736 | self.label = label |
||
2737 | self.master = master |
||
2738 | |||
2739 | def __call__(self, *_): |
||
2740 | self.control.setText(self.label % self.master.__dict__) |
||
2741 | |||
2742 | ############################################################################## |
||
2743 | ## Disabler is a call-back class for check box that can disable/enable other |
||
2744 | ## widgets according to state (checked/unchecked, enabled/disable) of the |
||
2745 | ## given check box |
||
2746 | ## |
||
2747 | ## Tricky: if self.propagateState is True (default), then if check box is |
||
2748 | ## disabled the related widgets will be disabled (even if the checkbox is |
||
2749 | ## checked). If self.propagateState is False, the related widgets will be |
||
2750 | ## disabled/enabled if check box is checked/clear, disregarding whether the |
||
2751 | ## check box itself is enabled or not. (If you don't understand, see the |
||
2752 | ## code :-) |
||
2753 | DISABLER = 1 |
||
2754 | HIDER = 2 |
||
2755 | |||
2756 | |||
2757 | # noinspection PyShadowingBuiltins |
||
2758 | class Disabler: |
||
2759 | def __init__(self, widget, master, valueName, propagateState=True, |
||
2760 | type=DISABLER): |
||
0 ignored issues
–
show
|
|||
2761 | self.widget = widget |
||
2762 | self.master = master |
||
2763 | self.valueName = valueName |
||
2764 | self.propagateState = propagateState |
||
2765 | self.type = type |
||
2766 | |||
2767 | def __call__(self, *value): |
||
2768 | currState = self.widget.isEnabled() |
||
2769 | if currState or not self.propagateState: |
||
2770 | if len(value): |
||
2771 | disabled = not value[0] |
||
2772 | else: |
||
2773 | disabled = not getdeepattr(self.master, self.valueName) |
||
2774 | else: |
||
2775 | disabled = 1 |
||
2776 | for w in self.widget.disables: |
||
2777 | if type(w) is tuple: |
||
2778 | if isinstance(w[0], int): |
||
2779 | i = 1 |
||
2780 | if w[0] == -1: |
||
2781 | disabled = not disabled |
||
2782 | else: |
||
2783 | i = 0 |
||
2784 | if self.type == DISABLER: |
||
2785 | w[i].setDisabled(disabled) |
||
2786 | elif self.type == HIDER: |
||
2787 | if disabled: |
||
2788 | w[i].hide() |
||
2789 | else: |
||
2790 | w[i].show() |
||
2791 | if hasattr(w[i], "makeConsistent"): |
||
2792 | w[i].makeConsistent() |
||
2793 | else: |
||
2794 | if self.type == DISABLER: |
||
2795 | w.setDisabled(disabled) |
||
2796 | elif self.type == HIDER: |
||
2797 | if disabled: |
||
2798 | w.hide() |
||
2799 | else: |
||
2800 | w.show() |
||
2801 | |||
2802 | ############################################################################## |
||
2803 | # some table related widgets |
||
2804 | |||
2805 | |||
2806 | # noinspection PyShadowingBuiltins |
||
2807 | class tableItem(QtGui.QTableWidgetItem): |
||
2808 | def __init__(self, table, x, y, text, editType=None, backColor=None, |
||
0 ignored issues
–
show
table is re-defining a name which is already available in the outer-scope (previously defined on line 3216 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
2809 | icon=None, type=QtGui.QTableWidgetItem.Type): |
||
0 ignored issues
–
show
|
|||
2810 | super().__init__(type) |
||
2811 | if icon: |
||
2812 | self.setIcon(QtGui.QIcon(icon)) |
||
2813 | if editType is not None: |
||
2814 | self.setFlags(editType) |
||
2815 | else: |
||
2816 | self.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | |
||
2817 | Qt.ItemIsSelectable) |
||
2818 | if backColor is not None: |
||
2819 | self.setBackground(QtGui.QBrush(backColor)) |
||
2820 | # we add it this way so that text can also be int and sorting will be |
||
2821 | # done properly (as integers and not as text) |
||
2822 | self.setData(Qt.DisplayRole, text) |
||
2823 | table.setItem(x, y, self) |
||
2824 | |||
2825 | |||
2826 | TableValueRole = next(OrangeUserRole) # Role to retrieve orange.Value |
||
2827 | TableClassValueRole = next(OrangeUserRole) # Retrieve class value for the row |
||
2828 | TableDistribution = next(OrangeUserRole) # Retrieve distribution of the column |
||
2829 | TableVariable = next(OrangeUserRole) # Role to retrieve the column's variable |
||
2830 | |||
2831 | BarRatioRole = next(OrangeUserRole) # Ratio for drawing distribution bars |
||
2832 | BarBrushRole = next(OrangeUserRole) # Brush for distribution bar |
||
2833 | |||
2834 | SortOrderRole = next(OrangeUserRole) # Used for sorting |
||
2835 | |||
2836 | |||
2837 | class TableBarItem(QtGui.QItemDelegate): |
||
2838 | BarRole = next(OrangeUserRole) |
||
2839 | ColorRole = next(OrangeUserRole) |
||
2840 | |||
2841 | def __init__(self, parent=None, color=QtGui.QColor(255, 170, 127), |
||
2842 | color_schema=None): |
||
2843 | """ |
||
2844 | :param QObject parent: Parent object. |
||
2845 | :param QColor color: Default color of the distribution bar. |
||
2846 | :param color_schema: |
||
2847 | If not None it must be an instance of |
||
2848 | :class:`OWColorPalette.ColorPaletteGenerator` (note: this |
||
2849 | parameter, if set, overrides the ``color``) |
||
2850 | :type color_schema: :class:`OWColorPalette.ColorPaletteGenerator` |
||
2851 | """ |
||
2852 | super().__init__(parent) |
||
2853 | self.color = color |
||
2854 | self.color_schema = color_schema |
||
2855 | |||
2856 | def paint(self, painter, option, index): |
||
2857 | painter.save() |
||
2858 | self.drawBackground(painter, option, index) |
||
2859 | ratio = index.data(TableBarItem.BarRole) |
||
2860 | if isinstance(ratio, float): |
||
2861 | if math.isnan(ratio): |
||
2862 | ratio = None |
||
2863 | |||
2864 | color = self.color |
||
2865 | if self.color_schema is not None and ratio is not None: |
||
2866 | class_ = index.data(TableClassValueRole) |
||
2867 | if isinstance(class_, Orange.data.Value) and \ |
||
2868 | class_.variable.is_discrete and \ |
||
2869 | not math.isnan(class_): |
||
2870 | color = self.color_schema[int(class_)] |
||
2871 | |||
2872 | if ratio is not None: |
||
2873 | painter.save() |
||
2874 | painter.setPen(QtGui.QPen(QtGui.QBrush(color), 5, |
||
2875 | Qt.SolidLine, Qt.RoundCap)) |
||
2876 | rect = option.rect.adjusted(3, 0, -3, -5) |
||
2877 | x, y = rect.x(), rect.y() + rect.height() |
||
2878 | painter.drawLine(x, y, x + rect.width() * ratio, y) |
||
2879 | painter.restore() |
||
2880 | text_rect = option.rect.adjusted(0, 0, 0, -3) |
||
2881 | else: |
||
2882 | text_rect = option.rect |
||
2883 | text = index.data(Qt.DisplayRole) |
||
2884 | self.drawDisplay(painter, option, text_rect, text) |
||
2885 | painter.restore() |
||
2886 | |||
2887 | |||
2888 | class BarItemDelegate(QtGui.QStyledItemDelegate): |
||
2889 | def __init__(self, parent, brush=QtGui.QBrush(QtGui.QColor(255, 170, 127)), |
||
2890 | scale=(0.0, 1.0)): |
||
2891 | super().__init__(parent) |
||
2892 | self.brush = brush |
||
2893 | self.scale = scale |
||
2894 | |||
2895 | def paint(self, painter, option, index): |
||
2896 | if option.widget is not None: |
||
2897 | style = option.widget.style() |
||
2898 | else: |
||
2899 | style = QtGui.QApplication.style() |
||
2900 | |||
2901 | style.drawPrimitive( |
||
2902 | QtGui.QStyle.PE_PanelItemViewRow, option, painter, |
||
2903 | option.widget) |
||
2904 | style.drawPrimitive( |
||
2905 | QtGui.QStyle.PE_PanelItemViewItem, option, painter, |
||
2906 | option.widget) |
||
2907 | |||
2908 | rect = option.rect |
||
2909 | val = index.data(Qt.DisplayRole) |
||
2910 | if isinstance(val, float): |
||
2911 | minv, maxv = self.scale |
||
2912 | val = (val - minv) / (maxv - minv) |
||
2913 | painter.save() |
||
2914 | if option.state & QtGui.QStyle.State_Selected: |
||
2915 | painter.setOpacity(0.75) |
||
2916 | painter.setBrush(self.brush) |
||
2917 | painter.drawRect( |
||
2918 | rect.adjusted(1, 1, - rect.width() * (1.0 - val) - 2, -2)) |
||
2919 | painter.restore() |
||
2920 | |||
2921 | |||
2922 | class IndicatorItemDelegate(QtGui.QStyledItemDelegate): |
||
2923 | IndicatorRole = next(OrangeUserRole) |
||
2924 | |||
2925 | def __init__(self, parent, role=IndicatorRole, indicatorSize=2): |
||
2926 | super().__init__(parent) |
||
2927 | self.role = role |
||
2928 | self.indicatorSize = indicatorSize |
||
2929 | |||
2930 | def paint(self, painter, option, index): |
||
2931 | super().paint(painter, option, index) |
||
2932 | rect = option.rect |
||
2933 | indicator = index.data(self.role) |
||
2934 | |||
2935 | if indicator: |
||
2936 | painter.save() |
||
2937 | painter.setRenderHints(QtGui.QPainter.Antialiasing) |
||
2938 | painter.setBrush(QtGui.QBrush(Qt.black)) |
||
2939 | painter.drawEllipse(rect.center(), |
||
2940 | self.indicatorSize, self.indicatorSize) |
||
2941 | painter.restore() |
||
2942 | |||
2943 | |||
2944 | class LinkStyledItemDelegate(QtGui.QStyledItemDelegate): |
||
2945 | LinkRole = next(OrangeUserRole) |
||
2946 | |||
2947 | def __init__(self, parent): |
||
2948 | super().__init__(parent) |
||
2949 | self.mousePressState = QtCore.QModelIndex(), QtCore.QPoint() |
||
2950 | parent.entered.connect(self.onEntered) |
||
2951 | |||
2952 | def sizeHint(self, option, index): |
||
0 ignored issues
–
show
This method could be written as a function/class method.
If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example class Foo:
def some_method(self, x, y):
return x + y;
could be written as class Foo:
@classmethod
def some_method(cls, x, y):
return x + y;
![]() |
|||
2953 | size = super().sizeHint(option, index) |
||
2954 | return QtCore.QSize(size.width(), max(size.height(), 20)) |
||
2955 | |||
2956 | def linkRect(self, option, index): |
||
2957 | if option.widget is not None: |
||
2958 | style = option.widget.style() |
||
2959 | else: |
||
2960 | style = QtGui.QApplication.style() |
||
2961 | |||
2962 | text = self.displayText(index.data(Qt.DisplayRole), |
||
2963 | QtCore.QLocale.system()) |
||
2964 | self.initStyleOption(option, index) |
||
2965 | textRect = style.subElementRect( |
||
2966 | QtGui.QStyle.SE_ItemViewItemText, option, option.widget) |
||
2967 | |||
2968 | if not textRect.isValid(): |
||
2969 | textRect = option.rect |
||
2970 | margin = style.pixelMetric( |
||
2971 | QtGui.QStyle.PM_FocusFrameHMargin, option, option.widget) + 1 |
||
2972 | textRect = textRect.adjusted(margin, 0, -margin, 0) |
||
2973 | font = index.data(Qt.FontRole) |
||
2974 | if not isinstance(font, QtGui.QFont): |
||
2975 | font = option.font |
||
2976 | |||
2977 | metrics = QtGui.QFontMetrics(font) |
||
2978 | elideText = metrics.elidedText(text, option.textElideMode, |
||
2979 | textRect.width()) |
||
2980 | return metrics.boundingRect(textRect, option.displayAlignment, |
||
2981 | elideText) |
||
2982 | |||
2983 | def editorEvent(self, event, model, option, index): |
||
2984 | if event.type() == QtCore.QEvent.MouseButtonPress and \ |
||
2985 | self.linkRect(option, index).contains(event.pos()): |
||
2986 | self.mousePressState = (QtCore.QPersistentModelIndex(index), |
||
2987 | QtCore.QPoint(event.pos())) |
||
2988 | |||
2989 | elif event.type() == QtCore.QEvent.MouseButtonRelease: |
||
2990 | link = index.data(LinkRole) |
||
2991 | if not isinstance(link, str): |
||
2992 | link = None |
||
2993 | |||
2994 | pressedIndex, pressPos = self.mousePressState |
||
2995 | if pressedIndex == index and \ |
||
2996 | (pressPos - event.pos()).manhattanLength() < 5 and \ |
||
2997 | link is not None: |
||
2998 | import webbrowser |
||
2999 | webbrowser.open(link) |
||
3000 | self.mousePressState = QtCore.QModelIndex(), event.pos() |
||
3001 | |||
3002 | elif event.type() == QtCore.QEvent.MouseMove: |
||
3003 | link = index.data(LinkRole) |
||
3004 | if not isinstance(link, str): |
||
3005 | link = None |
||
3006 | |||
3007 | if link is not None and \ |
||
3008 | self.linkRect(option, index).contains(event.pos()): |
||
3009 | self.parent().viewport().setCursor(Qt.PointingHandCursor) |
||
3010 | else: |
||
3011 | self.parent().viewport().setCursor(Qt.ArrowCursor) |
||
3012 | |||
3013 | return super().editorEvent(event, model, option, index) |
||
3014 | |||
3015 | def onEntered(self, index): |
||
3016 | link = index.data(LinkRole) |
||
3017 | if not isinstance(link, str): |
||
3018 | link = None |
||
3019 | if link is None: |
||
3020 | self.parent().viewport().setCursor(Qt.ArrowCursor) |
||
3021 | |||
3022 | def paint(self, painter, option, index): |
||
3023 | link = index.data(LinkRole) |
||
3024 | if not isinstance(link, str): |
||
3025 | link = None |
||
3026 | |||
3027 | if link is not None: |
||
3028 | if option.widget is not None: |
||
3029 | style = option.widget.style() |
||
3030 | else: |
||
3031 | style = QtGui.QApplication.style() |
||
3032 | style.drawPrimitive( |
||
3033 | QtGui.QStyle.PE_PanelItemViewRow, option, painter, |
||
3034 | option.widget) |
||
3035 | style.drawPrimitive( |
||
3036 | QtGui.QStyle.PE_PanelItemViewItem, option, painter, |
||
3037 | option.widget) |
||
3038 | |||
3039 | text = self.displayText(index.data(Qt.DisplayRole), |
||
3040 | QtCore.QLocale.system()) |
||
3041 | textRect = style.subElementRect( |
||
3042 | QtGui.QStyle.SE_ItemViewItemText, option, option.widget) |
||
3043 | if not textRect.isValid(): |
||
3044 | textRect = option.rect |
||
3045 | margin = style.pixelMetric( |
||
3046 | QtGui.QStyle.PM_FocusFrameHMargin, option, option.widget) + 1 |
||
3047 | textRect = textRect.adjusted(margin, 0, -margin, 0) |
||
3048 | elideText = QtGui.QFontMetrics(option.font).elidedText( |
||
3049 | text, option.textElideMode, textRect.width()) |
||
3050 | painter.save() |
||
3051 | font = index.data(Qt.FontRole) |
||
3052 | if not isinstance(font, QtGui.QFont): |
||
3053 | font = option.font |
||
3054 | painter.setFont(font) |
||
3055 | painter.setPen(QtGui.QPen(Qt.blue)) |
||
3056 | painter.drawText(textRect, option.displayAlignment, elideText) |
||
3057 | painter.restore() |
||
3058 | else: |
||
3059 | super().paint(painter, option, index) |
||
3060 | |||
3061 | |||
3062 | LinkRole = LinkStyledItemDelegate.LinkRole |
||
3063 | |||
3064 | |||
3065 | class ColoredBarItemDelegate(QtGui.QStyledItemDelegate): |
||
3066 | """ Item delegate that can also draws a distribution bar |
||
3067 | """ |
||
3068 | def __init__(self, parent=None, decimals=3, color=Qt.red): |
||
3069 | super().__init__(parent) |
||
3070 | self.decimals = decimals |
||
3071 | self.float_fmt = "%%.%if" % decimals |
||
3072 | self.color = QtGui.QColor(color) |
||
3073 | |||
3074 | def displayText(self, value, locale): |
||
0 ignored issues
–
show
|
|||
3075 | if isinstance(value, float): |
||
3076 | return self.float_fmt % value |
||
3077 | elif isinstance(value, str): |
||
3078 | return value |
||
3079 | elif value is None: |
||
3080 | return "NA" |
||
3081 | else: |
||
3082 | return str(value) |
||
3083 | |||
3084 | def sizeHint(self, option, index): |
||
3085 | font = self.get_font(option, index) |
||
3086 | metrics = QtGui.QFontMetrics(font) |
||
3087 | height = metrics.lineSpacing() + 8 # 4 pixel margin |
||
3088 | width = metrics.width(self.displayText(index.data(Qt.DisplayRole), |
||
3089 | QtCore.QLocale())) + 8 |
||
3090 | return QtCore.QSize(width, height) |
||
3091 | |||
3092 | def paint(self, painter, option, index): |
||
3093 | self.initStyleOption(option, index) |
||
3094 | text = self.displayText(index.data(Qt.DisplayRole), QtCore.QLocale()) |
||
3095 | ratio, have_ratio = self.get_bar_ratio(option, index) |
||
3096 | |||
3097 | rect = option.rect |
||
3098 | if have_ratio: |
||
3099 | # The text is raised 3 pixels above the bar. |
||
3100 | # TODO: Style dependent margins? |
||
0 ignored issues
–
show
|
|||
3101 | text_rect = rect.adjusted(4, 1, -4, -4) |
||
3102 | else: |
||
3103 | text_rect = rect.adjusted(4, 4, -4, -4) |
||
3104 | |||
3105 | painter.save() |
||
3106 | font = self.get_font(option, index) |
||
3107 | painter.setFont(font) |
||
3108 | |||
3109 | if option.widget is not None: |
||
3110 | style = option.widget.style() |
||
3111 | else: |
||
3112 | style = QtGui.QApplication.style() |
||
3113 | |||
3114 | style.drawPrimitive( |
||
3115 | QtGui.QStyle.PE_PanelItemViewRow, option, painter, |
||
3116 | option.widget) |
||
3117 | style.drawPrimitive( |
||
3118 | QtGui.QStyle.PE_PanelItemViewItem, option, painter, |
||
3119 | option.widget) |
||
3120 | |||
3121 | # TODO: Check ForegroundRole. |
||
0 ignored issues
–
show
|
|||
3122 | if option.state & QtGui.QStyle.State_Selected: |
||
3123 | color = option.palette.highlightedText().color() |
||
3124 | else: |
||
3125 | color = option.palette.text().color() |
||
3126 | painter.setPen(QtGui.QPen(color)) |
||
3127 | |||
3128 | align = self.get_text_align(option, index) |
||
3129 | |||
3130 | metrics = QtGui.QFontMetrics(font) |
||
3131 | elide_text = metrics.elidedText( |
||
3132 | text, option.textElideMode, text_rect.width()) |
||
3133 | painter.drawText(text_rect, align, elide_text) |
||
3134 | |||
3135 | painter.setRenderHint(QtGui.QPainter.Antialiasing, True) |
||
3136 | if have_ratio: |
||
3137 | brush = self.get_bar_brush(option, index) |
||
3138 | |||
3139 | painter.setBrush(brush) |
||
3140 | painter.setPen(QtGui.QPen(brush, 1)) |
||
3141 | bar_rect = QtCore.QRect(text_rect) |
||
3142 | bar_rect.setTop(bar_rect.bottom() - 1) |
||
3143 | bar_rect.setBottom(bar_rect.bottom() + 1) |
||
3144 | w = text_rect.width() |
||
3145 | bar_rect.setWidth(max(0, min(w * ratio, w))) |
||
3146 | painter.drawRoundedRect(bar_rect, 2, 2) |
||
3147 | painter.restore() |
||
3148 | |||
3149 | def get_font(self, option, index): |
||
0 ignored issues
–
show
This method could be written as a function/class method.
If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example class Foo:
def some_method(self, x, y):
return x + y;
could be written as class Foo:
@classmethod
def some_method(cls, x, y):
return x + y;
![]() |
|||
3150 | font = index.data(Qt.FontRole) |
||
3151 | if not isinstance(font, QtGui.QFont): |
||
3152 | font = option.font |
||
3153 | return font |
||
3154 | |||
3155 | def get_text_align(self, _, index): |
||
0 ignored issues
–
show
This method could be written as a function/class method.
If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example class Foo:
def some_method(self, x, y):
return x + y;
could be written as class Foo:
@classmethod
def some_method(cls, x, y):
return x + y;
![]() |
|||
3156 | align = index.data(Qt.TextAlignmentRole) |
||
3157 | if not isinstance(align, int): |
||
3158 | align = Qt.AlignLeft | Qt.AlignVCenter |
||
3159 | |||
3160 | return align |
||
3161 | |||
3162 | def get_bar_ratio(self, _, index): |
||
0 ignored issues
–
show
This method could be written as a function/class method.
If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example class Foo:
def some_method(self, x, y):
return x + y;
could be written as class Foo:
@classmethod
def some_method(cls, x, y):
return x + y;
![]() |
|||
3163 | ratio = index.data(BarRatioRole) |
||
3164 | return ratio, isinstance(ratio, float) |
||
3165 | |||
3166 | def get_bar_brush(self, _, index): |
||
3167 | bar_brush = index.data(BarBrushRole) |
||
3168 | if not isinstance(bar_brush, (QtGui.QColor, QtGui.QBrush)): |
||
3169 | bar_brush = self.color |
||
3170 | return QtGui.QBrush(bar_brush) |
||
3171 | |||
3172 | ############################################################################## |
||
3173 | # progress bar management |
||
3174 | |||
3175 | |||
3176 | class ProgressBar: |
||
3177 | def __init__(self, widget, iterations): |
||
3178 | self.iter = iterations |
||
3179 | self.widget = widget |
||
3180 | self.count = 0 |
||
3181 | self.widget.progressBarInit() |
||
3182 | |||
3183 | def advance(self, count=1): |
||
3184 | self.count += count |
||
3185 | self.widget.progressBarSet(int(self.count * 100 / max(1, self.iter))) |
||
3186 | |||
3187 | def finish(self): |
||
3188 | self.widget.progressBarFinished() |
||
3189 | |||
3190 | |||
3191 | |||
3192 | ############################################################################## |
||
3193 | |||
3194 | def tabWidget(widget): |
||
3195 | w = QtGui.QTabWidget(widget) |
||
3196 | if widget.layout() is not None: |
||
3197 | widget.layout().addWidget(w) |
||
3198 | return w |
||
3199 | |||
3200 | |||
3201 | def createTabPage(tabWidget, name, widgetToAdd=None, canScroll=False): |
||
0 ignored issues
–
show
tabWidget is re-defining a name which is already available in the outer-scope (previously defined on line 3194 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
3202 | if widgetToAdd is None: |
||
3203 | widgetToAdd = widgetBox(tabWidget, addToLayout=0, margin=4) |
||
3204 | if canScroll: |
||
3205 | scrollArea = QtGui.QScrollArea() |
||
3206 | tabWidget.addTab(scrollArea, name) |
||
3207 | scrollArea.setWidget(widgetToAdd) |
||
3208 | scrollArea.setWidgetResizable(1) |
||
3209 | scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) |
||
3210 | scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) |
||
3211 | else: |
||
3212 | tabWidget.addTab(widgetToAdd, name) |
||
3213 | return widgetToAdd |
||
3214 | |||
3215 | |||
3216 | def table(widget, rows=0, columns=0, selectionMode=-1, addToLayout=True): |
||
3217 | w = QtGui.QTableWidget(rows, columns, widget) |
||
3218 | if widget and addToLayout and widget.layout() is not None: |
||
3219 | widget.layout().addWidget(w) |
||
3220 | if selectionMode != -1: |
||
3221 | w.setSelectionMode(selectionMode) |
||
3222 | w.setHorizontalScrollMode(QtGui.QTableWidget.ScrollPerPixel) |
||
3223 | w.horizontalHeader().setMovable(True) |
||
3224 | return w |
||
3225 | |||
3226 | |||
3227 | class VisibleHeaderSectionContextEventFilter(QtCore.QObject): |
||
3228 | def __init__(self, parent, itemView=None): |
||
3229 | super().__init__(parent) |
||
3230 | self.itemView = itemView |
||
3231 | |||
3232 | def eventFilter(self, view, event): |
||
3233 | if not isinstance(event, QtGui.QContextMenuEvent): |
||
3234 | return False |
||
3235 | |||
3236 | model = view.model() |
||
3237 | headers = [(view.isSectionHidden(i), |
||
3238 | model.headerData(i, view.orientation(), Qt.DisplayRole) |
||
3239 | ) for i in range(view.count())] |
||
3240 | menu = QtGui.QMenu("Visible headers", view) |
||
3241 | |||
3242 | for i, (checked, name) in enumerate(headers): |
||
3243 | action = QtGui.QAction(name, menu) |
||
3244 | action.setCheckable(True) |
||
3245 | action.setChecked(not checked) |
||
3246 | menu.addAction(action) |
||
3247 | |||
3248 | def toogleHidden(b, section=i): |
||
3249 | view.setSectionHidden(section, not b) |
||
3250 | if not b: |
||
3251 | return |
||
3252 | if self.itemView: |
||
3253 | self.itemView.resizeColumnToContents(section) |
||
3254 | else: |
||
3255 | view.resizeSection(section, |
||
3256 | max(view.sectionSizeHint(section), 10)) |
||
3257 | |||
3258 | action.toggled.connect(toogleHidden) |
||
3259 | menu.exec_(event.globalPos()) |
||
3260 | return True |
||
3261 | |||
3262 | |||
3263 | def checkButtonOffsetHint(button, style=None): |
||
0 ignored issues
–
show
button is re-defining a name which is already available in the outer-scope (previously defined on line 1150 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
3264 | option = QtGui.QStyleOptionButton() |
||
3265 | option.initFrom(button) |
||
3266 | if style is None: |
||
3267 | style = button.style() |
||
3268 | if isinstance(button, QtGui.QCheckBox): |
||
3269 | pm_spacing = QtGui.QStyle.PM_CheckBoxLabelSpacing |
||
3270 | pm_indicator_width = QtGui.QStyle.PM_IndicatorWidth |
||
3271 | else: |
||
3272 | pm_spacing = QtGui.QStyle.PM_RadioButtonLabelSpacing |
||
3273 | pm_indicator_width = QtGui.QStyle.PM_ExclusiveIndicatorWidth |
||
3274 | space = style.pixelMetric(pm_spacing, option, button) |
||
3275 | width = style.pixelMetric(pm_indicator_width, option, button) |
||
3276 | # TODO: add other styles (Maybe load corrections from .cfg file?) |
||
0 ignored issues
–
show
|
|||
3277 | style_correction = {"macintosh (aqua)": -2, "macintosh(aqua)": -2, |
||
3278 | "plastique": 1, "cde": 1, "motif": 1} |
||
3279 | return space + width + \ |
||
3280 | style_correction.get(QtGui.qApp.style().objectName().lower(), 0) |
||
3281 | |||
3282 | |||
3283 | def toolButtonSizeHint(button=None, style=None): |
||
0 ignored issues
–
show
button is re-defining a name which is already available in the outer-scope (previously defined on line 1150 ).
It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior: param = 5
class Foo:
def __init__(self, param): # "param" would be flagged here
self.param = param
![]() |
|||
3284 | if button is None and style is None: |
||
3285 | style = QtGui.qApp.style() |
||
3286 | elif style is None: |
||
3287 | style = button.style() |
||
3288 | |||
3289 | button_size = \ |
||
3290 | style.pixelMetric(QtGui.QStyle.PM_SmallIconSize) + \ |
||
3291 | style.pixelMetric(QtGui.QStyle.PM_ButtonMargin) |
||
3292 | return button_size |
||
3293 | |||
3294 | |||
3295 | class FloatSlider(QtGui.QSlider): |
||
3296 | valueChangedFloat = Signal(float) |
||
3297 | |||
3298 | def __init__(self, orientation, min_value, max_value, step, parent=None): |
||
3299 | super().__init__(orientation, parent) |
||
3300 | self.setScale(min_value, max_value, step) |
||
3301 | self.valueChanged[int].connect(self.sendValue) |
||
3302 | |||
3303 | def update(self): |
||
3304 | self.setSingleStep(1) |
||
3305 | if self.min_value != self.max_value: |
||
3306 | self.setEnabled(True) |
||
3307 | self.setMinimum(int(self.min_value / self.step)) |
||
3308 | self.setMaximum(int(self.max_value / self.step)) |
||
3309 | else: |
||
3310 | self.setEnabled(False) |
||
3311 | |||
3312 | def sendValue(self, slider_value): |
||
3313 | value = min(max(slider_value * self.step, self.min_value), |
||
3314 | self.max_value) |
||
3315 | self.valueChangedFloat.emit(value) |
||
3316 | |||
3317 | def setValue(self, value): |
||
3318 | super().setValue(value // self.step) |
||
3319 | |||
3320 | def setScale(self, minValue, maxValue, step=0): |
||
3321 | if minValue >= maxValue: |
||
3322 | ## It would be more logical to disable the slider in this case |
||
3323 | ## (self.setEnabled(False)) |
||
3324 | ## However, we do nothing to keep consistency with Qwt |
||
3325 | # TODO If it's related to Qwt, remove it |
||
0 ignored issues
–
show
|
|||
3326 | return |
||
3327 | if step <= 0 or step > (maxValue - minValue): |
||
3328 | if isinstance(maxValue, int) and isinstance(minValue, int): |
||
3329 | step = 1 |
||
3330 | else: |
||
3331 | step = float(minValue - maxValue) / 100.0 |
||
3332 | self.min_value = float(minValue) |
||
3333 | self.max_value = float(maxValue) |
||
3334 | self.step = step |
||
3335 | self.update() |
||
3336 | |||
3337 | def setRange(self, minValue, maxValue, step=1.0): |
||
3338 | # For compatibility with qwtSlider |
||
3339 | # TODO If it's related to Qwt, remove it |
||
0 ignored issues
–
show
|
|||
3340 | self.setScale(minValue, maxValue, step) |
||
3341 |
This can be caused by one of the following:
1. Missing Dependencies
This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.
2. Missing __init__.py files
This error could also result from missing
__init__.py
files in your module folders. Make sure that you place one file in each sub-folder.