source: orange/orange/OrangeCanvas/orngSignalManager.py @ 9549:754ecbee6314

Revision 9549:754ecbee6314, 28.6 KB checked in by ales_erjavec <ales.erjavec@…>, 2 years ago (diff)

Fixed some more signal names.

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