source: orange/Orange/OrangeCanvas/orngSignalManager.py @ 10651:4f6fcf57db06

Revision 10651:4f6fcf57db06, 28.7 KB checked in by markotoplak, 2 years ago (diff)

Moved caching, collections, debugging, fileutil, r, testing from misc to utils.

Line 
1# Author: Gregor Leban (gregor.leban@fri.uni-lj.si)
2# Description:
3#    manager, that handles correct processing of widget signals
4#
5
6import sys, os, time
7import logging
8import logging.handlers
9
10import orange
11from Orange.utils import debugging
12
13Single = 2
14Multiple = 4
15
16Default = 8
17NonDefault = 16
18
19Explicit = 32 # Explicit - only connected if specifically requested or the only possibility
20
21Dynamic = 64 #Dynamic output signal
22
23
24class InputSignal(object):
25    def __init__(self, name, signalType, handler, parameters = Single + NonDefault, oldParam = 0):
26        self.name = name
27        self.type = signalType
28        self.handler = handler
29
30        if type(parameters) == str: 
31            parameters = eval(parameters)   # parameters are stored as strings
32        # if we have the old definition of parameters then transform them
33        if parameters in [0,1]:
34            self.single = parameters
35            self.default = not oldParam
36            return
37
38        if not (parameters & Single or parameters & Multiple): parameters += Single
39        if not (parameters & Default or parameters & NonDefault): parameters += NonDefault
40        self.single = parameters & Single
41        self.default = parameters & Default
42        self.explicit = parameters & Explicit
43       
44       
45class OutputSignal(object):
46    def __init__(self, name, signalType, parameters = Single + NonDefault):
47        self.name = name
48        self.type = signalType
49
50        if type(parameters) == str: parameters = eval(parameters)
51        if parameters in [0,1]: # old definition of parameters
52            self.default = not parameters
53            return
54
55        if not (parameters & Default or parameters & NonDefault): parameters += NonDefault
56        self.single = parameters & Single
57        self.default = parameters & Default
58        self.explicit = parameters & Explicit
59       
60        self.dynamic = parameters & Dynamic
61        if self.dynamic and self.single:
62            print "Output signal can not be Multiple and Dynamic"
63            self.dynamic = 0
64           
65           
66def canConnect(output, input, dynamic=False):
67    ret = issubclass(output.type, input.type)
68    if output.dynamic and dynamic:
69        ret = ret or issubclass(input.type,output.type)
70    return ret
71
72
73class SignalLink(object):
74    def __init__(self, widgetFrom, outputSignal, widgetTo, inputSignal, enabled=True):
75        self.widgetFrom = widgetFrom
76        self.widgetTo = widgetTo
77       
78        self.outputSignal = outputSignal
79        self.inputSignal = inputSignal
80       
81        if issubclass(outputSignal.type, inputSignal.type):
82            self.dynamic = False
83        else: 
84            self.dynamic = outputSignal.dynamic
85       
86        self.enabled = enabled
87   
88        self.signalNameFrom = self.outputSignal.name
89        self.signalNameTo = self.inputSignal.name
90       
91   
92    def canEnableDynamic(self, obj):
93        """ Can dynamic signal link be enabled for `obj`?
94        """
95        return isinstance(obj, self.inputSignal.type)
96   
97   
98# class that allows to process only one signal at a time
99class SignalWrapper(object):
100    def __init__(self, widget, method):
101        self.widget = widget
102        self.method = method
103
104    def __call__(self, *k):
105        manager = self.widget.signalManager
106        if not manager:
107            manager = signalManager
108       
109        manager.signalProcessingInProgress += 1
110        try:
111            self.method(*k)
112        finally:
113            manager.signalProcessingInProgress -= 1
114            if not manager.signalProcessingInProgress:
115                manager.processNewSignals(self.widget) 
116
117
118class SignalManager(object):
119    widgets = []    # topologically sorted list of widgets
120    links = {}      # dicionary. keys: widgetFrom, values: [SignalLink, ...]
121    freezing = 0            # do we want to process new signal immediately
122    signalProcessingInProgress = 0 # this is set to 1 when manager is propagating new signal values
123
124    def __init__(self, *args):
125        self.debugFile = None
126        self.verbosity = debugging.orngVerbosity
127        self.stderr = sys.stderr
128        self._seenExceptions = {}
129        self.widgetQueue = []
130        self.asyncProcessingEnabled = False
131       
132        from Orange.utils import environ
133        if not hasattr(self, "log"):
134            SignalManager.log = logging.getLogger("SignalManager")
135            self.logFileName = os.path.join(environ.canvas_settings_dir,
136                "signalManager.log")
137            try:
138                self.log.addHandler(logging.handlers.RotatingFileHandler(self.logFileName, maxBytes=2**20, backupCount=2))
139            except:
140                pass
141            self.log.setLevel(logging.INFO)
142           
143        self.log.info("Signal Manager started")
144       
145        self.stdout = sys.stdout
146       
147        class err(object):
148            def write(myself, str):
149                self.log.error(str[:-1] if str.endswith("\n") else str)
150            def flush(myself):
151                pass
152        self.myerr = err()
153           
154        if debugging.orngDebuggingEnabled:
155            self.debugHandler = logging.FileHandler(debugging.orngDebuggingFileName, mode="wb")
156            self.log.addHandler(self.debugHandler)
157            self.log.setLevel(logging.DEBUG if debugging.orngVerbosity > 0
158            else logging.INFO)
159            sys.excepthook = self.exceptionHandler
160            sys.stderr = self.myerr
161
162    def setDebugMode(self, debugMode = 0, debugFileName = "signalManagerOutput.txt", verbosity = 1):
163        self.verbosity = verbosity
164
165        if debugMode:
166            handler = logging.FileHandler(debugFileName, "wb")
167            self.log.addHandler(handler)
168           
169            sys.excepthook = self.exceptionHandler
170                   
171            sys.stderr = self.myerr
172
173    # ----------------------------------------------------------
174    # ----------------------------------------------------------
175    # DEBUGGING FUNCTION
176
177    def closeDebugFile(self):
178        sys.stderr = self.stderr
179
180    def addEvent(self, strValue, object = None, eventVerbosity = 1):
181        info = str(strValue)
182        if isinstance(object, orange.ExampleTable):
183            name = " " + getattr(object, "name", "")
184            info += ". Token type = ExampleTable" + name + ". len = " + str(len(object))
185        elif type(object) == list:
186            info += ". Token type = %s. Value = %s" % (str(type(object)), str(object[:10]))
187        elif object != None:
188            info += ". Token type = %s. Value = %s" % (str(type(object)), str(object)[:100])
189        if eventVerbosity > 0:
190            self.log.debug(info)
191        else:
192            self.log.info(info)
193
194
195    def exceptionSeen(self, type, value, tracebackInfo):
196        import traceback, os
197        shortEStr = "".join(traceback.format_exception(type, value, tracebackInfo))[-2:]
198        return self._seenExceptions.has_key(shortEStr)
199
200    def exceptionHandler(self, type, value, tracebackInfo):
201        import traceback, os, StringIO
202
203        # every exception show only once
204        shortEStr = "".join(traceback.format_exception(type, value, tracebackInfo))[-2:]
205        if self._seenExceptions.has_key(shortEStr):
206            return
207        self._seenExceptions[shortEStr] = 1
208       
209        list = traceback.extract_tb(tracebackInfo, 10)
210        space = "\t"
211        totalSpace = space
212        message = StringIO.StringIO()
213        message.write("Unhandled exception of type %s\n" % ( str(type)))
214        message.write("Traceback:\n")
215
216        for i, (file, line, funct, code) in enumerate(list):
217            if not code:
218                continue
219            message.write(totalSpace + "File: " + os.path.split(file)[1] + " in line %4d\n" %(line))
220            message.write(totalSpace + "Function name: %s\n" % (funct))
221            message.write(totalSpace + "Code: " + code + "\n")
222            totalSpace += space
223
224        message.write(totalSpace[:-1] + "Exception type: " + str(type) + "\n")
225        message.write(totalSpace[:-1] + "Exception value: " + str(value)+ "\n")
226        self.log.error(message.getvalue())
227#        message.flush()
228
229    # ----------------------------------------------------------
230    # ----------------------------------------------------------
231
232    # freeze/unfreeze signal processing. If freeze=1 no signal will be processed until freeze is set back to 0
233    def setFreeze(self, freeze, startWidget = None):
234        """ Freeze/unfreeze signal processing. If freeze=1 no signal will be
235        processed until freeze is set back to 0
236       
237        """
238        self.freezing = max(freeze, 0)
239        if freeze > 0:
240            self.addEvent("Freezing signal processing (%s)" % str(freeze), startWidget)
241        elif freeze == 0:
242            self.addEvent("Unfreezing signal processing", startWidget)
243        else:
244            self.addEvent("Invalid freeze value! (by %s)", startWidget, eventVerbosity=0)
245           
246        if self.freezing == 0 and self.widgets != []:
247            self.processNewSignals(self.widgets[0]) # always start processing from the first
248#            if startWidget:
249#                self.processNewSignals(startWidget)
250#            else:
251#                self.processNewSignals(self.widgets[0])
252
253    def addWidget(self, widget):
254        """ Add `widget` to the `widgets` list
255        """
256       
257        self.addEvent("Added widget " + widget.captionTitle, eventVerbosity = 2)
258
259        if widget not in self.widgets:
260            self.widgets.append(widget)
261#            widget.connect(widget, SIGNAL("blockingStateChanged(bool)"), self.onStateChanged)
262
263    def removeWidget(self, widget):
264        """ Remove widget from the `widgets` list
265        """
266#        if self.verbosity >= 2:
267        self.addEvent("Remove widget " + widget.captionTitle, eventVerbosity = 2)
268        self.widgets.remove(widget)
269        if widget in self.links:
270            del self.links[widget]
271
272    def getLinks(self, widgetFrom=None, widgetTo=None, signalNameFrom=None, signalNameTo=None):
273        """ Return a list of matching SignalLinks
274        """
275        links = []
276        if widgetFrom is None:
277            widgets = self.widgets # search all widgets
278        else:
279            widgets = [widgetFrom]
280        for w in widgets:
281            for link in self.links.get(w, []):
282                if (widgetFrom is None or widgetFrom is link.widgetFrom) and \
283                   (widgetTo is None or widgetTo is link.widgetTo) and \
284                   (signalNameFrom is None or signalNameFrom == link.signalNameFrom) and \
285                   (signalNameTo is None or signalNameTo == link.signalNameTo):
286                        links.append(link)
287                   
288        return links
289
290    def getLinkWidgetsIn(self, widget, signalName):
291        """ Return a list of widgets that connect to `widget`'s input `signalName`
292        """
293        links = self.getLinks(None, widget, None, signalName)
294        return [link.widgetFrom for link in links]
295
296
297    def getLinkWidgetsOut(self, widget, signalName):
298        """ Return a list of widgets that connect to `widget`'s output `signalName`
299        """
300        links = self.getLinks(widget, None, signalName, None)
301        return [link.widgetTo for link in links]
302   
303   
304   
305    def canConnect(self, widgetFrom, widgetTo, dynamic=True):
306        # TODO: This should be retrieved from orngRegistry.WidgetDescription
307        outsignals = [OutputSignal(*tt) for tt in widgetFrom.outputs]
308        insignals = [InputSignal(*tt) for tt in widgetTo.inputs]
309       
310        return any(canConnect(out, in_, dynamic) for out in outsignals for in_ in insignals)
311       
312   
313    def proposePossibleLinks(self, widgetFrom, widgetTo, dynamic=True):
314        """ Return a ordered list of (OutputSignal, InputSignal, weight) tuples that
315        can connect both widgets
316        """
317        outSignals = [OutputSignal(*tt) for tt in widgetFrom.outputs]
318        inSignals = [InputSignal(*tt) for tt in widgetTo.inputs]
319       
320        # Get signals that are Single links and already connected to input widget
321        links = self.getLinks(None, widgetTo)
322        alreadyConnected = [link.signalNameTo for link in links if link.inputSignal.single]
323       
324        def weight(outS, inS):
325            if outS.explicit or inS.explicit:
326                # Zero weight for explicit signals
327                weight = 0
328            else:
329                check = [not outS.dynamic, inS.name not in alreadyConnected, bool(inS.default), bool(outS.default)] #Dynamic signals are lasts
330                weights = [2**i for i in range(len(check), 0, -1)]
331                weight = sum([w for w, c in zip(weights, check) if c])
332            return weight
333       
334        possibleLinks = []
335        for outS in outSignals:
336            for inS in inSignals:
337                if canConnect(outS, inS, dynamic):
338                    possibleLinks.append((outS, inS, weight(outS, inS)))
339       
340        return sorted(possibleLinks, key=lambda link: link[-1], reverse=True)
341       
342   
343    def inputSignal(self, widget, name):
344        for tt in widget.inputs:
345            if tt[0] == name:
346                return InputSignal(*tt)
347           
348    def outputSignal(self, widget, name):
349        for tt in widget.outputs:
350            if tt[0] == name:
351                return OutputSignal(*tt)
352           
353
354    def addLink(self, widgetFrom, widgetTo, signalNameFrom, signalNameTo, enabled):
355        self.addEvent("Add link from " + widgetFrom.captionTitle + " to " + widgetTo.captionTitle, eventVerbosity = 2)
356
357        ## would this link create a cycle
358        if self.existsPath(widgetTo, widgetFrom): 
359            return 0
360        # check if signal names still exist
361        found = 0
362        output_names = [t[0] for t in widgetFrom.outputs]
363        found = signalNameFrom in output_names
364       
365        if not found:
366            if signalNameFrom in _CHANNEL_NAME_MAP and \
367                    _CHANNEL_NAME_MAP[signalNameFrom] in output_names:
368                self.addEvent("Widget changed its output signal  %r name. Changed to %r." % (signalNameFrom, _CHANNEL_NAME_MAP[signalNameFrom]),
369                              eventVerbosity=1)
370                signalNameFrom = _CHANNEL_NAME_MAP[signalNameFrom]
371                found = 1
372               
373        if not found:
374            print "Error. Widget %s changed its output signals. It does not have signal %s anymore." % (str(getattr(widgetFrom, "captionTitle", "")), signalNameFrom)
375            return 0
376
377        found = 0
378        input_names = [t[0] for t in widgetTo.inputs]
379        found = signalNameTo in input_names
380       
381        if not found:
382            if signalNameTo in _CHANNEL_NAME_MAP and \
383                    _CHANNEL_NAME_MAP[signalNameTo] in input_names:
384                self.addEvent("Widget changed its input signal  %r name. Changed to %r." % (signalNameFrom, _CHANNEL_NAME_MAP[signalNameTo]),
385                              eventVerbosity=1)
386                signalNameTo = _CHANNEL_NAME_MAP[signalNameTo]
387                found = 1
388               
389        if not found:
390            print "Error. Widget %s changed its input signals. It does not have signal %s anymore." % (str(getattr(widgetTo, "captionTitle", "")), signalNameTo)
391            return 0
392
393        if self.links.has_key(widgetFrom):
394            if self.getLinks(widgetFrom, widgetTo, signalNameFrom, signalNameTo):
395                print "connection ", widgetFrom, " to ", widgetTo, " alread exists. Error!!"
396                return
397
398        link = SignalLink(widgetFrom, self.outputSignal(widgetFrom, signalNameFrom),
399                          widgetTo, self.inputSignal(widgetTo, signalNameTo), enabled=enabled)
400        self.links[widgetFrom] = self.links.get(widgetFrom, []) + [link]
401
402        widgetTo.addInputConnection(widgetFrom, signalNameTo)
403
404        # if there is no key for the signalNameFrom, create it and set its id=None and data = None
405        if not widgetFrom.linksOut.has_key(signalNameFrom):
406            widgetFrom.linksOut[signalNameFrom] = {None:None}
407
408        # if channel is enabled, send data through it
409        if enabled:
410            self.pushAllOnLink(link)
411           
412        # reorder widgets if necessary
413        if self.widgets.index(widgetFrom) > self.widgets.index(widgetTo):
414            self.fixTopologicalOrdering()
415#            self.widgets.remove(widgetTo)
416#            self.widgets.append(widgetTo)   # appent the widget at the end of the list
417#            self.fixPositionOfDescendants(widgetTo)
418           
419        return 1
420
421    # fix position of descendants of widget so that the order of widgets in self.widgets is consistent with the schema
422    def fixPositionOfDescendants(self, widget):
423        for link in self.links.get(widget, []):
424            widgetTo = link.widgetTo
425            self.widgets.remove(widgetTo)
426            self.widgets.append(widgetTo)
427            self.fixPositionOfDescendants(widgetTo)
428           
429    def fixTopologicalOrdering(self):
430        """ fix the widgets topological ordering
431        """
432        order = []
433        visited = set()
434        queue = sorted([w for w in self.widgets if not self.getLinks(None, w)]) 
435        while queue:
436            w = queue.pop(0)
437            order.append(w)
438            visited.add(w)
439            linked = set([link.widgetTo for link in self.getLinks(w)])
440            queue.extend(sorted(linked.difference(queue)))
441        self.widgets[:] = order
442           
443
444    def findSignals(self, widgetFrom, widgetTo):
445        """ Return a list of (outputName, inputName) for links between widgets
446        """
447        links = self.getLinks(widgetFrom, widgetTo)
448        return [(link.signalNameFrom, link.signalNameTo) for link in links]
449   
450
451    def isSignalEnabled(self, widgetFrom, widgetTo, signalNameFrom, signalNameTo):
452        """ Is signal enabled
453        """
454        links = self.getLinks(widgetFrom, widgetTo, signalNameFrom, signalNameTo)
455        if links:
456            return links[0].enabled
457        else:
458            return False
459       
460
461    def removeLink(self, widgetFrom, widgetTo, signalNameFrom, signalNameTo):
462        """ Remove link
463        """
464        self.addEvent("Remove link from " + widgetFrom.captionTitle + " to " + widgetTo.captionTitle, eventVerbosity = 2)
465
466        # no need to update topology, just remove the link
467        if self.links.has_key(widgetFrom):
468            links = self.getLinks(widgetFrom, widgetTo, signalNameFrom, signalNameTo)
469            if len(links) != 1:
470                print "Error removing a link with none or more then one entries"
471                return
472               
473            link = links[0]
474            self.purgeLink(link)
475           
476            self.links[widgetFrom].remove(link)
477            if not self.freezing and not self.signalProcessingInProgress: 
478                self.processNewSignals(widgetFrom)
479        widgetTo.removeInputConnection(widgetFrom, signalNameTo)
480
481
482    # ############################################
483    # ENABLE OR DISABLE LINK CONNECTION
484
485    def setLinkEnabled(self, widgetFrom, widgetTo, enabled, justSend = False):
486        """ Set `enabled` state for links between widgets.
487        """
488        for link in self.getLinks(widgetFrom, widgetTo):
489            if not justSend:
490                link.enabled = enabled
491            if enabled:
492                self.pushAllOnLink(link)
493               
494        if enabled:
495            self.processNewSignals(widgetTo)
496
497
498    def getLinkEnabled(self, widgetFrom, widgetTo):
499        """ Is any link between widgets enabled
500        """
501        return any(link.enabled for link in self.getLinks(widgetFrom, widgetTo))
502
503
504    # widget widgetFrom sends signal with name signalName and value value
505    def send(self, widgetFrom, signalNameFrom, value, id):
506        """ Send signal `signalNameFrom` from `widgetFrom` with `value` and `id`
507        """
508        # add all target widgets new value and mark them as dirty
509        # if not freezed -> process dirty widgets
510        self.addEvent("Send data from " + widgetFrom.captionTitle + ". Signal = " + signalNameFrom, value, eventVerbosity = 2)
511
512        if not self.links.has_key(widgetFrom):
513            return
514       
515        for link in self.getLinks(widgetFrom, None, signalNameFrom, None):
516            self.pushToLink(link, value, id)
517
518        if not self.freezing and not self.signalProcessingInProgress:
519            self.processNewSignals(widgetFrom)
520
521    # when a new link is created, we have to
522    def sendOnNewLink(self, widgetFrom, widgetTo, signals):
523        for (signalNameFrom, signalNameTo) in signals:
524            for link in self.getLinks(widgetFrom, widgetTo, signalNameFrom, signalNameTo):
525                self.pushAllOnLink(link)
526
527
528    def pushAllOnLink(self, link):
529        """ Send all data on link
530        """
531        for key in link.widgetFrom.linksOut[link.signalNameFrom].keys():
532            self.pushToLink(link, link.widgetFrom.linksOut[link.signalNameFrom][key], key)
533
534
535    def purgeLink(self, link):
536        """ Clear all data on link (i.e. send None for all keys)
537        """
538        for key in link.widgetFrom.linksOut[link.signalNameFrom].keys():
539            self.pushToLink(link, None, key)
540           
541    def pushToLink(self, link, value, id):
542        """ Send value with id on link
543        """
544        if link.enabled:
545            if link.dynamic:
546                dyn_enable = link.canEnableDynamic(value)
547                self.setDynamicLinkEnabled(link, dyn_enable)
548                if not dyn_enable:
549                    value = None
550            link.widgetTo.updateNewSignalData(link.widgetFrom, link.signalNameTo, 
551                                          value, id, link.signalNameFrom)
552
553    def processNewSignals(self, firstWidget=None):
554        """ Process new signals starting from `firstWidget`
555        """
556       
557        if len(self.widgets) == 0 or self.signalProcessingInProgress or self.freezing:
558            return
559
560        if firstWidget not in self.widgets or self.widgetQueue:
561            firstWidget = self.widgets[0]   # if some window that is not a widget started some processing we have to process new signals from the first widget
562           
563        self.addEvent("Process new signals starting from " + firstWidget.captionTitle, eventVerbosity = 2)
564
565        skipWidgets = self.getBlockedWidgets() # Widgets that are blocking
566       
567       
568        # start propagating
569        self.signalProcessingInProgress = 1
570       
571        index = self.widgets.index(firstWidget)
572        for i in range(index, len(self.widgets)):
573            if self.widgets[i] in skipWidgets:
574                continue
575               
576            while self.widgets[i] in self.widgetQueue:
577                self.widgetQueue.remove(self.widgets[i])
578            if self.widgets[i].needProcessing:
579                self.addEvent("Processing " + self.widgets[i].captionTitle)
580                try:
581                    self.widgets[i].processSignals()
582                except Exception:
583                    type, val, traceback = sys.exc_info()
584                    sys.excepthook(type, val, traceback)  # we pretend that we handled the exception, so that it doesn't crash canvas
585                   
586                if self.widgets[i].isBlocking():
587                    if not self.asyncProcessingEnabled:
588                        self.addEvent("Widget %s blocked during signal processing. Aborting." % self.widgets[i].captionTitle)
589                        break
590                    else:
591                        self.addEvent("Widget %s blocked during signal processing." % self.widgets[i].captionTitle)           
592                   
593                    # If during signal processing the widget changed state to
594                    # blocking we skip all of its descendants
595                    skipWidgets.update(self.widgetDescendants(self.widgets[i]))
596            if self.freezing:
597                self.addEvent("Signals frozen during processing of " + self.widgets[i].captionTitle + ". Aborting.")
598                break
599       
600        # we finished propagating
601        self.signalProcessingInProgress = 0
602       
603        if self.widgetQueue:
604            # if there are still some widgets on queue
605            self.processNewSignals(None)
606       
607    def scheduleSignalProcessing(self, widget=None):
608        self.widgetQueue.append(widget)
609        self.processNewSignals(widget)
610
611
612    def existsPath(self, widgetFrom, widgetTo):
613        """ Is there a path between `widgetFrom` and `widgetTo`
614        """
615        # is there a direct link
616        if not self.links.has_key(widgetFrom):
617            return 0
618
619        for link in self.links[widgetFrom]:
620            if link.widgetTo == widgetTo:
621                return 1
622
623        # is there a nondirect link
624        for link in self.links[widgetFrom]:
625            if self.existsPath(link.widgetTo, widgetTo):
626                return 1
627
628        # there is no link...
629        return 0
630   
631
632    def widgetDescendants(self, widget):
633        """ Return all widget descendants of `widget`
634        """
635        queue = [widget]
636        queue_set = set(queue)
637       
638        index = self.widgets.index(widget)
639        for i in range(index, len(self.widgets)):
640            widget = self.widgets[i]
641            if widget not in queue:
642                continue
643            linked = [link.widgetTo for link in self.links.get(widget, []) if link.enabled]
644            for w in linked:
645                if w not in queue_set:
646                    queue.append(widget)
647                    queue_set.add(widget)
648        return queue
649   
650   
651    def isWidgetBlocked(self, widget):
652        """ Is this widget or any of its up-stream connected widgets blocked.
653        """
654        if widget.isBlocking():
655            return True
656        else:
657            widgets = [link.widgetFrom for link in self.getLinks(None, widget, None, None)]
658            if widgets:
659                return any(self.isWidgetBlocked(w) for w in widgets)
660            else:
661                return False
662           
663           
664    def getBlockedWidgets(self):
665        """ Return a set of all widgets that are blocked.
666        """
667        blocked = set()
668        for w in self.widgets:
669            if w not in blocked and w.isBlocking():
670                blocked.update(self.widgetDescendants(w))
671        return blocked
672   
673               
674    def freeze(self, widget=None):
675        """ Return a context manager that freezes the signal processing
676        """
677        signalManager = self
678        class freezer(object):
679            def __enter__(self):
680                self.push()
681                return self
682           
683            def __exit__(self, *args):
684                self.pop()
685               
686            def push(self):
687                signalManager.setFreeze(signalManager.freezing + 1)
688               
689            def pop(self):
690                signalManager.setFreeze(signalManager.freezing - 1, widget)
691               
692        return freezer()
693   
694    def setDynamicLinkEnabled(self, link, enabled):
695        import PyQt4.QtCore as QtCore
696        link.widgetFrom.emit(QtCore.SIGNAL("dynamicLinkEnabledChanged(PyQt_PyObject, bool)"), link, enabled)
697       
698
699# Channel renames.
700
701_CHANNEL_NAME_MAP = \
702    {'Additional Tables': 'Additional Data',
703     'Attribute Definitions': 'Feature Definitions',
704     'Attribute List': 'Features',
705     'Attribute Pair': 'Interacting Features',
706     'Attribute Selection List': 'Features',
707     'Attribute Statistics': 'Feature Statistics',
708     'Attribute selection': 'Features',
709     'Attributes': 'Features',
710     'Choosen Tree': 'Selected Tree',
711     'Covered Examples': 'Covered Data',
712     'Data Instances': 'Data',
713     'Data Table': 'Data',
714     'Distance Matrix': 'Distances',
715     'Distance matrix': 'Distances',
716     'Distances btw. Instances': 'Distances',
717     'Example Subset': 'Data Subset',
718     'Example Table': 'Data',
719     'Examples': 'Data',
720     'Examples A': 'Data A',
721     'Examples B': 'Data B',
722     'Examples with Z-scores': 'Data with z-score',
723     'Graph with ExampleTable': 'Graph with Data',
724     'Input Data': 'Data',
725     'Input Table': 'Data',
726     'Instances': 'Data',
727     'Items Distance Matrix': 'Distances',
728     'Items Subset': 'Item Subset',
729     'Items to Mark': 'Marked Items',
730     'KNN Classifier': 'kNN Classifier',
731     'Marked Examples': 'Marked Data',
732     'Matching Examples': 'Matching Data',
733     'Merged Examples A+B': 'Merged Data A+B',
734     'Merged Examples B+A': 'Merged Data B+A',
735     'Mismatching Examples': 'Mismatched Data',
736     'Non-Matching Examples': 'Unmatched Data',
737     'Output Data': 'Data',
738     'Output Table': 'Data',
739     'Preprocessed Example Table': 'Preprocessed Data',
740     'Primary Table': 'Primary Data',
741     'Reduced Example Table': 'Reduced Data',
742     'Remaining Examples': 'Remaining Data',
743     'SOMMap': 'SOM',
744     'Sample': 'Data Sample',
745     'Selected Attributes List': 'Selected Features',
746     'Selected Examples': 'Selected Data',
747     'Selected Instances': 'Selected Data',
748     'Selected Items Distance Matrix': 'Distance Matrix',
749     'Shuffled Data Table': 'Shuffled Data',
750     'Train Data': 'Training Data',
751     'Training data': 'Data',
752     'Unselected Examples': 'Other Data',
753     'Unselected Items': 'Other Items',
754     }   
755
756# create a global instance of signal manager
757globalSignalManager = SignalManager()
758
Note: See TracBrowser for help on using the repository browser.