source: orange/orange/OrangeWidgets/OWBaseWidget.py @ 4620:06ec3a4bd0cd

Revision 4620:06ec3a4bd0cd, 37.8 KB checked in by janezd <janez.demsar@…>, 5 years ago (diff)
  • added checking of the exit code after calling htmlhelp
  • commented out the htmlhelp since catalog.chm is practically empty
Line 
1#
2# OWWidget.py
3# Orange Widget
4# A General Orange Widget, from which all the Orange Widgets are derived
5#
6from OWTools import *
7from OWContexts import *
8import sys, time, random, user, os, os.path, cPickle, copy, orngMisc
9import orange
10import orngDebugging
11from string import *
12from orngSignalManager import *
13
14def unisetattr(self, name, value, grandparent):
15    if "." in name:
16        names = name.split(".")
17        lastname = names.pop()
18        obj = reduce(lambda o, n: getattr(o, n, None),  names, self)
19    else:
20        lastname, obj = name, self
21
22    if not obj:
23        print "unable to set setting ", name, " to value ", value
24    else:
25        if hasattr(grandparent, "__setattr__") and isinstance(obj, grandparent):
26            grandparent.__setattr__(obj, lastname,  value)
27        else:
28            obj.__dict__[lastname] = value
29
30    controlledAttributes = getattr(self, "controlledAttributes", None)
31    controlCallback = controlledAttributes and controlledAttributes.get(name, None)
32    if controlCallback:
33        for callback in controlCallback:
34            callback(value)
35#        controlCallback(value)
36
37    # controlled things (checkboxes...) never have __attributeControllers
38    else:
39        if hasattr(self, "__attributeControllers"):
40            for controller, myself in self.__attributeControllers.keys():
41                if getattr(controller, myself, None) != self:
42                    del self.__attributeControllers[(controller, myself)]
43                    continue
44
45                controlledAttributes = getattr(controller, "controlledAttributes", None)
46                if controlledAttributes:
47                    fullName = myself + "." + name
48
49                    controlCallback = controlledAttributes.get(fullName, None)
50                    if controlCallback:
51                        for callback in controlCallback:
52                            callback(value)
53
54                    else:
55                        lname = fullName + "."
56                        dlen = len(lname)
57                        for controlled in controlledAttributes.keys():
58                            if controlled[:dlen] == lname:
59                                self.setControllers(value, controlled[dlen:], controller, fullName)
60                                # no break -- can have a.b.c.d and a.e.f.g; needs to set controller for all!
61
62
63    # if there are any context handlers, call the fastsave to write the value into the context
64    if hasattr(self, "contextHandlers") and hasattr(self, "currentContexts"):
65        for contextName, contextHandler in self.contextHandlers.items():
66            contextHandler.fastSave(self.currentContexts.get(contextName), self, name, value)
67
68
69
70class ControlledAttributesDict(dict):
71    def __init__(self, master):
72        self.master = master
73
74    def __setitem__(self, key, value):
75        if not self.has_key(key):
76            dict.__setitem__(self, key, [value])
77        else:
78            dict.__getitem__(self, key).append(value)
79        self.master.setControllers(self.master, key, self.master, "")
80
81
82
83##################
84# this definitions are needed only to define ExampleTable as subclass of ExampleTableWithClass
85class ExampleTable(orange.ExampleTable):
86    pass
87
88#class ExampleTableWithClass(ExampleTable):
89#    pass
90
91class AttributeList(list):
92    pass
93
94class ExampleList(list):
95    pass
96
97class OWBaseWidget(QDialog):
98    def __init__(self, parent = None, signalManager = None, title="Orange BaseWidget", modal=FALSE, savePosition = False):
99        # the "currentContexts" MUST be the first thing assigned to a widget
100        self.currentContexts = {}
101        self._guiElements = []      # used for automatic widget debugging
102        self._useContexts = 1       # do you want to use contexts
103        self._owInfo = 1            # currently disabled !!!
104        self._owWarning = 1         # do we want to see warnings
105        self._owError = 1           # do we want to see errors
106        self._owShowStatus = 0      # do we want to see warnings and errors in status bar area of the widget
107
108        # do we want to save widget position and restore it on next load
109        self.savePosition = savePosition
110        if savePosition:
111            self.settingsList = getattr(self, "settingsList", []) + ["widgetWidth", "widgetHeight", "widgetXPosition", "widgetYPosition", "widgetShown"]
112
113        self.title = title.replace("&","")
114
115        QDialog.__init__(self, parent, self.title, modal, Qt.WStyle_Customize + Qt.WStyle_NormalBorder + Qt.WStyle_Title + Qt.WStyle_SysMenu + Qt.WStyle_Minimize + Qt.WStyle_Maximize)
116
117        # directories are better defined this way, otherwise .ini files get written in many places
118        self.widgetDir = os.path.dirname(__file__) + "/"
119
120        # create output directory for widget settings
121        home = user.home
122        if home[-1] == ":":
123            home += "\\"
124        if os.name == "nt":
125            if not os.path.exists(os.path.join(home, "Application Data")):
126                os.mkdir(os.path.join(home, "Application Data"))
127            self.outputDir = os.path.join(os.path.join(home, "Application Data"), "Orange")                  # directory for saving settings and stuff
128        elif sys.platform == "darwin":
129            self.outputDir = os.path.join(home, "Library")
130            self.outputDir = os.path.join(self.outputDir, "Application Support")
131            self.outputDir = os.path.join(self.outputDir, "Orange")
132        else:
133            self.outputDir = os.path.join(home, "Orange")                  # directory for saving settings and stuff
134
135        if not os.path.exists(self.outputDir):
136            try: os.mkdir(self.outputDir)            # Vista has roaming profiles that will say that this folder does not exist and will then fail to create it, because it exists...
137            except: pass
138
139        self.outputDir = os.path.join(self.outputDir, "widgetSettings")
140        if not os.path.exists(self.outputDir):
141            try: os.mkdir(self.outputDir)            # Vista has roaming profiles that will say that this folder does not exist and will then fail to create it, because it exists...
142            except: pass
143
144        self.captionTitle = title.replace("&","")     # used for widget caption
145
146        # if we want the widget to show the title then the title must start with "Qt"
147        if (int(qVersion()[0]) < 3) and self.captionTitle[:2].upper() != "QT":
148            self.captionTitle = "Qt " + self.captionTitle
149
150        # number of control signals, that are currently being processed
151        # needed by signalWrapper to know when everything was sent
152        self.parent = parent
153        self.needProcessing = 0     # used by signalManager
154        if not signalManager: self.signalManager = globalSignalManager        # use the global instance of signalManager  - not advised
155        else:                 self.signalManager = signalManager              # use given instance of signal manager
156
157        self.inputs = []     # signalName:(dataType, handler, onlySingleConnection)
158        self.outputs = []    # signalName: dataType
159        self.wrappers =[]    # stored wrappers for widget events
160        self.linksIn = {}      # signalName : (dirty, widgetFrom, handler, signalData)
161        self.linksOut = {}       # signalName: (signalData, id)
162        self.connections = {}   # dictionary where keys are (control, signal) and values are wrapper instances. Used in connect/disconnect
163        self.controlledAttributes = ControlledAttributesDict(self)
164        self.progressBarHandler = None  # handler for progress bar events
165        self.processingHandler = None   # handler for processing events
166        self.eventHandler = None
167        self.callbackDeposit = []
168        self.startTime = time.time()    # used in progressbar
169
170        self.widgetStateHandler = None
171        self.widgetState = {"Info":{}, "Warning":{}, "Error":{}}
172
173        #the title
174        self.setCaption(self.captionTitle)
175
176        if hasattr(self, "contextHandlers"):
177            for contextHandler in self.contextHandlers.values():
178                contextHandler.initLocalContext(self)
179
180    # uncomment this when you need to see which events occured
181    """
182    def event(self, e):
183        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')])
184        print self, eventDict[e.type()]
185        return QDialog.event(self, e)
186    """
187
188    def setWidgetIcon(self, iconName):
189        if os.path.exists(iconName):
190            QDialog.setIcon(self, QPixmap(iconName))
191        elif os.path.exists(os.path.join(self.widgetDir, iconName)):
192            QDialog.setIcon(self, QPixmap(os.path.join(self.widgetDir, iconName)))
193        elif os.path.exists(os.path.join(self.widgetDir, "icons/" + iconName)):
194            QDialog.setIcon(self, QPixmap(os.path.join(self.widgetDir, "icons/" + iconName)))
195        elif os.path.exists(os.path.join(os.path.dirname(sys.modules[self.__module__].__file__), "icons/" + iconName)):        # search for icons also in the folder where the module is
196            QDialog.setIcon(self, QPixmap(os.path.join(os.path.dirname(sys.modules[self.__module__].__file__), "icons/" + iconName)))
197        elif os.path.exists(os.path.join(self.widgetDir, "icons/Unknown.png")):
198            QDialog.setIcon(self, QPixmap(os.path.join(self.widgetDir, "icons/Unknown.png")))
199
200    # ##############################################
201    def createAttributeIconDict(self):
202        import OWGUI
203        return OWGUI.getAttributeIcons()
204
205    def isDataWithClass(self, data, wantedVarType = None):
206        self.error([1234, 1235])
207        if not data:
208            return 0
209        if not data.domain.classVar:
210            self.error(1234, "A data set with a class attribute is required.")
211            return 0
212        if wantedVarType and data.domain.classVar.varType != wantedVarType:
213            self.error(1235, "Unable to handle %s class." % (data.domain.classVar.varType == orange.VarTypes.Discrete and "discrete" or "continuous"))
214            return 0
215        return 1
216
217    # call processEvents(), but first remember position and size of widget in case one of the events would be move or resize
218    # call this function if needed in __init__ of the widget
219    def safeProcessEvents(self):
220        keys = ["widgetXPosition", "widgetYPosition", "widgetShown", "widgetWidth", "widgetHeight"]
221        vals = [(key, getattr(self, key, None)) for key in keys]
222        qApp.processEvents()
223        for (key, val) in vals:
224            if val != None:
225                setattr(self, key, val)
226
227
228    # this function is called at the end of the widget's __init__ when the widgets is saving its position and size parameters
229    def restoreWidgetPosition(self):
230        if self.savePosition:
231            if getattr(self, "widgetXPosition", None) != None and getattr(self, "widgetYPosition", None) != None:
232##                print self.title, "restoring position", self.widgetXPosition, self.widgetYPosition
233                self.move(self.widgetXPosition, self.widgetYPosition)
234            if getattr(self, "widgetWidth", None) != None and getattr(self, "widgetHeight", None) != None:
235                self.resize(self.widgetWidth, self.widgetHeight)
236
237    # this is called in canvas when loading a schema. it opens the widgets that were shown when saving the schema
238    def restoreWidgetStatus(self):
239        if self.savePosition and getattr(self, "widgetShown", None):
240            self.show()
241
242    # when widget is resized, save new width and height into widgetWidth and widgetHeight. some widgets can put this two
243    # variables into settings and last widget shape is restored after restart
244    def resizeEvent(self, ev):
245        QDialog.resizeEvent(self, ev)
246        if self.savePosition:
247            self.widgetWidth = self.width()
248            self.widgetHeight = self.height()
249##            print self.title, "saving resize", self.widgetWidth, self.widgetHeight
250
251
252    # when widget is moved, save new x and y position into widgetXPosition and widgetYPosition. some widgets can put this two
253    # variables into settings and last widget position is restored after restart
254    def moveEvent(self, ev):
255        QDialog.moveEvent(self, ev)
256        if self.savePosition:
257            self.widgetXPosition = ev.pos().x()
258            self.widgetYPosition = ev.pos().y()
259##            print self.title, "saving position", self.widgetXPosition, self.widgetYPosition
260
261    # set widget state to hidden
262    def hideEvent(self, ev):
263        QDialog.hideEvent(self, ev)
264        if self.savePosition:
265            self.widgetShown = 0
266
267    # set widget state to shown
268    def showEvent(self, ev):
269        QDialog.showEvent(self, ev)
270        if self.savePosition:
271            self.widgetShown = 1
272
273    def setCaption(self, caption):
274        if self.parent != None and isinstance(self.parent, QTabWidget): self.parent.changeTab(self, caption)
275        else: QDialog.setCaption(self, caption)
276
277    def setCaptionTitle(self, caption):
278        self.captionTitle = caption     # we have to save caption title in case progressbar will change it
279        self.setCaption(caption)
280
281    # put this widget on top of all windows
282    def reshow(self):
283        self.hide()
284        self.show()
285
286    def send(self, signalName, value, id = None):
287        if not self.hasOutputName(signalName):
288            print "Warning! Signal '%s' is not a valid signal name for the '%s' widget. Please fix the signal name." % (signalName, self.title)
289
290        if self.linksOut.has_key(signalName):
291            self.linksOut[signalName][id] = value
292        else:
293            self.linksOut[signalName] = {id:value}
294
295        self.signalManager.send(self, signalName, value, id)
296
297
298    def getdeepattr(self, attr, **argkw):
299        try:
300            return reduce(lambda o, n: getattr(o, n),  attr.split("."), self)
301        except:
302            if argkw.has_key("default"):
303                return argkw[default]
304            else:
305                raise AttributeError, "'%s' has no attribute '%s'" % (self, attr)
306
307
308    # Set all settings
309    # settings - the map with the settings
310    def setSettings(self,settings):
311        for key in settings:
312            self.__setattr__(key, settings[key])
313        #self.__dict__.update(settings)
314
315    # Get all settings
316    # returns map with all settings
317    def getSettings(self):
318        settings = {}
319        if hasattr(self, "settingsList"):
320            for name in self.settingsList:
321                try:
322                    settings[name] =  self.getdeepattr(name)
323                except:
324                    #print "Attribute %s not found in %s widget. Remove it from the settings list." % (name, self.title)
325                    pass
326        return settings
327
328
329    def getSettingsFile(self, file):
330        if file==None:
331            if os.path.exists(os.path.join(self.outputDir, self.title + ".ini")):
332                file = os.path.join(self.outputDir, self.title + ".ini")
333            else:
334                return
335        if type(file) == str:
336            if os.path.exists(file):
337                return open(file, "r")
338        else:
339            return file
340
341
342    # Loads settings from the widget's .ini file
343    def loadSettings(self, file = None):
344        file = self.getSettingsFile(file)
345        if file:
346            try:
347                settings = cPickle.load(file)
348            except:
349                settings = None
350
351            # can't close everything into one big try-except since this would mask all errors in the below code
352            if settings:
353                if hasattr(self, "settingsList"):
354                    self.setSettings(settings)
355
356                contextHandlers = getattr(self, "contextHandlers", {})
357                for contextHandler in contextHandlers.values():
358                    localName = contextHandler.localContextName
359
360                    structureVersion, dataVersion = settings.get(localName+"Version", (0, 0))
361                    if (structureVersion < contextStructureVersion or dataVersion < contextHandler.contextDataVersion) \
362                       and settings.has_key(localName):
363                        del settings[localName]
364                        delattr(self, localName)
365                        contextHandler.initLocalContext(self)
366
367                    if not getattr(contextHandler, "globalContexts", False): # don't have it or empty
368                        contexts = settings.get(localName, False)
369                        if contexts != False:
370                            contextHandler.globalContexts = contexts
371                    else:
372                        if contextHandler.syncWithGlobal:
373                            setattr(self, localName, contextHandler.globalContexts)
374
375
376    def saveSettings(self, file = None):
377        settings = self.getSettings()
378
379        contextHandlers = getattr(self, "contextHandlers", {})
380        for contextHandler in contextHandlers.values():
381            contextHandler.mergeBack(self)
382            settings[contextHandler.localContextName] = contextHandler.globalContexts
383            settings[contextHandler.localContextName+"Version"] = (contextStructureVersion, contextHandler.contextDataVersion)
384
385        if settings:
386            if file==None:
387                file = os.path.join(self.outputDir, self.title + ".ini")
388            if type(file) == str:
389                file = open(file, "w")
390            cPickle.dump(settings, file)
391
392    # Loads settings from string str which is compatible with cPickle
393    def loadSettingsStr(self, str):
394        if str == None or str == "":
395            return
396
397        settings = cPickle.loads(str)
398        self.setSettings(settings)
399
400        contextHandlers = getattr(self, "contextHandlers", {})
401        for contextHandler in contextHandlers.values():
402            localName = contextHandler.localContextName
403            if settings.has_key(localName):
404                structureVersion, dataVersion = settings.get(localName+"Version", (0, 0))
405                if structureVersion < contextStructureVersion or dataVersion < contextHandler.contextDataVersion:
406                    del settings[localName]
407                    delattr(self, localName)
408                    contextHandler.initLocalContext(self)
409                else:
410                    setattr(self, localName, settings[localName])
411
412    # return settings in string format compatible with cPickle
413    def saveSettingsStr(self):
414        str = ""
415        settings = self.getSettings()
416
417        contextHandlers = getattr(self, "contextHandlers", {})
418        for contextHandler in contextHandlers.values():
419            settings[contextHandler.localContextName] = getattr(self, contextHandler.localContextName)
420            settings[contextHandler.localContextName+"Version"] = (contextStructureVersion, contextHandler.contextDataVersion)
421
422        return cPickle.dumps(settings)
423
424    # this function is only intended for derived classes to send appropriate signals when all settings are loaded
425    def activateLoadedSettings(self):
426        pass
427
428    # reimplemented in other widgets
429    def setOptions(self):
430        pass
431
432    # does widget have a signal with name in inputs
433    def hasInputName(self, name):
434        for input in self.inputs:
435            if name == input[0]: return 1
436        return 0
437
438    # does widget have a signal with name in outputs
439    def hasOutputName(self, name):
440        for output in self.outputs:
441            if name == output[0]: return 1
442        return 0
443
444    def getInputType(self, signalName):
445        for input in self.inputs:
446            if input[0] == signalName: return input[1]
447        return None
448
449    def getOutputType(self, signalName):
450        for output in self.outputs:
451            if output[0] == signalName: return output[1]
452        return None
453
454    # ########################################################################
455    def connect(self, control, signal, method):
456        wrapper = SignalWrapper(self, method)
457        self.connections[(control, signal)] = wrapper   # save for possible disconnect
458        self.wrappers.append(wrapper)
459        QDialog.connect(control, signal, wrapper)
460        #QWidget.connect(control, signal, method)        # ordinary connection useful for dialogs and windows that don't send signals to other widgets
461
462
463    def disconnect(self, control, signal, method=None):
464        wrapper = self.connections[(control, signal)]
465        QDialog.disconnect(control, signal, wrapper)
466
467
468    def getConnectionMethod(self, control, signal):
469        if (control, signal) in self.connections:
470            wrapper = self.connections[(control, signal)]
471            return wrapper.method
472        else:
473            return None
474
475
476    def signalIsOnlySingleConnection(self, signalName):
477        for i in self.inputs:
478            input = InputSignal(*i)
479            if input.name == signalName: return input.single
480
481    def addInputConnection(self, widgetFrom, signalName):
482        for i in range(len(self.inputs)):
483            if self.inputs[i][0] == signalName:
484                handler = self.inputs[i][2]
485                break
486
487        existing = []
488        if self.linksIn.has_key(signalName):
489            existing = self.linksIn[signalName]
490            for (dirty, widget, handler, data) in existing:
491                if widget == widgetFrom: return             # no need to add new tuple, since one from the same widget already exists
492        self.linksIn[signalName] = existing + [(0, widgetFrom, handler, [])]    # (dirty, handler, signalData)
493        #if not self.linksIn.has_key(signalName): self.linksIn[signalName] = [(0, widgetFrom, handler, [])]    # (dirty, handler, signalData)
494
495    # delete a link from widgetFrom and this widget with name signalName
496    def removeInputConnection(self, widgetFrom, signalName):
497        if self.linksIn.has_key(signalName):
498            links = self.linksIn[signalName]
499            for i in range(len(self.linksIn[signalName])):
500                if widgetFrom == self.linksIn[signalName][i][1]:
501                    self.linksIn[signalName].remove(self.linksIn[signalName][i])
502                    if self.linksIn[signalName] == []:  # if key is empty, delete key value
503                        del self.linksIn[signalName]
504                    return
505
506    # 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)
507    def removeExistingSingleLink(self, signal):
508        for i in self.inputs:
509            input = InputSignal(*i)
510            if input.name == signal and not input.single: return None
511
512        for signalName in self.linksIn.keys():
513            if signalName == signal:
514                widget = self.linksIn[signalName][0][1]
515                del self.linksIn[signalName]
516                return widget
517
518        return None
519
520    # signal manager calls this function when all input signals have updated the data
521    def processSignals(self):
522        if self.processingHandler: self.processingHandler(self, 1)    # focus on active widget
523        newSignal = 0        # did we get any new signals
524
525        # we define only a way to handle signals that have defined a handler function
526        for signal in self.inputs:        # we go from the first to the last defined input
527            key = signal[0]
528            if self.linksIn.has_key(key):
529                for i in range(len(self.linksIn[key])):
530                    (dirty, widgetFrom, handler, signalData) = self.linksIn[key][i]
531                    if not (handler and dirty): continue
532                    newSignal = 1
533
534                    qApp.setOverrideCursor(QWidget.waitCursor)
535                    try:
536                        for (value, id, nameFrom) in signalData:
537                            if self.signalIsOnlySingleConnection(key):
538                                self.printEvent("ProcessSignals: Calling %s with %s" % (handler, value), eventVerbosity = 2)
539                                handler(value)
540                            else:
541                                self.printEvent("ProcessSignals: Calling %s with %s (%s, %s)" % (handler, value, nameFrom, id), eventVerbosity = 2)
542                                handler(value, (widgetFrom, nameFrom, id))
543                    except:
544                        type, val, traceback = sys.exc_info()
545                        sys.excepthook(type, val, traceback)  # we pretend that we handled the exception, so that we don't crash other widgets
546
547                    qApp.restoreOverrideCursor()
548                    self.linksIn[key][i] = (0, widgetFrom, handler, []) # clear the dirty flag
549
550        if hasattr(self, "handleNewSignals") and newSignal == 1:
551            self.handleNewSignals()
552
553        if self.processingHandler:
554            self.processingHandler(self, 0)    # remove focus from this widget
555        self.needProcessing = 0
556
557    # set new data from widget widgetFrom for a signal with name signalName
558    def updateNewSignalData(self, widgetFrom, signalName, value, id, signalNameFrom):
559        if not self.linksIn.has_key(signalName): return
560        for i in range(len(self.linksIn[signalName])):
561            (dirty, widget, handler, signalData) = self.linksIn[signalName][i]
562            if widget == widgetFrom:
563                if self.linksIn[signalName][i][3] == []:
564                    self.linksIn[signalName][i] = (1, widget, handler, [(value, id, signalNameFrom)])
565                else:
566                    found = 0
567                    for j in range(len(self.linksIn[signalName][i][3])):
568                        (val, ID, nameFrom) = self.linksIn[signalName][i][3][j]
569                        if ID == id and nameFrom == signalNameFrom:
570                            self.linksIn[signalName][i][3][j] = (value, id, signalNameFrom)
571                            found = 1
572                    if not found:
573                        self.linksIn[signalName][i] = (1, widget, handler, self.linksIn[signalName][i][3] + [(value, id, signalNameFrom)])
574        self.needProcessing = 1
575
576
577    # ############################################
578    # PROGRESS BAR FUNCTIONS
579    def progressBarInit(self):
580        self.progressBarValue = 0
581        self.startTime = time.time()
582        self.setCaption(self.captionTitle + " (0% complete)")
583        if self.progressBarHandler:
584            self.progressBarHandler(self, -1)
585
586    def progressBarSet(self, value):
587        if value > 0:
588            self.progressBarValue = value
589            diff = time.time() - self.startTime
590            total = diff * 100.0/float(value)
591            remaining = max(total - diff, 0)
592            h = int(remaining/3600)
593            min = int((remaining - h*3600)/60)
594            sec = int(remaining - h*3600 - min*60)
595            if h > 0:
596                text = "%(h)d h, %(min)d min, %(sec)d sec" % vars()
597            else:
598                text = "%(min)d min, %(sec)d sec" % vars()
599            self.setCaption(self.captionTitle + " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
600        else:
601            self.setCaption(self.captionTitle + " (0% complete)" )
602        if self.progressBarHandler: self.progressBarHandler(self, value)
603        qApp.processEvents()
604
605    def progressBarAdvance(self, value):
606        self.progressBarSet(self.progressBarValue+value)
607
608    def progressBarFinished(self):
609        self.setCaption(self.captionTitle)
610        if self.progressBarHandler: self.progressBarHandler(self, 101)
611
612    # handler must be a function, that receives 2 arguments. First is the widget instance, the second is the value between -1 and 101
613    def setProgressBarHandler(self, handler):
614        self.progressBarHandler = handler
615
616    def setProcessingHandler(self, handler):
617        self.processingHandler = handler
618
619    def setEventHandler(self, handler):
620        self.eventHandler = handler
621
622    def setWidgetStateHandler(self, handler):
623        self.widgetStateHandler = handler
624
625    # if we are in debug mode print the event into the file
626    def printEvent(self, text, eventVerbosity = 1):
627        self.signalManager.addEvent(self.captionTitle[3:] + ": " + text, eventVerbosity = eventVerbosity)
628        if self.eventHandler:
629            self.eventHandler(self.captionTitle[3:] + ": " + text, eventVerbosity)
630
631    def openWidgetHelp(self):
632        if orangedir:
633#            try:
634#                import win32help
635#                if win32help.HtmlHelp(0, "%s/doc/catalog.chm::/catalog/%s/%s.htm" % (orangedir, self.category, self.__class__.__name__[2:]), win32help.HH_DISPLAY_TOPIC):
636#                    return
637#            except:
638#                pass
639
640            try:
641                import webbrowser
642                webbrowser.open("file://%s/doc/widgets/catalog/%s/%s.htm" % (orangedir, self.category, self.__class__.__name__[2:]), 0, 1)
643                return
644            except:
645                pass
646
647        try:
648            import webbrowser
649            webbrowser.open("http://www.ailab.si/orange/doc/widgets/catalog/%s/%s.htm" % (self.category, self.__class__.__name__[2:]))
650            return
651        except:
652            pass
653
654
655    def keyPressEvent(self, e):
656        if e.key() == 0x1030:
657            self.openWidgetHelp()
658#            e.ignore()
659        else:
660            QDialog.keyPressEvent(self, e)
661
662    def information(self, id = 0, text = None):
663        #self.setState("Info", id, text)
664        self.setState("Warning", id, text)      # if we want information just set warning
665
666    def warning(self, id = 0, text = ""):
667        self.setState("Warning", id, text)
668
669    def error(self, id = 0, text = ""):
670        self.setState("Error", id, text)
671
672    def setState(self, stateType, id, text):
673        changed = 0
674        if type(id) == list:
675            for val in id:
676                if self.widgetState[stateType].has_key(val):
677                    self.widgetState[stateType].pop(val)
678                    changed = 1
679        else:
680            if type(id) == str:
681                text = id; id = 0       # if we call information(), warning(), or error() function with only one parameter - a string - then set id = 0
682            if not text:
683                if self.widgetState[stateType].has_key(id):
684                    self.widgetState[stateType].pop(id)
685                    changed = 1
686            else:
687                self.widgetState[stateType][id] = text
688                changed = 1
689
690        if changed:
691            if self.widgetStateHandler:
692                self.widgetStateHandler()
693            elif text: # and stateType != "Info":
694                self.printEvent(stateType + " - " + text)
695            #qApp.processEvents()
696        return changed
697
698    def synchronizeContexts(self):
699        if hasattr(self, "contextHandlers"):
700            for contextName, handler in self.contextHandlers.items():
701                context = self.currentContexts.get(contextName, None)
702                if context:
703                    handler.settingsFromWidget(self, context)
704
705    def openContext(self, contextName="", *arg):
706        if not self._useContexts:
707            return
708        handler = self.contextHandlers[contextName]
709        context = handler.openContext(self, *arg)
710        if context:
711            self.currentContexts[contextName] = context
712
713
714    def closeContext(self, contextName=""):
715        if not self._useContexts:
716            return
717        curcontext = self.currentContexts.get(contextName)
718        if curcontext:
719            self.contextHandlers[contextName].closeContext(self, curcontext)
720            del self.currentContexts[contextName]
721
722    def settingsToWidgetCallback(self, handler, context):
723        pass
724
725    def settingsFromWidgetCallback(self, handler, context):
726        pass
727
728    def setControllers(self, obj, controlledName, controller, prefix):
729        while obj:
730            if prefix:
731#                print "SET CONTROLLERS: %s %s + %s" % (obj.__class__.__name__, prefix, controlledName)
732                if obj.__dict__.has_key("attributeController"):
733                    obj.__dict__["__attributeControllers"][(controller, prefix)] = True
734                else:
735                    obj.__dict__["__attributeControllers"] = {(controller, prefix): True}
736
737            parts = controlledName.split(".", 1)
738            if len(parts) < 2:
739                break
740            obj = getattr(obj, parts[0], None)
741            prefix += parts[0]
742            controlledName = parts[1]
743
744    def __setattr__(self, name, value):
745        return unisetattr(self, name, value, QDialog)
746
747    def randomlyChangeSettings(self, verboseMode = 0):
748        if len(self._guiElements) == 0: return
749
750        try:
751            newValue = ""
752            callback = None
753
754            #index = random.randint(0, len(self._guiElements)-1)
755            index = random.randint(0, len(self._guiElements))
756            if index == len(self._guiElements):
757                elementType = "signalChange"
758                widget = None
759            else:
760                elementType, widget = self._guiElements[index][0], self._guiElements[index][1]
761                if not widget.isEnabled(): return
762
763            if elementType == "signalChange":
764                if len(self.outputs) > 0:
765                    output = self.outputs[random.randint(0, len(self.outputs)-1)][0]
766                    self.send(output, None)
767                    newValue = "Sending None to output signal " + output
768            elif elementType == "qwtPlot":
769                widget.randomChange()
770                newValue = "Random change in qwtPlot"
771            elif elementType == "checkBox":
772                elementType, widget, value, callback = self._guiElements[index]
773                newValue = "Changing checkbox %s to %s" % (value, not self.getdeepattr(value))
774                setattr(self, value, not self.getdeepattr(value))
775            elif elementType == "button":
776                elementType, widget, callback = self._guiElements[index]
777                if widget.isToggleButton():
778                    newValue = "Clicking button %s. State is %d" % (str(widget.text()).strip(), not widget.isOn())
779                    widget.setOn(not widget.isOn())
780                else:
781                    newValue = "Pressed button %s" % (str(widget.text()).strip())
782            elif elementType == "listBox":
783                elementType, widget, value, callback = self._guiElements[index]
784                if widget.count():
785                    itemIndex = random.randint(0, widget.count()-1)
786                    newValue = "Listbox %s. Changed selection of item %d to %s" % (value, itemIndex, not widget.isSelected(itemIndex))
787                    widget.setSelected(itemIndex, not widget.isSelected(itemIndex))
788                else:
789                    callback = None
790            elif elementType == "radioButtonsInBox":
791                elementType, widget, value, callback = self._guiElements[index]
792                radioIndex = random.randint(0, len(widget.buttons)-1)
793                if widget.buttons[radioIndex].isEnabled():
794                    newValue = "Set radio button %s to index %d" % (value, radioIndex)
795                    setattr(self, value, radioIndex)
796                else:
797                    callback = None
798            elif elementType == "radioButton":
799                elementType, widget, value, callback = self._guiElements[index]
800                newValue = "Set radio button %s to %d" % (value, not self.getdeepattr(value))
801                setattr(self, value, not self.getdeepattr(value))
802            elif elementType in ["hSlider", "qwtHSlider", "spin"]:
803                elementType, widget, value, min, max, step, callback = self._guiElements[index]
804                currentValue = self.getdeepattr(value)
805                if currentValue == min:   setattr(self, value, currentValue+step)
806                elif currentValue == max: setattr(self, value, currentValue-step)
807                else:                     setattr(self, value, currentValue + [-step,step][random.randint(0,1)])
808                newValue = "Changed value of %s to %f" % (value, self.getdeepattr(value))
809            elif elementType == "comboBox":
810                elementType, widget, value, sendSelectedValue, valueType, callback = self._guiElements[index]
811                if widget.count():
812                    pos = random.randint(0, widget.count()-1)
813                    newValue = "Changed value of combo %s to %s" % (value, str(widget.text(pos)))
814                    if sendSelectedValue:
815                        setattr(self, value, valueType(str(widget.text(pos))))
816                    else:
817                        setattr(self, value, pos)
818                else:
819                    callback = None
820            if newValue != "":
821                self.printEvent("Widget %s. %s" % (str(self.caption()), newValue), eventVerbosity = 1)
822            if callback:
823                if type(callback) == list:
824                    for c in callback:
825                        c()
826                else:
827                    callback()
828        except:
829            excType, value, tracebackInfo = sys.exc_info()
830            if not self.signalManager.exceptionSeen(type, value, tracebackInfo):
831                sys.stderr.write("------------------\n")
832                if newValue != "":
833                    sys.stderr.write("Widget %s. %s\n" % (str(self.caption()), newValue))
834                sys.excepthook(excType, value, tracebackInfo)  # print the exception
835                sys.stderr.write("Widget settings are:\n")
836                for i, setting in enumerate(getattr(self, "settingsList", [])):
837                    sys.stderr.write("%30s: %7s\n" % (setting, str(self.getdeepattr(setting))))
838
839
840if __name__ == "__main__":
841    a=QApplication(sys.argv)
842    oww=OWBaseWidget()
843    a.setMainWidget(oww)
844    oww.show()
845    a.exec_loop()
846    oww.saveSettings()
Note: See TracBrowser for help on using the repository browser.