source: orange/Orange/OrangeWidgets/OWBaseWidget.py @ 10018:97245b43c610

Revision 10018:97245b43c610, 46.9 KB checked in by ales_erjavec, 2 years ago (diff)

Orange.misc.debuging import rename to orngDebugging.

Line 
1#
2# OWWidget.py
3# Orange Widget
4# A General Orange Widget, from which all the Orange Widgets are derived
5#
6from Orange.misc import environ
7from Orange.orng.orngEnviron import directoryNames as old_directory_names
8from PyQt4.QtCore import *
9from PyQt4.QtGui import *
10
11# Define  pyqtConfigure not available in PyQt4 versions prior to 4.6
12if not hasattr(QObject, "pyqtConfigure"):
13    def pyqtConfigure(obj, **kwargs):
14        meta = obj.metaObject()
15        for name, val in kwargs.items():
16            if meta.indexOfProperty(name) >= 0:
17                obj.setProperty(name, QVariant(val))
18            elif meta.indexOfSignal(meta.normalizedSignature(name)) >= 0:
19                obj.connect(obj, SIGNAL(name), val)
20    QObject.pyqtConfigure = pyqtConfigure
21
22from OWContexts import *
23import sys, time, random, user, os, os.path, cPickle, copy
24import orange
25from Orange import misc
26from Orange.misc import debugging as orngDebugging
27from string import *
28from orngSignalManager import *
29import OWGUI
30
31ERROR = 0
32WARNING = 1
33
34TRUE=1
35FALSE=0
36
37def unisetattr(self, name, value, grandparent):
38    if "." in name:
39        names = name.split(".")
40        lastname = names.pop()
41        obj = reduce(lambda o, n: getattr(o, n, None),  names, self)
42    else:
43        lastname, obj = name, self
44
45    if not obj:
46        print "unable to set setting ", name, " to value ", value
47    else:
48        if hasattr(grandparent, "__setattr__") and isinstance(obj, grandparent):
49            grandparent.__setattr__(obj, lastname,  value)
50        else:
51            setattr(obj, lastname, value)
52#            obj.__dict__[lastname] = value
53
54    controlledAttributes = hasattr(self, "controlledAttributes") and getattr(self, "controlledAttributes", None)
55    controlCallback = controlledAttributes and controlledAttributes.get(name, None)
56    if controlCallback:
57        for callback in controlCallback:
58            callback(value)
59#        controlCallback(value)
60
61    # controlled things (checkboxes...) never have __attributeControllers
62    else:
63        if hasattr(self, "__attributeControllers"):
64            for controller, myself in self.__attributeControllers.keys():
65                if getattr(controller, myself, None) != self:
66                    del self.__attributeControllers[(controller, myself)]
67                    continue
68
69                controlledAttributes = hasattr(controller, "controlledAttributes") and getattr(controller, "controlledAttributes", None)
70                if controlledAttributes:
71                    fullName = myself + "." + name
72
73                    controlCallback = controlledAttributes.get(fullName, None)
74                    if controlCallback:
75                        for callback in controlCallback:
76                            callback(value)
77
78                    else:
79                        lname = fullName + "."
80                        dlen = len(lname)
81                        for controlled in controlledAttributes.keys():
82                            if controlled[:dlen] == lname:
83                                self.setControllers(value, controlled[dlen:], controller, fullName)
84                                # no break -- can have a.b.c.d and a.e.f.g; needs to set controller for all!
85
86
87    # if there are any context handlers, call the fastsave to write the value into the context
88    if hasattr(self, "contextHandlers") and hasattr(self, "currentContexts"):
89        for contextName, contextHandler in self.contextHandlers.items():
90            contextHandler.fastSave(self.currentContexts.get(contextName), self, name, value)
91
92
93
94class ControlledAttributesDict(dict):
95    def __init__(self, master):
96        self.master = master
97
98    def __setitem__(self, key, value):
99        if not self.has_key(key):
100            dict.__setitem__(self, key, [value])
101        else:
102            dict.__getitem__(self, key).append(value)
103        self.master.setControllers(self.master, key, self.master, "")
104
105
106
107##################
108# this definitions are needed only to define ExampleTable as subclass of ExampleTableWithClass
109from orange import ExampleTable
110
111class AttributeList(list):
112    pass
113
114class ExampleList(list):
115    pass
116
117widgetId = 0
118
119class OWBaseWidget(QDialog):
120    def __new__(cls, *arg, **args):
121        self = QDialog.__new__(cls)
122       
123        #print "arg", arg
124        #print "args: ", args
125        self.currentContexts = {}   # the "currentContexts" MUST be the first thing assigned to a widget
126        self._useContexts = 1       # do you want to use contexts
127        self._owInfo = 1            # currently disabled !!!
128        self._owWarning = 1         # do we want to see warnings
129        self._owError = 1           # do we want to see errors
130        self._owShowStatus = 0      # do we want to see warnings and errors in status bar area of the widget
131        self._guiElements = []      # used for automatic widget debugging
132        for key in args:
133            if key in ["_owInfo", "_owWarning", "_owError", "_owShowStatus", "_useContexts", "_category", "_settingsFromSchema"]:
134                self.__dict__[key] = args[key]        # we cannot use __dict__.update(args) since we can have many other
135
136        return self
137
138
139    def __init__(self, parent = None, signalManager = None, title="Orange BaseWidget", modal=FALSE, savePosition = False, resizingEnabled = 1, **args):
140        if resizingEnabled:
141            QDialog.__init__(self, parent, Qt.Window)
142        else:
143            QDialog.__init__(self, parent, Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)# | Qt.WindowMinimizeButtonHint)
144           
145        # do we want to save widget position and restore it on next load
146        self.savePosition = savePosition
147        if savePosition:
148            self.settingsList = getattr(self, "settingsList", []) + ["widgetWidth", "widgetHeight", "widgetXPosition", "widgetYPosition", "widgetShown", "savedWidgetGeometry"]
149
150        # directories are better defined this way, otherwise .ini files get written in many places
151        self.__dict__.update(old_directory_names)
152        try:
153            self.__dict__["thisWidgetDir"] = os.path.dirname(sys.modules[self.__class__.__module__].__file__)
154        except:
155            pass
156
157        self.setCaption(title.replace("&","")) # used for widget caption
158        self.setFocusPolicy(Qt.StrongFocus)
159
160        # number of control signals, that are currently being processed
161        # needed by signalWrapper to know when everything was sent
162        self.parent = parent
163        self.needProcessing = 0     # used by signalManager
164        if not signalManager: self.signalManager = globalSignalManager        # use the global instance of signalManager  - not advised
165        else:                 self.signalManager = signalManager              # use given instance of signal manager
166
167        self.inputs = []     # signalName:(dataType, handler, onlySingleConnection)
168        self.outputs = []    # signalName: dataType
169        self.wrappers = []    # stored wrappers for widget events
170        self.linksIn = {}      # signalName : (dirty, widgetFrom, handler, signalData)
171        self.linksOut = {}       # signalName: (signalData, id)
172        self.connections = {}   # dictionary where keys are (control, signal) and values are wrapper instances. Used in connect/disconnect
173        self.controlledAttributes = ControlledAttributesDict(self)
174        self.progressBarHandler = None  # handler for progress bar events
175        self.processingHandler = None   # handler for processing events
176        self.eventHandler = None
177        self.callbackDeposit = []
178        self.startTime = time.time()    # used in progressbar
179
180        self.widgetStateHandler = None
181        self.widgetState = {"Info":{}, "Warning":{}, "Error":{}}
182
183        if hasattr(self, "contextHandlers"):
184            for contextHandler in self.contextHandlers.values():
185                contextHandler.initLocalContext(self)
186               
187        global widgetId
188        widgetId += 1
189        self.widgetId = widgetId
190       
191        self._private_thread_pools = {}
192        self.asyncCalls = []
193        self.asyncBlock = False
194       
195        self.connect(self, SIGNAL("blockingStateChanged(bool)"), lambda bool :self.signalManager.log.info("Blocking state changed %s %s" % (str(self), str(bool))))
196
197
198    # uncomment this when you need to see which events occured
199    """
200    def event(self, e):
201        #eventDict = dict([(0, 'None'), (1, 'Timer'), (2, 'MouseButtonPress'), (3, 'MouseButtonRelease'), (4, 'MouseButtonDblClick'), (5, 'MouseMove'), (6, 'KeyPress'), (7, 'KeyRelease'), (8, 'FocusIn'), (9, 'FocusOut'), (10, 'Enter'), (11, 'Leave'), (12, 'Paint'), (13, 'Move'), (14, 'Resize'), (15, 'Create'), (16, 'Destroy'), (17, 'Show'), (18, 'Hide'), (19, 'Close'), (20, 'Quit'), (21, 'Reparent'), (22, 'ShowMinimized'), (23, 'ShowNormal'), (24, 'WindowActivate'), (25, 'WindowDeactivate'), (26, 'ShowToParent'), (27, 'HideToParent'), (28, 'ShowMaximized'), (30, 'Accel'), (31, 'Wheel'), (32, 'AccelAvailable'), (33, 'CaptionChange'), (34, 'IconChange'), (35, 'ParentFontChange'), (36, 'ApplicationFontChange'), (37, 'ParentPaletteChange'), (38, 'ApplicationPaletteChange'), (40, 'Clipboard'), (42, 'Speech'), (50, 'SockAct'), (51, 'AccelOverride'), (60, 'DragEnter'), (61, 'DragMove'), (62, 'DragLeave'), (63, 'Drop'), (64, 'DragResponse'), (70, 'ChildInserted'), (71, 'ChildRemoved'), (72, 'LayoutHint'), (73, 'ShowWindowRequest'), (80, 'ActivateControl'), (81, 'DeactivateControl'), (1000, 'User')])
202        eventDict = dict([(0, "None"), (130, "AccessibilityDescription"), (119, "AccessibilityHelp"), (86, "AccessibilityPrepare"), (114, "ActionAdded"), (113, "ActionChanged"), (115, "ActionRemoved"), (99, "ActivationChange"), (121, "ApplicationActivated"), (122, "ApplicationDeactivated"), (36, "ApplicationFontChange"), (37, "ApplicationLayoutDirectionChange"), (38, "ApplicationPaletteChange"), (35, "ApplicationWindowIconChange"), (68, "ChildAdded"), (69, "ChildPolished"), (71, "ChildRemoved"), (40, "Clipboard"), (19, "Close"), (82, "ContextMenu"), (52, "DeferredDelete"), (60, "DragEnter"), (62, "DragLeave"), (61, "DragMove"), (63, "Drop"), (98, "EnabledChange"), (10, "Enter"), (150, "EnterEditFocus"), (124, "EnterWhatsThisMode"), (116, "FileOpen"), (8, "FocusIn"), (9, "FocusOut"), (97, "FontChange"), (159, "GraphicsSceneContextMenu"), (164, "GraphicsSceneDragEnter"), (166, "GraphicsSceneDragLeave"), (165, "GraphicsSceneDragMove"), (167, "GraphicsSceneDrop"), (163, "GraphicsSceneHelp"), (160, "GraphicsSceneHoverEnter"), (162, "GraphicsSceneHoverLeave"), (161, "GraphicsSceneHoverMove"), (158, "GraphicsSceneMouseDoubleClick"), (155, "GraphicsSceneMouseMove"), (156, "GraphicsSceneMousePress"), (157, "GraphicsSceneMouseRelease"), (168, "GraphicsSceneWheel"), (18, "Hide"), (27, "HideToParent"), (127, "HoverEnter"), (128, "HoverLeave"), (129, "HoverMove"), (96, "IconDrag"), (101, "IconTextChange"), (83, "InputMethod"), (6, "KeyPress"), (7, "KeyRelease"), (89, "LanguageChange"), (90, "LayoutDirectionChange"), (76, "LayoutRequest"), (11, "Leave"), (151, "LeaveEditFocus"), (125, "LeaveWhatsThisMode"), (88, "LocaleChange"), (153, "MenubarUpdated"), (43, "MetaCall"), (102, "ModifiedChange"), (4, "MouseButtonDblClick"), (2, "MouseButtonPress"), (3, "MouseButtonRelease"), (5, "MouseMove"), (109, "MouseTrackingChange"), (13, "Move"), (12, "Paint"), (39, "PaletteChange"), (131, "ParentAboutToChange"), (21, "ParentChange"), (75, "Polish"), (74, "PolishRequest"), (123, "QueryWhatsThis"), (14, "Resize"), (117, "Shortcut"), (51, "ShortcutOverride"), (17, "Show"), (26, "ShowToParent"), (50, "SockAct"), (112, "StatusTip"), (100, "StyleChange"), (87, "TabletMove"), (92, "TabletPress"), (93, "TabletRelease"), (171, "TabletEnterProximity"), (172, "TabletLeaveProximity"), (1, "Timer"), (120, "ToolBarChange"), (110, "ToolTip"), (78, "UpdateLater"), (77, "UpdateRequest"), (111, "WhatsThis"), (118, "WhatsThisClicked"), (31, "Wheel"), (132, "WinEventAct"), (24, "WindowActivate"), (103, "WindowBlocked"), (25, "WindowDeactivate"), (34, "WindowIconChange"), (105, "WindowStateChange"), (33, "WindowTitleChange"), (104, "WindowUnblocked"), (126, "ZOrderChange"), (169, "KeyboardLayoutChange"), (170, "DynamicPropertyChange")])
203        if eventDict.has_key(e.type()):
204            print str(self.windowTitle()), eventDict[e.type()]
205        return QDialog.event(self, e)
206    """
207
208    def getIconNames(self, iconName):
209        if type(iconName) == list:      # if canvas sent us a prepared list of valid names, just return those
210            return iconName
211       
212        names = []
213        name, ext = os.path.splitext(iconName)
214        for num in [16, 32, 42, 60]:
215            names.append("%s_%d%s" % (name, num, ext))
216        fullPaths = []
217        for paths in [(self.widgetDir, name), (self.widgetDir, "icons", name), (os.path.dirname(sys.modules[self.__module__].__file__), "icons", name)]:
218            for name in names + [iconName]:
219                fname = os.path.join(*paths)
220                if os.path.exists(fname):
221                    fullPaths.append(fname)
222            if fullPaths != []:
223                break
224
225        if len(fullPaths) > 1 and fullPaths[-1].endswith(iconName):
226            fullPaths.pop()     # if we have the new icons we can remove the default icon
227        return fullPaths
228   
229
230    def setWidgetIcon(self, iconName):
231        iconNames = self.getIconNames(iconName)
232           
233        icon = QIcon()
234        for name in iconNames:
235            pix = QPixmap(name)
236            icon.addPixmap(pix)
237#            frame = QPixmap(os.path.join(self.widgetDir, "icons/frame.png"))
238#            icon = QPixmap(iconName)
239#            result = QPixmap(icon.size())
240#            painter = QPainter()
241#            painter.begin(result)
242#            painter.drawPixmap(0,0, frame)
243#            painter.drawPixmap(0,0, icon)
244#            painter.end()
245
246        self.setWindowIcon(icon)
247       
248
249    # ##############################################
250    def createAttributeIconDict(self):
251        return OWGUI.getAttributeIcons()
252
253    def isDataWithClass(self, data, wantedVarType = None, checkMissing=False):
254        self.error([1234, 1235, 1236])
255        if not data:
256            return 0
257        if not data.domain.classVar:
258            self.error(1234, "A data set with a class attribute is required.")
259            return 0
260        if wantedVarType and data.domain.classVar.varType != wantedVarType:
261            self.error(1235, "Unable to handle %s class." % (data.domain.classVar.varType == orange.VarTypes.Discrete and "discrete" or "continuous"))
262            return 0
263        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
264            self.error(1236, "Unable to handle data set with no known classes")
265            return 0
266        return 1
267
268    # call processEvents(), but first remember position and size of widget in case one of the events would be move or resize
269    # call this function if needed in __init__ of the widget
270    def safeProcessEvents(self):
271        keys = ["widgetXPosition", "widgetYPosition", "widgetShown", "widgetWidth", "widgetHeight"]
272        vals = [(key, getattr(self, key, None)) for key in keys]
273        qApp.processEvents()
274        for (key, val) in vals:
275            if val != None:
276                setattr(self, key, val)
277
278
279    # this function is called at the end of the widget's __init__ when the widgets is saving its position and size parameters
280    def restoreWidgetPosition(self):
281        if self.savePosition:
282            geometry = getattr(self, "savedWidgetGeometry", None)
283            restored = False
284            if geometry is not None:
285               restored =  self.restoreGeometry(QByteArray(geometry))
286               
287            if restored:
288                space = qApp.desktop().availableGeometry(self)
289                frame, geometry = self.frameGeometry(), self.geometry()
290               
291                #Fix the widget size to fit inside the available space
292                width = min(space.width() - (frame.width() - geometry.width()), geometry.width())
293                height = min(space.height() - (frame.height() - geometry.height()), geometry.height())
294                self.resize(width, height)
295               
296                #Move the widget to the center of available space if it is currently outside it
297                if not space.contains(self.frameGeometry()):
298                    x = max(0, space.width() / 2 - width / 2)
299                    y = max(0, space.height() / 2 - height / 2)
300           
301                    self.move(x, y)
302           
303#            geometry.move(frameOffset) #Make sure the title bar is shown
304#            self.setGeometry(geometry.intersected(space.adjusted(-frameOffset.x(), -frameOffset.y(), 0, 0)))
305           
306           
307#            if self.isWindow():
308#                frame = self.frameGeometry()
309#                if space.topLeft() != QPoint(0, 0):
310#                    self.move(self.geometry().topLeft() - frame.topLeft())
311#            if getattr(self, "widgetXPosition", None) != None and getattr(self, "widgetYPosition", None) != None:
312##                print self.captionTitle, "restoring position", self.widgetXPosition, self.widgetYPosition, "to", max(self.widgetXPosition, 0), max(self.widgetYPosition, 0)
313#                self.move(max(self.widgetXPosition, space.x()), max(self.widgetYPosition, space.y()))
314#            if getattr(self,"widgetWidth", None) != None and getattr(self,"widgetHeight", None) != None:
315#                self.resize(min(self.widgetWidth, space.width()), min(self.widgetHeight, space.height()))
316#            frame = self.frameGeometry()
317#            area = lambda rect: rect.width() * rect.height()
318#            if area(frame.intersected(space)) < area(frame):
319#                self.move(max(min(space.right() - frame.width(), frame.x()), space.x()),
320#                          max(min(space.height() - frame.height(), frame.y()), space.y()))
321
322    # this is called in canvas when loading a schema. it opens the widgets that were shown when saving the schema
323    def restoreWidgetStatus(self):
324        if self.savePosition and getattr(self, "widgetShown", None):
325            self.show()
326
327    # when widget is resized, save new width and height into widgetWidth and widgetHeight. some widgets can put this two
328    # variables into settings and last widget shape is restored after restart
329    def resizeEvent(self, ev):
330        QDialog.resizeEvent(self, ev)
331        if self.savePosition:
332            self.widgetWidth = self.width()
333            self.widgetHeight = self.height()
334            self.savedWidgetGeometry = str(self.saveGeometry())
335
336
337    # when widget is moved, save new x and y position into widgetXPosition and widgetYPosition. some widgets can put this two
338    # variables into settings and last widget position is restored after restart
339    # Commented out because of Ubuntu (on call to restoreGeometry calls move event saving pos (0, 0)
340#    def moveEvent(self, ev):
341#        QDialog.moveEvent(self, ev)
342#        if self.savePosition:
343#            self.widgetXPosition = self.frameGeometry().x()
344#            self.widgetYPosition = self.frameGeometry().y()
345#            self.savedWidgetGeometry = str(self.saveGeometry())
346
347    # set widget state to hidden
348    def hideEvent(self, ev):
349        if self.savePosition:
350            self.widgetShown = 0
351            self.widgetXPosition = self.frameGeometry().x()
352            self.widgetYPosition = self.frameGeometry().y()
353            self.savedWidgetGeometry = str(self.saveGeometry())
354        QDialog.hideEvent(self, ev)
355
356    # override the default show function.
357    # after show() we must call processEvents because show puts some LayoutRequests in queue
358    # and we must process them immediately otherwise the width(), height(), ... of elements in the widget will be wrong
359#    def show(self):
360#        QDialog.show(self)
361#        qApp.processEvents()
362
363    # set widget state to shown
364    def showEvent(self, ev):   
365        QDialog.showEvent(self, ev)
366        if self.savePosition:
367            self.widgetShown = 1
368           
369        self.restoreWidgetPosition()
370       
371    def closeEvent(self, ev):
372        if self.savePosition:
373            self.widgetXPosition = self.frameGeometry().x()
374            self.widgetYPosition = self.frameGeometry().y()
375            self.savedWidgetGeometry = str(self.saveGeometry())
376        QDialog.closeEvent(self, ev)
377       
378    def wheelEvent(self, event):
379        """ Silently accept the wheel event. This is to ensure combo boxes
380        and other controls that have focus don't receive this event unless
381        the cursor is over them.
382       
383        """
384        event.accept()
385
386    def setCaption(self, caption):
387        if self.parent != None and isinstance(self.parent, QTabWidget):
388            self.parent.setTabText(self.parent.indexOf(self), caption)
389        else:
390            self.captionTitle = caption     # we have to save caption title in case progressbar will change it
391            self.setWindowTitle(caption)
392
393    # put this widget on top of all windows
394    def reshow(self):
395        self.show()
396        self.raise_()
397        self.activateWindow()
398
399
400    def send(self, signalName, value, id = None):
401        if not self.hasOutputName(signalName):
402            print "Warning! Signal '%s' is not a valid signal name for the '%s' widget. Please fix the signal name." % (signalName, self.captionTitle)
403
404        if self.linksOut.has_key(signalName):
405            self.linksOut[signalName][id] = value
406        else:
407            self.linksOut[signalName] = {id:value}
408
409        self.signalManager.send(self, signalName, value, id)
410
411
412    def getdeepattr(self, attr, **argkw):
413        try:
414            return reduce(lambda o, n: getattr(o, n, None),  attr.split("."), self)
415        except:
416            if argkw.has_key("default"):
417                return argkw[default]
418            else:
419                raise AttributeError, "'%s' has no attribute '%s'" % (self, attr)
420
421
422    # Set all settings
423    # settings - the map with the settings
424    def setSettings(self,settings):
425        for key in settings:
426            self.__setattr__(key, settings[key])
427        #self.__dict__.update(settings)
428
429    # Get all settings
430    # returns map with all settings
431    def getSettings(self, alsoContexts = True, globalContexts=False):
432        settings = {}
433        if hasattr(self, "settingsList"):
434            for name in self.settingsList:
435                try:
436                    settings[name] =  self.getdeepattr(name)
437                except:
438                    #print "Attribute %s not found in %s widget. Remove it from the settings list." % (name, self.captionTitle)
439                    pass
440       
441        if alsoContexts:
442            self.synchronizeContexts()
443            contextHandlers = getattr(self, "contextHandlers", {})
444            for contextHandler in contextHandlers.values():
445                contextHandler.mergeBack(self)
446#                settings[contextHandler.localContextName] = contextHandler.globalContexts
447# Instead of the above line, I found this; as far as I recall this was a fix
448# for some bugs related to, say, Select Attributes not handling the context
449# attributes properly, but I dare not add it without understanding what it does.
450# Here it is, if these contexts give us any further trouble.
451                if (contextHandler.syncWithGlobal and contextHandler.globalContexts is getattr(self, contextHandler.localContextName)) or globalContexts:
452                    settings[contextHandler.localContextName] = contextHandler.globalContexts
453                else:
454                    contexts = getattr(self, contextHandler.localContextName, None)
455                    if contexts:
456                        settings[contextHandler.localContextName] = contexts
457###
458                settings[contextHandler.localContextName+"Version"] = (contextStructureVersion, contextHandler.contextDataVersion)
459           
460        return settings
461
462
463    def getSettingsFile(self, file):
464        if file==None:
465            file = os.path.join(self.widgetSettingsDir, self.captionTitle + ".ini")
466            if not os.path.exists(file):
467                try:
468                    f = open(file, "wb")
469                    cPickle.dump({}, f)
470                    f.close()
471                except IOError:
472                    return 
473        if type(file) == str:
474            if os.path.exists(file):
475                return open(file, "r")
476        else:
477            return file
478
479
480    # Loads settings from the widget's .ini file
481    def loadSettings(self, file = None):
482        file = self.getSettingsFile(file)
483        if file:
484            try:
485                settings = cPickle.load(file)
486            except Exception, ex:
487                print >> sys.stderr, "Failed to load settings!", repr(ex)
488                settings = None
489           
490            if hasattr(self, "_settingsFromSchema"):
491                if settings: settings.update(self._settingsFromSchema)
492                else:        settings = self._settingsFromSchema
493
494            # can't close everything into one big try-except since this would mask all errors in the below code
495            if settings:
496                if hasattr(self, "settingsList"):
497                    self.setSettings(settings)
498
499                contextHandlers = getattr(self, "contextHandlers", {})
500                for contextHandler in contextHandlers.values():
501                    localName = contextHandler.localContextName
502
503                    structureVersion, dataVersion = settings.get(localName+"Version", (0, 0))
504                    if (structureVersion < contextStructureVersion or dataVersion < contextHandler.contextDataVersion) \
505                       and settings.has_key(localName):
506                        del settings[localName]
507                        delattr(self, localName)
508                        contextHandler.initLocalContext(self)
509                       
510                    if not hasattr(self, "_settingsFromSchema"): #When running stand alone widgets
511                        if contextHandler.syncWithGlobal:
512                            contexts = settings.get(localName, None)
513                            if contexts is not None:
514                                contextHandler.globalContexts = contexts
515                        else:
516                            setattr(self, localName, contextHandler.globalContexts)
517
518
519    def saveSettings(self, file = None):
520        settings = self.getSettings(globalContexts=True)
521        if settings:
522            if file==None:
523                file = os.path.join(self.widgetSettingsDir, self.captionTitle + ".ini")
524            if type(file) == str:
525                file = open(file, "w")
526            cPickle.dump(settings, file)
527
528    # Loads settings from string str which is compatible with cPickle
529    def loadSettingsStr(self, str):
530        if str == None or str == "":
531            return
532
533        settings = cPickle.loads(str)
534        self.setSettings(settings)
535
536        contextHandlers = getattr(self, "contextHandlers", {})
537        for contextHandler in contextHandlers.values():
538            localName = contextHandler.localContextName
539            if settings.has_key(localName):
540                structureVersion, dataVersion = settings.get(localName+"Version", (0, 0))
541                if structureVersion < contextStructureVersion or dataVersion < contextHandler.contextDataVersion:
542                    del settings[localName]
543                    delattr(self, localName)
544                    contextHandler.initLocalContext(self)
545                else:
546                    setattr(self, localName, settings[localName])
547
548    # return settings in string format compatible with cPickle
549    def saveSettingsStr(self):
550        settings = self.getSettings()
551        return cPickle.dumps(settings)
552
553    def onDeleteWidget(self):
554        pass
555
556    # this function is only intended for derived classes to send appropriate signals when all settings are loaded
557    def activateLoadedSettings(self):
558        pass
559
560    # reimplemented in other widgets
561    def setOptions(self):
562        pass
563
564    # does widget have a signal with name in inputs
565    def hasInputName(self, name):
566        for input in self.inputs:
567            if name == input[0]: return 1
568        return 0
569
570    # does widget have a signal with name in outputs
571    def hasOutputName(self, name):
572        for output in self.outputs:
573            if name == output[0]: return 1
574        return 0
575
576    def getInputType(self, signalName):
577        for input in self.inputs:
578            if input[0] == signalName: return input[1]
579        return None
580
581    def getOutputType(self, signalName):
582        for output in self.outputs:
583            if output[0] == signalName: return output[1]
584        return None
585
586    # ########################################################################
587    def connect(self, control, signal, method, type=Qt.AutoConnection):
588        wrapper = SignalWrapper(self, method)
589        self.connections[(control, signal)] = wrapper   # save for possible disconnect
590        self.wrappers.append(wrapper)
591        QDialog.connect(control, signal, wrapper, type)
592        #QWidget.connect(control, signal, method)        # ordinary connection useful for dialogs and windows that don't send signals to other widgets
593
594
595    def disconnect(self, control, signal, method=None):
596        wrapper = self.connections[(control, signal)]
597        QDialog.disconnect(control, signal, wrapper)
598
599
600    def getConnectionMethod(self, control, signal):
601        if (control, signal) in self.connections:
602            wrapper = self.connections[(control, signal)]
603            return wrapper.method
604        else:
605            return None
606
607
608    def signalIsOnlySingleConnection(self, signalName):
609        for i in self.inputs:
610            input = InputSignal(*i)
611            if input.name == signalName: return input.single
612
613    def addInputConnection(self, widgetFrom, signalName):
614        for i in range(len(self.inputs)):
615            if self.inputs[i][0] == signalName:
616                handler = self.inputs[i][2]
617                break
618
619        existing = []
620        if self.linksIn.has_key(signalName):
621            existing = self.linksIn[signalName]
622            for (dirty, widget, handler, data) in existing:
623                if widget == widgetFrom: return             # no need to add new tuple, since one from the same widget already exists
624        self.linksIn[signalName] = existing + [(0, widgetFrom, handler, [])]    # (dirty, handler, signalData)
625        #if not self.linksIn.has_key(signalName): self.linksIn[signalName] = [(0, widgetFrom, handler, [])]    # (dirty, handler, signalData)
626
627    # delete a link from widgetFrom and this widget with name signalName
628    def removeInputConnection(self, widgetFrom, signalName):
629        if self.linksIn.has_key(signalName):
630            links = self.linksIn[signalName]
631            for i in range(len(self.linksIn[signalName])):
632                if widgetFrom == self.linksIn[signalName][i][1]:
633                    self.linksIn[signalName].remove(self.linksIn[signalName][i])
634                    if self.linksIn[signalName] == []:  # if key is empty, delete key value
635                        del self.linksIn[signalName]
636                    return
637
638    # return widget, that is already connected to this singlelink signal. If this widget exists, the connection will be deleted (since this is only single connection link)
639    def removeExistingSingleLink(self, signal):
640        for i in self.inputs:
641            input = InputSignal(*i)
642            if input.name == signal and not input.single: return None
643
644        for signalName in self.linksIn.keys():
645            if signalName == signal:
646                widget = self.linksIn[signalName][0][1]
647                del self.linksIn[signalName]
648                return widget
649
650        return None
651
652
653    def handleNewSignals(self):
654        # this is called after all new signals have been handled
655        # implement this in your widget if you want to process something only after you received multiple signals
656        pass
657
658    # signal manager calls this function when all input signals have updated the data
659    def processSignals(self):
660        if self.processingHandler:
661            self.processingHandler(self, 1)    # focus on active widget
662        newSignal = 0        # did we get any new signals
663
664        # we define only a way to handle signals that have defined a handler function
665        for signal in self.inputs:        # we go from the first to the last defined input
666            key = signal[0]
667            if self.linksIn.has_key(key):
668                for i in range(len(self.linksIn[key])):
669                    (dirty, widgetFrom, handler, signalData) = self.linksIn[key][i]
670                    if not (handler and dirty): continue
671                    newSignal = 1
672
673                    qApp.setOverrideCursor(Qt.WaitCursor)
674                    try:
675                        for (value, id, nameFrom) in signalData:
676                            if self.signalIsOnlySingleConnection(key):
677                                self.printEvent("ProcessSignals: Calling %s with %s" % (handler, value), eventVerbosity = 2)
678                                handler(value)
679                            else:
680                                self.printEvent("ProcessSignals: Calling %s with %s (%s, %s)" % (handler, value, nameFrom, id), eventVerbosity = 2)
681                                handler(value, (widgetFrom, nameFrom, id))
682                    except:
683                        type, val, traceback = sys.exc_info()
684                        sys.excepthook(type, val, traceback)  # we pretend that we handled the exception, so that we don't crash other widgets
685                    qApp.restoreOverrideCursor()
686
687                    self.linksIn[key][i] = (0, widgetFrom, handler, []) # clear the dirty flag
688
689        if newSignal == 1:
690            self.handleNewSignals()
691       
692        while self.isBlocking():
693            self.thread().msleep(50)
694            qApp.processEvents()
695
696        if self.processingHandler:
697            self.processingHandler(self, 0)    # remove focus from this widget
698        self.needProcessing = 0
699
700    # set new data from widget widgetFrom for a signal with name signalName
701    def updateNewSignalData(self, widgetFrom, signalName, value, id, signalNameFrom):
702        if not self.linksIn.has_key(signalName): return
703        for i in range(len(self.linksIn[signalName])):
704            (dirty, widget, handler, signalData) = self.linksIn[signalName][i]
705            if widget == widgetFrom:
706                if self.linksIn[signalName][i][3] == []:
707                    self.linksIn[signalName][i] = (1, widget, handler, [(value, id, signalNameFrom)])
708                else:
709                    found = 0
710                    for j in range(len(self.linksIn[signalName][i][3])):
711                        (val, ID, nameFrom) = self.linksIn[signalName][i][3][j]
712                        if ID == id and nameFrom == signalNameFrom:
713                            self.linksIn[signalName][i][3][j] = (value, id, signalNameFrom)
714                            found = 1
715                    if not found:
716                        self.linksIn[signalName][i] = (1, widget, handler, self.linksIn[signalName][i][3] + [(value, id, signalNameFrom)])
717        self.needProcessing = 1
718
719
720    # ############################################
721    # PROGRESS BAR FUNCTIONS
722    def progressBarInit(self):
723        self.progressBarValue = 0
724        self.startTime = time.time()
725        self.setWindowTitle(self.captionTitle + " (0% complete)")
726        if self.progressBarHandler:
727            self.progressBarHandler(self, 0)
728
729    def progressBarSet(self, value):
730        if value > 0:
731            self.progressBarValue = value
732            usedTime = max(1, time.time() - self.startTime)
733            totalTime = (100.0*usedTime)/float(value)
734            remainingTime = max(0, totalTime - usedTime)
735            h = int(remainingTime/3600)
736            min = int((remainingTime - h*3600)/60)
737            sec = int(remainingTime - h*3600 - min*60)
738            if h > 0: text = "%(h)d:%(min)02d:%(sec)02d" % vars()
739            else:     text = "%(min)d:%(sec)02d" % vars()
740            self.setWindowTitle(self.captionTitle + " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
741        else:
742            self.setWindowTitle(self.captionTitle + " (0% complete)" )
743        if self.progressBarHandler: self.progressBarHandler(self, value)
744        qApp.processEvents()
745
746    def progressBarAdvance(self, value):
747        self.progressBarSet(self.progressBarValue+value)
748
749    def progressBarFinished(self):
750        self.setWindowTitle(self.captionTitle)
751        if self.progressBarHandler: self.progressBarHandler(self, 101)
752
753    # handler must be a function, that receives 2 arguments. First is the widget instance, the second is the value between -1 and 101
754    def setProgressBarHandler(self, handler):
755        self.progressBarHandler = handler
756
757    def setProcessingHandler(self, handler):
758        self.processingHandler = handler
759
760    def setEventHandler(self, handler):
761        self.eventHandler = handler
762
763    def setWidgetStateHandler(self, handler):
764        self.widgetStateHandler = handler
765
766
767    # if we are in debug mode print the event into the file
768    def printEvent(self, text, eventVerbosity = 1):
769        self.signalManager.addEvent(self.captionTitle + ": " + text, eventVerbosity = eventVerbosity)
770        if self.eventHandler:
771            self.eventHandler(self.captionTitle + ": " + text, eventVerbosity)
772
773    def openWidgetHelp(self):
774        if "widgetInfo" in self.__dict__:  # This widget is on a canvas.
775            qApp.canvasDlg.helpWindow.showHelpFor(self.widgetInfo, True)
776       
777    def focusInEvent(self, *ev):
778        #print "focus in"
779        #if qApp.canvasDlg.settings["synchronizeHelp"]:  on ubuntu: pops up help window on first widget focus for every widget   
780        #    qApp.canvasDlg.helpWindow.showHelpFor(self, True)
781        QDialog.focusInEvent(self, *ev)
782       
783   
784    def keyPressEvent(self, e):
785        if e.key() in (Qt.Key_Help, Qt.Key_F1):
786            self.openWidgetHelp()
787#            e.ignore()
788        elif (int(e.modifiers()), e.key()) in OWBaseWidget.defaultKeyActions:
789            OWBaseWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
790        else:
791            QDialog.keyPressEvent(self, e)
792
793    def information(self, id = 0, text = None):
794        self.setState("Info", id, text)
795        #self.setState("Warning", id, text)
796
797    def warning(self, id = 0, text = ""):
798        self.setState("Warning", id, text)
799        #self.setState("Info", id, text)        # if we want warning just set information
800
801    def error(self, id = 0, text = ""):
802        self.setState("Error", id, text)
803
804    def setState(self, stateType, id, text):
805        changed = 0
806        if type(id) == list:
807            for val in id:
808                if self.widgetState[stateType].has_key(val):
809                    self.widgetState[stateType].pop(val)
810                    changed = 1
811        else:
812            if type(id) == str:
813                text = id; id = 0       # if we call information(), warning(), or error() function with only one parameter - a string - then set id = 0
814            if not text:
815                if self.widgetState[stateType].has_key(id):
816                    self.widgetState[stateType].pop(id)
817                    changed = 1
818            else:
819                self.widgetState[stateType][id] = text
820                changed = 1
821
822        if changed:
823            if self.widgetStateHandler:
824                self.widgetStateHandler()
825            elif text: # and stateType != "Info":
826                self.printEvent(stateType + " - " + text)
827           
828            if type(id) == list:
829                for i in id:
830                    self.emit(SIGNAL("widgetStateChanged(QString, int, QString)"),
831                              QString(stateType), i,QString(""))
832            else:
833                self.emit(SIGNAL("widgetStateChanged(QString, int, QString)"),
834                             QString(stateType), id, QString(text or ""))
835            #qApp.processEvents()
836        return changed
837   
838    def widgetStateToHtml(self, info=True, warning=True, error=True):
839        pixmaps = self.getWidgetStateIcons()
840        items = [] 
841        iconPath = {"Info": "canvasIcons:information.png",
842                    "Warning": "canvasIcons:warning.png",
843                    "Error": "canvasIcons:error.png"}
844        for show, what in [(info, "Info"), (warning, "Warning"),(error, "Error")]:
845            if self.widgetState[what]:
846                items.append('<img src="%s" style="float: left;"> %s' % (iconPath[what], "\n".join(self.widgetState[what].values())))
847        return "<br>".join(items)
848       
849    @classmethod
850    def getWidgetStateIcons(cls):
851        if not hasattr(cls, "_cached__widget_state_icons"):
852            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
853            QDir.addSearchPath("canvasIcons",os.path.join(environ.canvas_install_dir,
854                "icons/"))
855            info = QPixmap("canvasIcons:information.png")
856            warning = QPixmap("canvasIcons:warning.png")
857            error = QPixmap("canvasIcons:error.png")
858            cls._cached__widget_state_icons = \
859                    {"Info": info, "Warning": warning, "Error": error}
860        return cls._cached__widget_state_icons
861
862    def synchronizeContexts(self):
863        if hasattr(self, "contextHandlers"):
864            for contextName, handler in self.contextHandlers.items():
865                context = self.currentContexts.get(contextName, None)
866                if context:
867                    handler.settingsFromWidget(self, context)
868
869    def openContext(self, contextName="", *arg):
870        if not self._useContexts:
871            return
872        handler = self.contextHandlers[contextName]
873        context = handler.openContext(self, *arg)
874        if context:
875            self.currentContexts[contextName] = context
876
877
878    def closeContext(self, contextName=""):
879        if not self._useContexts:
880            return
881        curcontext = self.currentContexts.get(contextName)
882        if curcontext:
883            self.contextHandlers[contextName].closeContext(self, curcontext)
884            del self.currentContexts[contextName]
885
886    def settingsToWidgetCallback(self, handler, context):
887        pass
888
889    def settingsFromWidgetCallback(self, handler, context):
890        pass
891
892    def setControllers(self, obj, controlledName, controller, prefix):
893        while obj:
894            if prefix:
895#                print "SET CONTROLLERS: %s %s + %s" % (obj.__class__.__name__, prefix, controlledName)
896                if obj.__dict__.has_key("attributeController"):
897                    obj.__dict__["__attributeControllers"][(controller, prefix)] = True
898                else:
899                    obj.__dict__["__attributeControllers"] = {(controller, prefix): True}
900
901            parts = controlledName.split(".", 1)
902            if len(parts) < 2:
903                break
904            obj = getattr(obj, parts[0], None)
905            prefix += parts[0]
906            controlledName = parts[1]
907
908    def __setattr__(self, name, value):
909        return unisetattr(self, name, value, QDialog)
910   
911    defaultKeyActions = {}
912   
913    if sys.platform == "darwin":
914        defaultKeyActions = {
915            (Qt.ControlModifier, Qt.Key_M): lambda self: self.showMaximized if self.isMinimized() else self.showMinimized(),
916            (Qt.ControlModifier, Qt.Key_W): lambda self: self.setVisible(not self.isVisible())}
917
918
919    def scheduleSignalProcessing(self):
920        self.signalManager.scheduleSignalProcessing(self)
921
922    def setBlocking(self, state=True):
923        """ Set blocking flag for this widget. While this flag is set this
924        widget and all its descendants will not receive any new signals from
925        the signal manager
926        """
927        self.asyncBlock = state
928        self.emit(SIGNAL("blockingStateChanged(bool)"), self.asyncBlock)
929        if not self.isBlocking():
930            self.scheduleSignalProcessing()
931       
932       
933    def isBlocking(self):
934        """ Is this widget blocking signal processing. Widget is blocking if
935        asyncBlock value is True or any AsyncCall objects in asyncCalls list
936        has blocking flag set
937        """
938        return self.asyncBlock or any(a.blocking for a in self.asyncCalls)
939   
940    def asyncExceptionHandler(self, (etype, value, tb)):
941        import traceback
942        sys.excepthook(etype, value, tb)
943       
944    def asyncFinished(self, async, string):
945        """ Remove async from asyncCalls, update blocking state
946        """
947       
948        index = self.asyncCalls.index(async)
949        async = self.asyncCalls.pop(index)
950       
951        if async.blocking and not self.isBlocking():
952            # if we are responsible for unblocking
953            self.emit(SIGNAL("blockingStateChanged(bool)"), False)
954            self.scheduleSignalProcessing()
955           
956        async.disconnect(async, SIGNAL("finished(PyQt_PyObject, QString)"), self.asyncFinished)
957        self.emit(SIGNAL("asyncCallsStateChange()"))
958               
959           
960   
961    def asyncCall(self, func, args=(), kwargs={}, name=None, onResult=None, onStarted=None, onFinished=None, onError=None, blocking=True, thread=None, threadPool=None):
962        """ Return an OWConcurent.AsyncCall object func, args and kwargs
963        set and signals connected.
964        """
965        from functools import partial
966        from OWConcurrent import AsyncCall
967       
968        asList = lambda slot: slot if isinstance(slot, list) else ([slot] if slot else [])
969       
970        onResult = asList(onResult)
971        onStarted = asList(onStarted) #+ [partial(self.setBlocking, True)]
972        onFinished = asList(onFinished) #+ [partial(self.blockSignals, False)]
973        onError = asList(onError) or [self.asyncExceptionHandler]
974       
975        async = AsyncCall(func, args, kwargs, thread=thread, threadPool=threadPool)
976        async.name = name if name is not None else ""
977           
978        for slot in  onResult:
979            async.connect(async, SIGNAL("resultReady(PyQt_PyObject)"), slot, Qt.QueuedConnection)
980        for slot in onStarted:
981            async.connect(async, SIGNAL("starting()"), slot, Qt.QueuedConnection)
982        for slot in onFinished:
983            async.connect(async, SIGNAL("finished(QString)"), slot, Qt.QueuedConnection)
984        for slot in onError:
985            async.connect(async, SIGNAL("unhandledException(PyQt_PyObject)"), slot, Qt.QueuedConnection)
986       
987        self.addAsyncCall(async, blocking)
988           
989        return async
990   
991    def addAsyncCall(self, async, blocking=True):
992        """ Add AsyncCall object to asyncCalls list (will be removed
993        once it finishes processing).
994       
995        """
996        ## TODO: make this thread safe
997       
998        async.connect(async, SIGNAL("finished(PyQt_PyObject, QString)"), self.asyncFinished)
999       
1000        async.blocking = blocking
1001       
1002        if blocking:
1003            # if we are responsible for blocking
1004            state = any(a.blocking for a in self.asyncCalls)
1005            self.asyncCalls.append(async)
1006            if not state:
1007                self.emit(SIGNAL("blockingStateChanged(bool)"), True)
1008        else:
1009            self.asyncCalls.append(async)
1010           
1011        self.emit(SIGNAL("asyncCallsStateChange()"))
1012       
1013   
1014   
1015def blocking(method):
1016    """ Return method that sets blocking flag while executing
1017    """
1018    from functools import wraps
1019    @wraps(method)
1020    def wrapper(self, *args, **kwargs):
1021        old = self._blocking
1022        self.setBlocking(True)
1023        try:
1024            return method(self, *args, **kwargs)
1025        finally:
1026            self.setBlocking(old)
1027   
1028
1029if __name__ == "__main__":
1030    a=QApplication(sys.argv)
1031    oww=OWBaseWidget(adfaf=1)
1032    oww.show()
1033    a.exec_()
1034    oww.saveSettings()
Note: See TracBrowser for help on using the repository browser.