source: orange/Orange/OrangeCanvas/orngRegistry.py @ 9671:a7b056375472

Revision 9671:a7b056375472, 9.3 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Moved orange to Orange (part 2)

Line 
1# Author: Gregor Leban (gregor.leban@fri.uni-lj.si)
2#
3
4import os, sys, re, glob, stat
5from orngSignalManager import OutputSignal, InputSignal
6from PyQt4.QtCore import *
7from PyQt4.QtGui import *
8import widgetParser
9
10orangeDir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0]
11if not orangeDir in sys.path:
12    sys.path.append(orangeDir)
13
14import orngEnviron, Orange.misc.addons
15
16class WidgetDescription:
17    def __init__(self, **attrs):
18        self.__dict__.update(attrs)
19
20    def docDir(self):
21        if not self.addOn:  # A built-in widget
22            dir, widgetDir = os.path.realpath(self.directory), os.path.realpath(orngEnviron.widgetDir)
23            subDir = os.path.relpath(dir, widgetDir) if "relpath" in os.path.__dict__ else dir.replace(widgetDir, "")
24            return os.path.join(orngEnviron.orangeDocDir, "widgets", subDir)
25        else:  # An add-on widget
26            addOnDocDir = self.addOn.directory_documentation()
27            return os.path.join(addOnDocDir, "widgets")
28
29
30class WidgetCategory(dict):
31    def __init__(self, name, widgets=None):
32        if widgets:
33            self.update(widgets)
34        self.name = name
35   
36def readCategories(silent=False):
37    currentCacheVersion = 2
38   
39    global widgetsWithError, widgetsWithErrorPrototypes
40    widgetDirName = os.path.realpath(orngEnviron.directoryNames["widgetDir"])
41    canvasSettingsDir = os.path.realpath(orngEnviron.directoryNames["canvasSettingsDir"])
42    cacheFilename = os.path.join(canvasSettingsDir, "cachedWidgetDescriptions.pickle")
43
44    try:
45        import cPickle
46        cacheFile = file(cacheFilename, "rb")
47        cats = cPickle.load(cacheFile)
48        try:
49            version = cPickle.load(cacheFile)
50        except EOFError:
51            version = 0
52        if version == currentCacheVersion:
53            cachedWidgetDescriptions = dict([(w.fullName, w) for cat in cats.values() for w in cat.values()])
54        else:
55            cachedWidgetDescriptions = {}
56    except:
57        cachedWidgetDescriptions = {} 
58
59    directories = [] # tuples (defaultCategory, dirName, plugin, isPrototype)
60    for dirName in os.listdir(widgetDirName):
61        directory = os.path.join(widgetDirName, dirName)
62        if os.path.isdir(directory):
63            directories.append((None, directory, None, "prototypes" in dirName.lower()))
64           
65    # read list of add-ons
66    for addOn in Orange.misc.addons.installed_addons.values() + Orange.misc.addons.registered_addons:
67        addOnWidgetsDir = os.path.join(addOn.directory, "widgets")
68        if os.path.isdir(addOnWidgetsDir):
69            directories.append((addOn.name, addOnWidgetsDir, addOn, False))
70        addOnWidgetsPrototypesDir = os.path.join(addOnWidgetsDir, "prototypes")
71        if os.path.isdir(addOnWidgetsPrototypesDir):
72            directories.append((None, addOnWidgetsPrototypesDir, addOn, True))
73
74    categories = {}     
75    for defCat, dirName, addOn, isPrototype in directories:
76        widgets = readWidgets(dirName, cachedWidgetDescriptions, isPrototype, silent=silent, addOn=addOn, defaultCategory=defCat)
77        for (wName, wInfo) in widgets:
78            catName = wInfo.category
79            if not catName in categories:
80                categories[catName] = WidgetCategory(catName)
81            if wName in categories[catName]:
82                print "Warning! A widget with duplicated name '%s' in category '%s' has been found! It will _not_ be available in the Canvas." % (wName, catName)
83            else:
84                categories[catName][wName] = wInfo
85
86    cacheFile = file(cacheFilename, "wb")
87    cPickle.dump(categories, cacheFile)
88    cPickle.dump(currentCacheVersion, cacheFile)
89    if splashWindow:
90        splashWindow.hide()
91
92    if not silent:
93        if widgetsWithError != []:
94            print "The following widgets could not be imported and will not be available: " + ", ".join(set(widgetsWithError)) + "."
95        if widgetsWithErrorPrototypes != []:
96            print "The following prototype widgets could not be imported and will not be available: " + ", ".join(set(widgetsWithErrorPrototypes)) + "."
97
98    return categories
99
100
101hasErrors = False
102splashWindow = None
103widgetsWithError = []
104widgetsWithErrorPrototypes = []
105
106def readWidgets(directory, cachedWidgetDescriptions, prototype=False, silent=False, addOn=None, defaultCategory=None):
107    import sys, imp
108    global hasErrors, splashWindow, widgetsWithError, widgetsWithErrorPrototypes
109   
110    widgets = []
111   
112    if not defaultCategory:
113        predir, defaultCategory = os.path.split(directory.strip(os.path.sep).strip(os.path.altsep))
114        if defaultCategory == "widgets":
115            defaultCategory = os.path.split(predir.strip(os.path.sep).strip(os.path.altsep))[1]
116   
117    if defaultCategory.lower() == "prototypes" or prototype:
118        defaultCategory = "Prototypes"
119   
120    for filename in glob.iglob(os.path.join(directory, "*.py")):
121        if os.path.isdir(filename):
122            continue
123       
124        datetime = str(os.stat(filename)[stat.ST_MTIME])
125        cachedDescription = cachedWidgetDescriptions.get(filename, None)
126        if cachedDescription and cachedDescription.time == datetime and hasattr(cachedDescription, "inputClasses"):
127            widgets.append((cachedDescription.name, cachedDescription))
128            continue
129       
130        data = file(filename).read()
131        try:
132            meta = widgetParser.WidgetMetaData(data, defaultCategory, enforceDefaultCategory=prototype)
133        except:   # Probably not an Orange widget module.
134            continue
135
136        dirname, fname = os.path.split(filename)
137        widgname = os.path.splitext(fname)[0]
138        try:
139            if not splashWindow:
140                import orngEnviron
141                logo = QPixmap(os.path.join(orngEnviron.directoryNames["canvasDir"], "icons", "splash.png"))
142                splashWindow = QSplashScreen(logo, Qt.WindowStaysOnTopHint)
143                splashWindow.setMask(logo.mask())
144                splashWindow.show()
145               
146            splashWindow.showMessage("Registering widget %s" % meta.name, Qt.AlignHCenter + Qt.AlignBottom)
147            qApp.processEvents()
148           
149            # We import modules using imp.load_source to avoid storing them in sys.modules,
150            # but we need to append the path to sys.path in case the module would want to load
151            # something
152            dirnameInPath = dirname in sys.path
153            if not dirnameInPath:
154                sys.path.append(dirname)
155            wmod = imp.load_source(widgname, filename)
156            if not dirnameInPath and dirname in sys.path: # I have no idea, why we need this, but it seems to disappear sometimes?!
157                sys.path.remove(dirname)
158            widgClass = wmod.__dict__[widgname]
159            inputClasses = set(eval(x[1], wmod.__dict__).__name__ for x in eval(meta.inputList))
160            outputClasses = set(y.__name__ for x in eval(meta.outputList) for y in eval(x[1], wmod.__dict__).mro())
161           
162            widgetInfo = WidgetDescription(
163                             name = meta.name,
164                             time = datetime,
165                             fileName = widgname,
166                             fullName = filename,
167                             directory = directory,
168                             addOn = addOn,
169                             inputList = meta.inputList, outputList = meta.outputList,
170                             inputClasses = inputClasses, outputClasses = outputClasses,
171                             tags=meta.tags
172                             )
173   
174            for attr in ["contact", "icon", "priority", "description", "category"]:
175                setattr(widgetInfo, attr, getattr(meta, attr))
176   
177            # build the tooltip
178            widgetInfo.inputs = [InputSignal(*signal) for signal in eval(widgetInfo.inputList)]
179            if len(widgetInfo.inputs) == 0:
180                formatedInList = "<b>Inputs:</b><br> &nbsp;&nbsp; None<br>"
181            else:
182                formatedInList = "<b>Inputs:</b><br>"
183                for signal in widgetInfo.inputs:
184                    formatedInList += " &nbsp;&nbsp; - " + signal.name + " (" + signal.type + ")<br>"
185   
186            widgetInfo.outputs = [OutputSignal(*signal) for signal in eval(widgetInfo.outputList)]
187            if len(widgetInfo.outputs) == 0:
188                formatedOutList = "<b>Outputs:</b><br> &nbsp; &nbsp; None<br>"
189            else:
190                formatedOutList = "<b>Outputs:</b><br>"
191                for signal in widgetInfo.outputs:
192                    formatedOutList += " &nbsp; &nbsp; - " + signal.name + " (" + signal.type + ")<br>"
193
194            addOnName = "" if not widgetInfo.addOn else " (from add-on %s)" % widgetInfo.addOn.name
195   
196            widgetInfo.tooltipText = "<b><b>&nbsp;%s</b></b>%s<hr><b>Description:</b><br>&nbsp;&nbsp;%s<hr>%s<hr>%s" % (meta.name, addOnName, widgetInfo.description, formatedInList[:-4], formatedOutList[:-4]) 
197            widgets.append((meta.name, widgetInfo))
198        except Exception, msg:
199            if not hasErrors and not silent:
200                print "There were problems importing the following widgets:"
201                hasErrors = True
202            if not silent:
203                print "   %s: %s" % (widgname, msg)
204
205            if not prototype:
206                widgetsWithError.append(widgname)
207            else:
208                widgetsWithErrorPrototypes.append(widgname)
209       
210    return widgets
Note: See TracBrowser for help on using the repository browser.