GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Orange.widgets.OWWidget   F
last analyzed

Complexity

Total Complexity 126

Size/Duplication

Total Lines 639
Duplicated Lines 0 %
Metric Value
dl 0
loc 639
rs 1.4558
wmc 126

47 Methods

Rating   Name   Duplication   Size   Complexity  
A storeSpecificSettings() 0 2 1
B __new__() 0 38 4
F setState() 0 56 17
A error() 0 2 1
A updateStatusBarState() 0 7 4
A handleNewSignals() 0 9 1
A setStatusBarText() 0 3 2
A widgetStateToHtml() 0 15 4
A workflowEnv() 0 9 1
A setBlocking() 0 20 2
A __init__() 0 3 1
A showEvent() 0 6 3
A closeEvent() 0 4 3
A __updateSavedGeometry() 0 6 3
A wheelEvent() 0 6 1
B __setattr__() 0 25 5
A isBlocking() 0 5 1
B set_warning_bar() 0 25 4
A progressBarInit() 0 20 2
A resetSettings() 0 2 1
A send() 0 6 4
F insertLayout() 0 76 9
A progressBarValue() 0 2 1
A information() 0 2 1
A workflowEnvChanged() 0 10 1
A statusMessage() 0 2 1
A closeContext() 0 2 1
A getWidgetStateIcons() 0 9 2
A prepareDataReport() 0 2 1
A retrieveSpecificSettings() 0 2 1
A setCaption() 0 4 1
A progressBarAdvance() 0 2 1
A hideEvent() 0 4 2
A resizeEvent() 0 8 3
A setStatusMessage() 0 4 2
A openContext() 0 2 1
A moveEvent() 0 4 3
A reshow() 0 4 1
A keyPressEvent() 0 5 2
A warning() 0 2 1
A onDeleteWidget() 0 9 1
B __restoreWidgetGeometry() 0 27 6
A createPixmapWidget() 0 8 2
A get_flags() 0 4 2
A saveSettings() 0 2 1
A progressBarFinished() 0 19 4
C progressBarSet() 0 44 7

How to fix   Complexity   

Complex Class

Complex classes like Orange.widgets.OWWidget often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
2
import sys
3
import time
4
import os
5
import warnings
6
import types
7
from functools import reduce
8
9
from PyQt4.QtCore import QByteArray, Qt, pyqtSignal as Signal, pyqtProperty,\
0 ignored issues
show
Configuration introduced by
The import PyQt4.QtCore could not be resolved.

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.

# .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 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.

Loading history...
10
    QEventLoop
11
from PyQt4.QtGui import QDialog, QPixmap, QLabel, QVBoxLayout, QSizePolicy, \
0 ignored issues
show
Configuration introduced by
The import PyQt4.QtGui could not be resolved.

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.

# .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 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.

Loading history...
12
    qApp, QFrame, QStatusBar, QHBoxLayout, QStyle, QApplication
13
14
from Orange.widgets import settings, gui
15
from Orange.canvas.registry import description as widget_description
16
17
from Orange.widgets.gui import ControlledAttributesDict, notify_changed
18
from Orange.widgets.settings import SettingsHandler
19
from Orange.widgets.utils import vartype
0 ignored issues
show
Unused Code introduced by
Unused vartype imported from Orange.widgets.utils
Loading history...
20
21
22
def _asmappingproxy(mapping):
23
    if isinstance(mapping, types.MappingProxyType):
24
        return mapping
25
    else:
26
        return types.MappingProxyType(mapping)
27
28
29
class WidgetMetaClass(type(QDialog)):
30
    """Meta class for widgets. If the class definition does not have a
31
       specific settings handler, the meta class provides a default one
32
       that does not handle contexts. Then it scans for any attributes
33
       of class settings.Setting: the setting is stored in the handler and
34
       the value of the attribute is replaced with the default."""
35
36
    #noinspection PyMethodParameters
37
    def __new__(mcs, name, bases, dict):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in dict.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Coding Style Best Practice introduced by
The first argument of the class method __new__ should be named cls.
Loading history...
38
        from Orange.canvas.registry.description import (
39
            input_channel_from_args, output_channel_from_args)
40
41
        cls = type.__new__(mcs, name, bases, dict)
42
        if not cls.name: # not a widget
43
            return cls
44
45
        cls.inputs = [input_channel_from_args(inp) for inp in cls.inputs]
46
        cls.outputs = [output_channel_from_args(outp) for outp in cls.outputs]
47
48
        for inp in cls.inputs:
49
            if not hasattr(cls, inp.handler):
50
                raise AttributeError("missing input signal handler '{}' in {}".
51
                                     format(inp.handler, cls.name))
52
53
        # TODO Remove this when all widgets are migrated to Orange 3.0
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
54
        if (hasattr(cls, "settingsToWidgetCallback") or
55
                hasattr(cls, "settingsFromWidgetCallback")):
56
            raise TypeError("Reimplement settingsToWidgetCallback and "
57
                            "settingsFromWidgetCallback")
58
59
        cls.settingsHandler = SettingsHandler.create(cls, template=cls.settingsHandler)
60
61
        return cls
62
63
64
class OWWidget(QDialog, metaclass=WidgetMetaClass):
65
    # Global widget count
66
    widget_id = 0
67
68
    # Widget description
69
    name = None
70
    id = None
71
    category = None
72
    version = None
73
    description = None
74
    long_description = None
75
    icon = "icons/Unknown.png"
76
    priority = sys.maxsize
77
    author = None
78
    author_email = None
79
    maintainer = None
80
    maintainer_email = None
81
    help = None
82
    help_ref = None
83
    url = None
84
    keywords = []
85
    background = None
86
    replaces = None
87
    inputs = []
88
    outputs = []
89
90
    # Default widget layout settings
91
    want_basic_layout = True
92
    want_main_area = True
93
    want_control_area = True
94
    want_graph = False
95
    show_save_graph = True
96
    want_status_bar = False
97
    no_report = False
98
99
    save_position = True
100
    resizing_enabled = True
101
102
    widgetStateChanged = Signal(str, int, str)
103
    blockingStateChanged = Signal(bool)
104
    progressBarValueChanged = Signal(float)
105
    processingStateChanged = Signal(int)
106
107
    settingsHandler = None
108
    """:type: SettingsHandler"""
109
110
    savedWidgetGeometry = settings.Setting(None)
111
112
    def __new__(cls, parent=None, *args, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument args seems to be unused.
Loading history...
Unused Code introduced by
The argument parent seems to be unused.
Loading history...
113
        self = super().__new__(cls, None, cls.get_flags())
114
        QDialog.__init__(self, None, self.get_flags())
115
116
        stored_settings = kwargs.get('stored_settings', None)
117
        if self.settingsHandler:
118
            self.settingsHandler.initialize(self, stored_settings)
119
120
        self.signalManager = kwargs.get('signal_manager', None)
121
        self.__env = _asmappingproxy(kwargs.get("env", {}))
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __env was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
122
123
        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
124
        self.__reportData = None
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __reportData was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
125
126
        OWWidget.widget_id += 1
127
        self.widget_id = OWWidget.widget_id
128
129
        if self.name:
130
            self.setCaption(self.name)
131
132
        self.setFocusPolicy(Qt.StrongFocus)
133
134
        self.startTime = time.time()    # used in progressbar
135
136
        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}
137
138
        self.__blocking = False
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __blocking was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
139
        # flag indicating if the widget's position was already restored
140
        self.__was_restored = False
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __was_restored was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
141
142
        self.__progressBarValue = -1
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __progressBarValue was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
143
        self.__progressState = 0
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __progressState was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
144
        self.__statusMessage = ""
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __statusMessage was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
145
146
        if self.want_basic_layout:
147
            self.insertLayout()
148
149
        return self
150
151
    def __init__(self, *args, **kwargs):
152
        """QDialog __init__ was already called in __new__,
153
        please do not call it here."""
154
155
    @classmethod
156
    def get_flags(cls):
157
        return (Qt.Window if cls.resizing_enabled
158
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)
159
160
    # noinspection PyAttributeOutsideInit
161
    def insertLayout(self):
162
        def createPixmapWidget(self, parent, iconName):
0 ignored issues
show
Unused Code introduced by
The argument self seems to be unused.
Loading history...
163
            w = QLabel(parent)
164
            parent.layout().addWidget(w)
165
            w.setFixedSize(16, 16)
166
            w.hide()
167
            if os.path.exists(iconName):
168
                w.setPixmap(QPixmap(iconName))
169
            return w
170
171
        self.setLayout(QVBoxLayout())
172
        self.layout().setMargin(2)
173
174
        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
175
                                         margin=0, spacing=0)
176
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
177
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
178
        self.warning_label.setStyleSheet("padding-top: 5px")
179
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
180
        gui.rubber(self.warning_bar)
181
        self.warning_bar.setVisible(False)
182
183
        self.topWidgetPart = gui.widgetBox(self,
184
                                           orientation="horizontal", margin=0)
185
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
186
                                            orientation="vertical", margin=0)
187
        if self.want_main_area:
188
            self.leftWidgetPart.setSizePolicy(
189
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
190
            self.leftWidgetPart.updateGeometry()
191
            self.mainArea = gui.widgetBox(self.topWidgetPart,
192
                                          orientation="vertical",
193
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
194
                                                                 QSizePolicy.Expanding),
195
                                          margin=0)
196
            self.mainArea.layout().setMargin(4)
197
            self.mainArea.updateGeometry()
198
199
        if self.want_control_area:
200
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
201
                                             orientation="vertical", margin=4)
202
203
        if self.want_graph and self.show_save_graph:
204
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
205
                                                  orientation="horizontal", margin=4)
206
            self.graphButton = gui.button(graphButtonBackground,
207
                                          self, "&Save Graph")
208
            self.graphButton.setAutoDefault(0)
209
210
        if self.want_status_bar:
211
            self.widgetStatusArea = QFrame(self)
212
            self.statusBarIconArea = QFrame(self)
213
            self.widgetStatusBar = QStatusBar(self)
214
215
            self.layout().addWidget(self.widgetStatusArea)
216
217
            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
218
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
219
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
220
            self.widgetStatusArea.layout().setMargin(0)
221
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)
222
223
            self.statusBarIconArea.setLayout(QHBoxLayout())
224
            self.widgetStatusBar.setSizeGripEnabled(0)
225
226
            self.statusBarIconArea.hide()
227
228
            self._warningWidget = createPixmapWidget(
229
                self.statusBarIconArea,
230
                gui.resource_filename("icons/triangle-orange.png"))
231
            self._errorWidget = createPixmapWidget(
232
                self.statusBarIconArea,
233
                gui.resource_filename("icons/triangle-red.png"))
234
235
        if not self.resizing_enabled:
236
            self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)
237
238
    def updateStatusBarState(self):
239
        if not hasattr(self, "widgetStatusArea"):
240
            return
241
        if self.widgetState["Warning"] or self.widgetState["Error"]:
242
            self.widgetStatusArea.show()
243
        else:
244
            self.widgetStatusArea.hide()
245
246
    def setStatusBarText(self, text, timeout=5000):
247
        if hasattr(self, "widgetStatusBar"):
248
            self.widgetStatusBar.showMessage(" " + text, timeout)
249
250
    # TODO add!
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
251
    def prepareDataReport(self, data):
252
        pass
253
254
    def __restoreWidgetGeometry(self):
255
        restored = False
256
        if self.save_position:
257
            geometry = self.savedWidgetGeometry
258
            if geometry is not None:
259
                restored = self.restoreGeometry(QByteArray(geometry))
260
261
            if restored and not self.windowState() & \
262
                    (Qt.WindowMaximized | Qt.WindowFullScreen):
263
                space = qApp.desktop().availableGeometry(self)
264
                frame, geometry = self.frameGeometry(), self.geometry()
265
266
                #Fix the widget size to fit inside the available space
267
                width = space.width() - (frame.width() - geometry.width())
268
                width = min(width, geometry.width())
269
                height = space.height() - (frame.height() - geometry.height())
270
                height = min(height, geometry.height())
271
                self.resize(width, height)
272
273
                # Move the widget to the center of available space if it is
274
                # currently outside it
275
                if not space.contains(self.frameGeometry()):
276
                    x = max(0, space.width() / 2 - width / 2)
277
                    y = max(0, space.height() / 2 - height / 2)
278
279
                    self.move(x, y)
280
        return restored
281
282
    def __updateSavedGeometry(self):
283
        if self.__was_restored and self.isVisible():
284
            # Update the saved geometry only between explicit show/hide
285
            # events (i.e. changes initiated by the user not by Qt's default
286
            # window management).
287
            self.savedWidgetGeometry = self.saveGeometry()
288
289
    # when widget is resized, save the new width and height
290
    def resizeEvent(self, ev):
291
        QDialog.resizeEvent(self, ev)
292
        # Don't store geometry if the widget is not visible
293
        # (the widget receives a resizeEvent (with the default sizeHint)
294
        # before first showEvent and we must not overwrite the the
295
        # savedGeometry with it)
296
        if self.save_position and self.isVisible():
297
            self.__updateSavedGeometry()
298
299
    def moveEvent(self, ev):
300
        QDialog.moveEvent(self, ev)
301
        if self.save_position and self.isVisible():
302
            self.__updateSavedGeometry()
303
304
    # set widget state to hidden
305
    def hideEvent(self, ev):
306
        if self.save_position:
307
            self.__updateSavedGeometry()
308
        QDialog.hideEvent(self, ev)
309
310
    def closeEvent(self, ev):
311
        if self.save_position and self.isVisible():
312
            self.__updateSavedGeometry()
313
        QDialog.closeEvent(self, ev)
314
315
    def showEvent(self, ev):
316
        QDialog.showEvent(self, ev)
317
        if self.save_position and not self.__was_restored:
318
            # Restore saved geometry on show
319
            self.__restoreWidgetGeometry()
320
            self.__was_restored = True
321
322
    def wheelEvent(self, event):
0 ignored issues
show
Coding Style introduced by
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;
Loading history...
323
        """ Silently accept the wheel event. This is to ensure combo boxes
324
        and other controls that have focus don't receive this event unless
325
        the cursor is over them.
326
        """
327
        event.accept()
328
329
    def setCaption(self, caption):
330
        # we have to save caption title in case progressbar will change it
331
        self.captionTitle = str(caption)
332
        self.setWindowTitle(caption)
333
334
    # put this widget on top of all windows
335
    def reshow(self):
336
        self.show()
337
        self.raise_()
338
        self.activateWindow()
339
340
    def send(self, signalName, value, id=None):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
341
        if not any(s.name == signalName for s in self.outputs):
342
            raise ValueError('{} is not a valid output signal for widget {}'.format(
343
                signalName, self.name))
344
        if self.signalManager is not None:
345
            self.signalManager.send(self, signalName, value, id)
346
347
    def __setattr__(self, name, value):
348
        """Set value to members of this instance or any of its members.
349
350
        If member is used in a gui control, notify the control about the change.
351
352
        name: name of the member, dot is used for nesting ("graph.point.size").
353
        value: value to set to the member.
354
355
        """
356
357
        names = name.rsplit(".")
358
        field_name = names.pop()
359
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
360
        if obj is None:
361
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))
362
363
        if obj is self:
364
            super().__setattr__(field_name, value)
365
        else:
366
            setattr(obj, field_name, value)
367
368
        notify_changed(obj, field_name, value)
369
370
        if self.settingsHandler:
371
            self.settingsHandler.fast_save(self, name, value)
372
373
    def openContext(self, *a):
374
        self.settingsHandler.open_context(self, *a)
375
376
    def closeContext(self):
377
        self.settingsHandler.close_context(self)
378
379
    def retrieveSpecificSettings(self):
380
        pass
381
382
    def storeSpecificSettings(self):
383
        pass
384
385
    def saveSettings(self):
386
        self.settingsHandler.update_defaults(self)
387
388
    def onDeleteWidget(self):
389
        """
390
        Invoked by the canvas to notify the widget it has been deleted
391
        from the workflow.
392
393
        If possible, subclasses should gracefully cancel any currently
394
        executing tasks.
395
        """
396
        pass
397
398
    def handleNewSignals(self):
399
        """
400
        Invoked by the workflow signal propagation manager after all
401
        signals handlers have been called.
402
403
        Reimplement this method in order to coalesce updates from
404
        multiple updated inputs.
405
        """
406
        pass
407
408
    # ############################################
409
    # PROGRESS BAR FUNCTIONS
410
411
    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
412
        """
413
        Initialize the widget's progress (i.e show and set progress to 0%).
414
415
        .. note::
416
            This method will by default call `QApplication.processEvents`
417
            with `processEvents`. To suppress this behavior pass
418
            ``processEvents=None``.
419
420
        :param processEvents: Process events flag
421
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
422
        """
423
        self.startTime = time.time()
424
        self.setWindowTitle(self.captionTitle + " (0% complete)")
425
426
        if self.__progressState != 1:
427
            self.__progressState = 1
428
            self.processingStateChanged.emit(1)
429
430
        self.progressBarSet(0, processEvents)
431
432
    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
433
        """
434
        Set the current progress bar to `value`.
435
436
        .. note::
437
            This method will by default call `QApplication.processEvents`
438
            with `processEvents`. To suppress this behavior pass
439
            ``processEvents=None``.
440
441
        :param float value: Progress value
442
        :param processEvents: Process events flag
443
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
444
        """
445
        old = self.__progressBarValue
446
        self.__progressBarValue = value
447
448
        if value > 0:
449
            if self.__progressState != 1:
450
                warnings.warn("progressBarSet() called without a "
451
                              "preceding progressBarInit()",
452
                              stacklevel=2)
453
                self.__progressState = 1
454
                self.processingStateChanged.emit(1)
455
456
            usedTime = max(1, time.time() - self.startTime)
457
            totalTime = (100.0 * usedTime) / float(value)
458
            remainingTime = max(0, totalTime - usedTime)
459
            h = int(remainingTime / 3600)
460
            min = int((remainingTime - h * 3600) / 60)
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in min.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
461
            sec = int(remainingTime - h * 3600 - min * 60)
0 ignored issues
show
Unused Code introduced by
The variable sec seems to be unused.
Loading history...
462
            if h > 0:
463
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
0 ignored issues
show
Unused Code introduced by
The variable text seems to be unused.
Loading history...
464
            else:
465
                text = "%(min)d:%(sec)02d" % vars()
466
            self.setWindowTitle(self.captionTitle +
467
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
468
        else:
469
            self.setWindowTitle(self.captionTitle + " (0% complete)")
470
471
        if old != value:
472
            self.progressBarValueChanged.emit(value)
473
474
        if processEvents is not None and processEvents is not False:
475
            qApp.processEvents(processEvents)
476
477
    def progressBarValue(self):
478
        return self.__progressBarValue
479
480
    progressBarValue = pyqtProperty(float, fset=progressBarSet,
481
                                    fget=progressBarValue)
482
483
    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like __progressState was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

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
Loading history...
484
485
    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
486
        self.progressBarSet(self.progressBarValue + value, processEvents)
487
488
    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
489
        """
490
        Stop the widget's progress (i.e hide the progress bar).
491
492
        .. note::
493
            This method will by default call `QApplication.processEvents`
494
            with `processEvents`. To suppress this behavior pass
495
            ``processEvents=None``.
496
497
        :param processEvents: Process events flag
498
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
499
        """
500
        self.setWindowTitle(self.captionTitle)
501
        if self.__progressState != 0:
502
            self.__progressState = 0
503
            self.processingStateChanged.emit(0)
504
505
        if processEvents is not None and processEvents is not False:
506
            qApp.processEvents(processEvents)
507
508
    #: Widget's status message has changed.
509
    statusMessageChanged = Signal(str)
510
511
    def setStatusMessage(self, text):
512
        if self.__statusMessage != text:
513
            self.__statusMessage = text
514
            self.statusMessageChanged.emit(text)
515
516
    def statusMessage(self):
517
        return self.__statusMessage
518
519
    def keyPressEvent(self, e):
520
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
521
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
522
        else:
523
            QDialog.keyPressEvent(self, e)
524
525
    def information(self, id=0, text=None):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
526
        self.setState("Info", id, text)
527
528
    def warning(self, id=0, text=""):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
529
        self.setState("Warning", id, text)
530
531
    def error(self, id=0, text=""):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
532
        self.setState("Error", id, text)
533
534
    def setState(self, state_type, id, text):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
535
        changed = 0
536
        if type(id) == list:
537
            for val in id:
538
                if val in self.widgetState[state_type]:
539
                    self.widgetState[state_type].pop(val)
540
                    changed = 1
541
        else:
542
            if type(id) == str:
543
                text = id
544
                id = 0
545
            if not text:
546
                if id in self.widgetState[state_type]:
547
                    self.widgetState[state_type].pop(id)
548
                    changed = 1
549
            else:
550
                self.widgetState[state_type][id] = text
551
                changed = 1
552
553
        if changed:
554
            if type(id) == list:
555
                for i in id:
556
                    self.widgetStateChanged.emit(state_type, i, "")
557
            else:
558
                self.widgetStateChanged.emit(state_type, id, text or "")
559
560
        tooltip_lines = []
561
        highest_type = None
562
        for a_type in ("Error", "Warning", "Info"):
563
            msgs_for_ids = self.widgetState.get(a_type)
564
            if not msgs_for_ids:
565
                continue
566
            msgs_for_ids = list(msgs_for_ids.values())
567
            if not msgs_for_ids:
568
                continue
569
            tooltip_lines += msgs_for_ids
570
            if highest_type is None:
571
                highest_type = a_type
572
573
        if highest_type is None:
574
            self.set_warning_bar(None)
575
        elif len(tooltip_lines) == 1:
576
            msg = tooltip_lines[0]
577
            if "\n" in msg:
578
                self.set_warning_bar(
579
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
580
            else:
581
                self.set_warning_bar(
582
                    highest_type, tooltip_lines[0], tooltip_lines[0])
583
        else:
584
            self.set_warning_bar(
585
                highest_type,
586
                "{} problems during execution".format(len(tooltip_lines)),
587
                "\n".join(tooltip_lines))
588
589
        return changed
590
591
    def set_warning_bar(self, state_type, text=None, tooltip=None):
592
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
593
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
594
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
595
        current_height = self.height()
596
        if state_type is None:
597
            if not self.warning_bar.isHidden():
598
                new_height = current_height - self.warning_bar.height()
599
                self.warning_bar.setVisible(False)
600
                self.resize(self.width(), new_height)
601
            return
602
        background, foreground, icon = colors[state_type]
603
        style = QApplication.instance().style()
604
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))
605
606
        self.warning_bar.setStyleSheet(
607
            "background-color: {}; color: {};"
608
            "padding: 3px; padding-left: 6px; vertical-align: center".
609
            format(background, foreground))
610
        self.warning_label.setText(text)
611
        self.warning_bar.setToolTip(tooltip)
612
        if self.warning_bar.isHidden():
613
            self.warning_bar.setVisible(True)
614
            new_height = current_height + self.warning_bar.height()
615
            self.resize(self.width(), new_height)
616
617
    def widgetStateToHtml(self, info=True, warning=True, error=True):
618
        iconpaths = {
619
            "Info": gui.resource_filename("icons/information.png"),
620
            "Warning": gui.resource_filename("icons/warning.png"),
621
            "Error": gui.resource_filename("icons/error.png")
622
        }
623
        items = []
624
625
        for show, what in [(info, "Info"), (warning, "Warning"),
626
                           (error, "Error")]:
627
            if show and self.widgetState[what]:
628
                items.append('<img src="%s" style="float: left;"> %s' %
629
                             (iconpaths[what],
630
                              "\n".join(self.widgetState[what].values())))
631
        return "<br>".join(items)
632
633
    @classmethod
634
    def getWidgetStateIcons(cls):
635
        if not hasattr(cls, "_cached__widget_state_icons"):
636
            info = QPixmap(gui.resource_filename("icons/information.png"))
637
            warning = QPixmap(gui.resource_filename("icons/warning.png"))
638
            error = QPixmap(gui.resource_filename("icons/error.png"))
639
            cls._cached__widget_state_icons = \
640
                {"Info": info, "Warning": warning, "Error": error}
641
        return cls._cached__widget_state_icons
642
643
    defaultKeyActions = {}
644
645
    if sys.platform == "darwin":
646
        defaultKeyActions = {
647
            (Qt.ControlModifier, Qt.Key_M):
648
                lambda self: self.showMaximized
649
                if self.isMinimized() else self.showMinimized(),
650
            (Qt.ControlModifier, Qt.Key_W):
651
                lambda self: self.setVisible(not self.isVisible())}
652
653
    def setBlocking(self, state=True):
654
        """
655
        Set blocking flag for this widget.
656
657
        While this flag is set this widget and all its descendants
658
        will not receive any new signals from the workflow signal manager.
659
660
        This is useful for instance if the widget does it's work in a
661
        separate thread or schedules processing from the event queue.
662
        In this case it can set the blocking flag in it's processNewSignals
663
        method schedule the task and return immediately. After the task
664
        has completed the widget can clear the flag and send the updated
665
        outputs.
666
667
        .. note::
668
            Failure to clear this flag will block dependent nodes forever.
669
        """
670
        if self.__blocking != state:
671
            self.__blocking = state
672
            self.blockingStateChanged.emit(state)
673
674
    def isBlocking(self):
675
        """
676
        Is this widget blocking signal processing.
677
        """
678
        return self.__blocking
679
680
    def resetSettings(self):
681
        self.settingsHandler.reset_settings(self)
682
683
    def workflowEnv(self):
684
        """
685
        Return (a view to) the workflow runtime environment.
686
687
        Returns
688
        -------
689
        env : types.MappingProxyType
690
        """
691
        return self.__env
692
693
    def workflowEnvChanged(self, key, value, oldvalue):
694
        """
695
        A workflow environment variable `key` has changed to value.
696
697
        Called by the canvas framework to notify widget of a change
698
        in the workflow runtime environment.
699
700
        The default implementation does nothing.
701
        """
702
        pass
703
704
705
# Pull signal constants from canvas to widget namespace
706
Default = widget_description.Default
707
NonDefault = widget_description.NonDefault
708
Single = widget_description.Single
709
Multiple = widget_description.Multiple
710
Explicit = widget_description.Explicit
711
Dynamic = widget_description.Dynamic
712
713
InputSignal = widget_description.InputSignal
714
OutputSignal = widget_description.OutputSignal
715
716
717
class AttributeList(list):
718
    pass
719
720
721
class ExampleList(list):
722
    pass
723