source: orange/Orange/OrangeWidgets/Data/OWFile.py @ 11096:cf7d2ae9d22b

Revision 11096:cf7d2ae9d22b, 14.9 KB checked in by Ales Erjavec <ales.erjavec@…>, 19 months ago (diff)

Added new svg icons for the widgets/categories.

Line 
1"""
2<name>File</name>
3<description>Reads data from a file.</description>
4<icon>icons/File.svg</icon>
5<contact>Janez Demsar (janez.demsar(@at@)fri.uni-lj.si)</contact>
6<priority>10</priority>
7"""
8
9# Don't move this - the line number of the call is important
10def call(f,*args,**keyargs):
11    return f(*args, **keyargs)
12
13from OWWidget import *
14import OWGUI, string, os, sys, warnings
15import orngIO
16
17warnings.filterwarnings("error", ".*" , orange.KernelWarning, "OWFile", 11)
18
19class FileNameContextHandler(ContextHandler):
20    def match(self, context, imperfect, filename):
21        return context.filename == filename and 2
22
23
24def addOrigin(examples, filename):
25    vars = examples.domain.variables + examples.domain.getmetas().values()
26    strings = [var for var in vars if isinstance(var, orange.StringVariable)]
27    dirname, basename = os.path.split(filename)
28    for var in strings:
29        if "type" in var.attributes and "origin" not in var.attributes:
30            var.attributes["origin"] = dirname
31
32
33NAME = "File"
34DESCRIPTION = "Reads a data table from a file."
35
36LONG_DESCRIPTION = """Read a data table from a supported file format on
37the the filesystem and send it to the the output."""
38
39ICON = "icons/File.svg"
40AUTHOR = "Janez Demsar"
41MAINTAINER_EMAIL = "janez.demsar(@at@)fri.uni-lj.si"
42PRIORITY = 10
43CATEGORY = "Data"
44HELP = "docs/html/data/file.html"
45
46OUTPUTS = [("Data", orange.ExampleTable,)]
47
48WIDGET_CLASS = "OWFile"
49
50
51class OWFile(OWWidget):
52    settingsList=["recentFiles", "createNewOn", "showAdvanced"]
53    contextHandlers = {"": FileNameContextHandler()}
54
55    registeredFileTypes = [ft for ft in orange.getRegisteredFileTypes() if len(ft)>2 and ft[2]]
56    dlgFormats = 'Tab-delimited files (*.tab *.txt)\nC4.5 files (*.data)\nAssistant files (*.dat)\nRetis files (*.rda *.rdo)\nBasket files (*.basket)\n' \
57                 + "\n".join("%s (%s)" % (ft[:2]) for ft in registeredFileTypes) \
58                 + "\nAll files(*.*)"
59                 
60    formats = {".tab": "Tab-delimited file", ".txt": "Tab-delimited file", ".data": "C4.5 file",
61               ".dat": "Assistant file", ".rda": "Retis file", ".rdo": "Retis file",
62               ".basket": "Basket file"}
63    formats.update(dict((ft[1][2:], ft[0]) for ft in registeredFileTypes))
64     
65                 
66    def __init__(self, parent=None, signalManager = None):
67        OWWidget.__init__(self, parent, signalManager, "File", wantMainArea = 0, resizingEnabled = 1)
68
69        self.inputs = []
70        self.outputs = [("Data", ExampleTable)]
71
72        self.recentFiles=["(none)"]
73        self.symbolDC = "?"
74        self.symbolDK = "~"
75        self.createNewOn = 1
76        self.domain = None
77        self.loadedFile = ""
78        self.showAdvanced = 0
79        self.loadSettings()
80
81        box = OWGUI.widgetBox(self.controlArea, "Data File", addSpace = True, orientation=0)
82        self.filecombo = QComboBox(box)
83        self.filecombo.setMinimumWidth(150)
84        box.layout().addWidget(self.filecombo)
85        button = OWGUI.button(box, self, '...', callback = self.browseFile, disabled=0)
86        button.setIcon(self.style().standardIcon(QStyle.SP_DirOpenIcon))
87        button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
88       
89        self.reloadBtn = OWGUI.button(box, self, "Reload", callback = self.reload, default=True)
90        self.reloadBtn.setIcon(self.style().standardIcon(QStyle.SP_BrowserReload))
91        self.reloadBtn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
92       
93        box = OWGUI.widgetBox(self.controlArea, "Info", addSpace = True)
94        self.infoa = OWGUI.widgetLabel(box, 'No data loaded.')
95        self.infob = OWGUI.widgetLabel(box, ' ')
96        self.warnings = OWGUI.widgetLabel(box, ' ')
97       
98        #Set word wrap so long warnings won't expand the widget
99        self.warnings.setWordWrap(True)
100        self.warnings.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
101       
102        smallWidget = OWGUI.collapsableWidgetBox(self.controlArea, "Advanced settings", self, "showAdvanced", callback=self.adjustSize0)
103       
104        box = OWGUI.widgetBox(smallWidget, "Missing Value Symbols")
105#        OWGUI.widgetLabel(box, "Symbols for missing values in tab-delimited files (besides default ones)")
106       
107        hbox = OWGUI.indentedBox(box)
108        OWGUI.lineEdit(hbox, self, "symbolDC", "Don't care:", labelWidth=80, orientation="horizontal", tooltip="Default values: '~' or '*'")
109        OWGUI.lineEdit(hbox, self, "symbolDK", "Don't know:", labelWidth=80, orientation="horizontal", tooltip="Default values: empty fields (space), '?' or 'NA'")
110
111        smallWidget.layout().addSpacing(8)
112        OWGUI.radioButtonsInBox(smallWidget, self, "createNewOn", box="New Attributes",
113                       label = "Create a new attribute when existing attribute(s) ...",
114                       btnLabels = ["Have mismatching order of values",
115                                    "Have no common values with the new (recommended)",
116                                    "Miss some values of the new attribute",
117                                    "... Always create a new attribute"
118                               ])
119       
120        OWGUI.rubber(smallWidget)
121        smallWidget.updateControls()
122       
123        OWGUI.rubber(self.controlArea)
124       
125        # remove missing data set names
126        def exists(path):
127            if not os.path.exists(path):
128                dirpath, basename = os.path.split(path)
129                return os.path.exists(os.path.join("./", basename))
130            else:
131                return True
132        self.recentFiles = filter(exists, self.recentFiles)
133        self.setFileList()
134
135        if len(self.recentFiles) > 0 and exists(self.recentFiles[0]):
136            self.openFile(self.recentFiles[0], 0, self.symbolDK, self.symbolDC)
137
138        self.connect(self.filecombo, SIGNAL('activated(int)'), self.selectFile)
139
140    def adjustSize0(self):
141        qApp.processEvents()
142        QTimer.singleShot(50, self.adjustSize)
143
144    def setFileList(self):
145        self.filecombo.clear()
146        if not self.recentFiles:
147            self.filecombo.addItem("(none)")
148        for file in self.recentFiles:
149            if file == "(none)":
150                self.filecombo.addItem("(none)")
151            else:
152                self.filecombo.addItem(os.path.split(file)[1])
153        self.filecombo.addItem("Browse documentation data sets...")
154       
155
156    def reload(self):
157        if self.recentFiles:
158            return self.openFile(self.recentFiles[0], 1, self.symbolDK, self.symbolDC)
159
160
161    def settingsFromWidgetCallback(self, handler, context):
162        context.filename = self.loadedFile
163        context.symbolDC, context.symbolDK = self.symbolDC, self.symbolDK
164
165    def settingsToWidgetCallback(self, handler, context):
166        self.symbolDC, self.symbolDK = context.symbolDC, context.symbolDK
167
168    def selectFile(self, n):
169        if n < len(self.recentFiles) :
170            name = self.recentFiles[n]
171            self.recentFiles.remove(name)
172            self.recentFiles.insert(0, name)
173        elif n:
174            self.browseFile(1)
175
176        if len(self.recentFiles) > 0:
177            self.setFileList()
178            self.openFile(self.recentFiles[0], 0, self.symbolDK, self.symbolDC)
179
180    def browseFile(self, inDemos=0):
181        "Display a FileDialog and select a file"
182        if inDemos:
183            try:
184                import orngConfiguration
185                startfile = orngConfiguration.datasetsPath
186            except:
187                startfile = ""
188               
189            if not startfile or not os.path.exists(startfile):
190                try:
191                    import win32api, win32con
192                    t = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SOFTWARE\\Python\\PythonCore\\%i.%i\\PythonPath\\Orange" % sys.version_info[:2], 0, win32con.KEY_READ)
193                    t = win32api.RegQueryValueEx(t, "")[0]
194                    startfile = t[:t.find("orange")] + "orange\\doc\\datasets"
195                except:
196                    startfile = ""
197
198            if not startfile or not os.path.exists(startfile):
199                widgetsdir = os.path.dirname(OWGUI.__file__)
200                orangedir = os.path.dirname(widgetsdir)
201                startfile = os.path.join(orangedir, "doc", "datasets")
202
203            if not startfile or not os.path.exists(startfile):
204                d = os.getcwd()
205                if os.path.basename(d) == "OrangeCanvas":
206                    startfile = os.path.join(os.path.dirname(d), "doc", "datasets")
207                else:
208                    startfile = os.path.join(d, "doc", "datasets")
209
210            if not os.path.exists(startfile):
211                QMessageBox.information( None, "File", "Cannot find the directory with example data sets", QMessageBox.Ok + QMessageBox.Default)
212                return
213        else:
214            if len(self.recentFiles) == 0 or self.recentFiles[0] == "(none)":
215                startfile = os.path.expanduser("~/")
216            else:
217                startfile = self.recentFiles[0]
218
219        filename = QFileDialog.getOpenFileName(self, 'Open Orange Data File', startfile, self.dlgFormats)
220        filename = unicode(filename)
221       
222        if filename == "":
223            return
224       
225        if filename in self.recentFiles:
226            self.recentFiles.remove(filename)
227        self.recentFiles.insert(0, filename)
228        self.setFileList()
229
230        self.openFile(self.recentFiles[0], 0, self.symbolDK, self.symbolDC)
231
232
233    # Open a file, create data from it and send it over the data channel
234    def openFile(self, fn, throughReload, DK=None, DC=None):
235        if self.processingHandler: 
236            self.processingHandler(self, 1)    # focus on active widget
237        self.error()
238        self.warning()
239        self.information()
240       
241        if not os.path.exists(fn):
242            dirname, basename = os.path.split(fn)
243            if os.path.exists(os.path.join("./", basename)):
244                fn = os.path.join("./", basename)
245                self.information("Loading '%s' from the current directory." % basename)
246
247        self.closeContext()
248        self.loadedFile = ""
249       
250        if fn == "(none)":
251            self.send("Data", None)
252            self.infoa.setText("No data loaded")
253            self.infob.setText("")
254            self.warnings.setText("")
255            return
256           
257        self.symbolDK = self.symbolDC = ""
258        self.openContext("", fn)
259
260        self.loadedFile = ""
261
262        argdict = {"createNewOn": 3-self.createNewOn}
263        if DK:
264            argdict["DK"] = str(DK)
265        if DC:
266            argdict["DC"] = str(DC)
267
268        data = None
269        try:
270            data = call(orange.ExampleTable, fn, **argdict)
271            self.loadedFile = fn
272        except Exception, (errValue):
273            if "is being loaded as" in str(errValue):
274                try:
275                    data = orange.ExampleTable(fn, **argdict)
276                    self.warning(0, str(errValue))
277                except:
278                    pass
279            if data is None:
280                self.error(str(errValue))
281                self.dataDomain = None
282                self.infoa.setText('Data was not loaded due to an error.')
283                self.infob.setText('Error:')
284                self.warnings.setText(str(errValue))
285                if self.processingHandler: self.processingHandler(self, 0)    # remove focus from this widget
286                return
287                       
288        self.dataDomain = data.domain
289
290        self.infoa.setText('%d example(s), ' % len(data) + '%d attribute(s), ' % len(data.domain.attributes) + '%d meta attribute(s).' % len(data.domain.getmetas()))
291        cl = data.domain.classVar
292        if cl:
293            if cl.varType == orange.VarTypes.Continuous:
294                    self.infob.setText('Regression; Numerical class.')
295            elif cl.varType == orange.VarTypes.Discrete:
296                    self.infob.setText('Classification; Discrete class with %d value(s).' % len(cl.values))
297            else:
298                self.infob.setText("Class is neither discrete nor continuous.")
299        else:
300            self.infob.setText("Data has no dependent variable.")
301
302        warnings = ""
303        metas = data.domain.getmetas()
304        if hasattr(data, "attribute_load_status"):  # For some file formats, this is not populated
305            for status, messageUsed, messageNotUsed in [
306                                    (orange.Variable.MakeStatus.Incompatible,
307                                     "",
308                                     "The following attributes already existed but had a different order of values, so new attributes needed to be created"),
309                                    (orange.Variable.MakeStatus.NoRecognizedValues,
310                                     "The following attributes were reused although they share no common values with the existing attribute of the same names",
311                                     "The following attributes were not reused since they share no common values with the existing attribute of the same names"),
312                                    (orange.Variable.MakeStatus.MissingValues,
313                                     "The following attribute(s) were reused although some values needed to be added",
314                                     "The following attribute(s) were not reused since they miss some values")
315                                    ]:
316                if self.createNewOn > status:
317                    message = messageUsed
318                else:
319                    message = messageNotUsed
320                if not message:
321                    continue
322                attrs = [attr.name for attr, stat in zip(data.domain, data.attributeLoadStatus) if stat == status] \
323                      + [attr.name for id, attr in metas.items() if data.metaAttributeLoadStatus.get(id, -99) == status]
324                if attrs:
325                    jattrs = ", ".join(attrs)
326                    if len(jattrs) > 80:
327                        jattrs = jattrs[:80] + "..."
328                    if len(jattrs) > 30: 
329                        warnings += "<li>%s:<br/> %s</li>" % (message, jattrs)
330                    else:
331                        warnings += "<li>%s: %s</li>" % (message, jattrs)
332
333        self.warnings.setText(warnings)
334        #qApp.processEvents()
335        #self.adjustSize()
336
337        addOrigin(data, fn)
338        # make new data and send it
339        fName = os.path.split(fn)[1]
340        if "." in fName:
341            data.name = fName[:fName.rfind('.')]
342        else:
343            data.name = fName
344
345        self.dataReport = self.prepareDataReport(data)
346        self.send("Data", data)
347        if self.processingHandler: self.processingHandler(self, 0)    # remove focus from this widget
348
349    def sendReport(self):
350        if hasattr(self, "dataReport"):
351            self.reportSettings("File",
352                                [("File name", self.loadedFile),
353                                 ("Format", self.formats.get(os.path.splitext(self.loadedFile)[1], "unknown format"))])
354            self.reportData(self.dataReport)
355
356if __name__ == "__main__":
357    a = QApplication(sys.argv)
358    ow = OWFile()
359    ow.show()
360    a.exec_()
361    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.