source: orange/Orange/OrangeWidgets/Data/OWFile.py @ 10856:c43ad9e0845d

Revision 10856:c43ad9e0845d, 14.5 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

Show the error string in the widgets's Info box.

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