source: orange/orange/OrangeWidgets/Data/OWFile.py @ 9546:2b6cc6f397fe

Revision 9546:2b6cc6f397fe, 14.3 KB checked in by ales_erjavec <ales.erjavec@…>, 2 years ago (diff)

Renamed widget channel names in line with the new naming rules/convention.
Added backwards compatibility in orngDoc loadDocument to enable loading of schemas saved before the change.

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
19
20class FileNameContextHandler(ContextHandler):
21    def match(self, context, imperfect, filename):
22        return context.filename == filename and 2
23
24
25def addOrigin(examples, filename):
26    vars = examples.domain.variables + examples.domain.getmetas().values()
27    strings = [var for var in vars if isinstance(var, orange.StringVariable)]
28    dirname, basename = os.path.split(filename)
29    for var in strings:
30        if "type" in var.attributes and "origin" not in var.attributes:
31            var.attributes["origin"] = dirname
32                 
33
34class OWFile(OWWidget):
35    settingsList=["recentFiles", "createNewOn", "showAdvanced"]
36    contextHandlers = {"": FileNameContextHandler()}
37
38    registeredFileTypes = [ft for ft in orange.getRegisteredFileTypes() if len(ft)>2 and ft[2]]
39    dlgFormats = 'Tab-delimited files (*.tab *.txt)\nC4.5 files (*.data)\nAssistant files (*.dat)\nRetis files (*.rda *.rdo)\nBasket files (*.basket)\n' \
40                 + "\n".join("%s (%s)" % (ft[:2]) for ft in registeredFileTypes) \
41                 + "\nAll files(*.*)"
42                 
43    formats = {".tab": "Tab-delimited file", ".txt": "Tab-delimited file", ".data": "C4.5 file",
44               ".dat": "Assistant file", ".rda": "Retis file", ".rdo": "Retis file",
45               ".basket": "Basket file"}
46    formats.update(dict((ft[1][2:], ft[0]) for ft in registeredFileTypes))
47     
48                 
49    def __init__(self, parent=None, signalManager = None):
50        OWWidget.__init__(self, parent, signalManager, "File", wantMainArea = 0, resizingEnabled = 1)
51
52        self.inputs = []
53        self.outputs = [("Data", ExampleTable)]
54
55        self.recentFiles=["(none)"]
56        self.symbolDC = "?"
57        self.symbolDK = "~"
58        self.createNewOn = 1
59        self.domain = None
60        self.loadedFile = ""
61        self.showAdvanced = 0
62        self.loadSettings()
63
64        box = OWGUI.widgetBox(self.controlArea, "Data File", addSpace = True, orientation=0)
65        self.filecombo = QComboBox(box)
66        self.filecombo.setMinimumWidth(150)
67        box.layout().addWidget(self.filecombo)
68        button = OWGUI.button(box, self, '...', callback = self.browseFile, disabled=0)
69        button.setIcon(self.style().standardIcon(QStyle.SP_DirOpenIcon))
70        button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
71       
72        self.reloadBtn = OWGUI.button(box, self, "Reload", callback = self.reload, default=True)
73        self.reloadBtn.setIcon(self.style().standardIcon(QStyle.SP_BrowserReload))
74        self.reloadBtn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
75       
76        box = OWGUI.widgetBox(self.controlArea, "Info", addSpace = True)
77        self.infoa = OWGUI.widgetLabel(box, 'No data loaded.')
78        self.infob = OWGUI.widgetLabel(box, ' ')
79        self.warnings = OWGUI.widgetLabel(box, ' ')
80       
81        #Set word wrap so long warnings won't expand the widget
82        self.warnings.setWordWrap(True)
83        self.warnings.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
84       
85        smallWidget = OWGUI.collapsableWidgetBox(self.controlArea, "Advanced settings", self, "showAdvanced", callback=self.adjustSize0)
86       
87        box = OWGUI.widgetBox(smallWidget, "Missing Value Symbols")
88#        OWGUI.widgetLabel(box, "Symbols for missing values in tab-delimited files (besides default ones)")
89       
90        hbox = OWGUI.indentedBox(box)
91        OWGUI.lineEdit(hbox, self, "symbolDC", "Don't care:", labelWidth=80, orientation="horizontal", tooltip="Default values: '~' or '*'")
92        OWGUI.lineEdit(hbox, self, "symbolDK", "Don't know:", labelWidth=80, orientation="horizontal", tooltip="Default values: empty fields (space), '?' or 'NA'")
93
94        smallWidget.layout().addSpacing(8)
95        OWGUI.radioButtonsInBox(smallWidget, self, "createNewOn", box="New Attributes",
96                       label = "Create a new attribute when existing attribute(s) ...",
97                       btnLabels = ["Have mismatching order of values",
98                                    "Have no common values with the new (recommended)",
99                                    "Miss some values of the new attribute",
100                                    "... Always create a new attribute"
101                               ])
102       
103        OWGUI.rubber(smallWidget)
104        smallWidget.updateControls()
105       
106        OWGUI.rubber(self.controlArea)
107       
108        # remove missing data set names
109        def exists(path):
110            if not os.path.exists(path):
111                dirpath, basename = os.path.split(path)
112                return os.path.exists(os.path.join("./", basename))
113            else:
114                return True
115        self.recentFiles = filter(exists, self.recentFiles)
116        self.setFileList()
117
118        if len(self.recentFiles) > 0 and exists(self.recentFiles[0]):
119            self.openFile(self.recentFiles[0], 0, self.symbolDK, self.symbolDC)
120
121        self.connect(self.filecombo, SIGNAL('activated(int)'), self.selectFile)
122
123    def adjustSize0(self):
124        qApp.processEvents()
125        QTimer.singleShot(50, self.adjustSize)
126
127    def setFileList(self):
128        self.filecombo.clear()
129        if not self.recentFiles:
130            self.filecombo.addItem("(none)")
131        for file in self.recentFiles:
132            if file == "(none)":
133                self.filecombo.addItem("(none)")
134            else:
135                self.filecombo.addItem(os.path.split(file)[1])
136        self.filecombo.addItem("Browse documentation data sets...")
137       
138
139    def reload(self):
140        if self.recentFiles:
141            return self.openFile(self.recentFiles[0], 1, self.symbolDK, self.symbolDC)
142
143
144    def settingsFromWidgetCallback(self, handler, context):
145        context.filename = self.loadedFile
146        context.symbolDC, context.symbolDK = self.symbolDC, self.symbolDK
147
148    def settingsToWidgetCallback(self, handler, context):
149        self.symbolDC, self.symbolDK = context.symbolDC, context.symbolDK
150
151    def selectFile(self, n):
152        if n < len(self.recentFiles) :
153            name = self.recentFiles[n]
154            self.recentFiles.remove(name)
155            self.recentFiles.insert(0, name)
156        elif n:
157            self.browseFile(1)
158
159        if len(self.recentFiles) > 0:
160            self.setFileList()
161            self.openFile(self.recentFiles[0], 0, self.symbolDK, self.symbolDC)
162
163    def browseFile(self, inDemos=0):
164        "Display a FileDialog and select a file"
165        if inDemos:
166            try:
167                import orngConfiguration
168                startfile = orngConfiguration.datasetsPath
169            except:
170                startfile = ""
171               
172            if not startfile or not os.path.exists(startfile):
173                try:
174                    import win32api, win32con
175                    t = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SOFTWARE\\Python\\PythonCore\\%i.%i\\PythonPath\\Orange" % sys.version_info[:2], 0, win32con.KEY_READ)
176                    t = win32api.RegQueryValueEx(t, "")[0]
177                    startfile = t[:t.find("orange")] + "orange\\doc\\datasets"
178                except:
179                    startfile = ""
180
181            if not startfile or not os.path.exists(startfile):
182                widgetsdir = os.path.dirname(OWGUI.__file__)
183                orangedir = os.path.dirname(widgetsdir)
184                startfile = os.path.join(orangedir, "doc", "datasets")
185
186            if not startfile or not os.path.exists(startfile):
187                d = os.getcwd()
188                if os.path.basename(d) == "OrangeCanvas":
189                    startfile = os.path.join(os.path.dirname(d), "doc", "datasets")
190                else:
191                    startfile = os.path.join(d, "doc", "datasets")
192
193            if not os.path.exists(startfile):
194                QMessageBox.information( None, "File", "Cannot find the directory with example data sets", QMessageBox.Ok + QMessageBox.Default)
195                return
196        else:
197            if len(self.recentFiles) == 0 or self.recentFiles[0] == "(none)":
198                startfile = os.path.expanduser("~/")
199            else:
200                startfile = self.recentFiles[0]
201
202        filename = str(QFileDialog.getOpenFileName(self, 'Open Orange Data File', startfile, self.dlgFormats))
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("")
266                self.warnings.setText("")
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        for status, messageUsed, messageNotUsed in [
287                                (orange.Variable.MakeStatus.Incompatible,
288                                 "",
289                                 "The following attributes already existed but had a different order of values, so new attributes needed to be created"),
290                                (orange.Variable.MakeStatus.NoRecognizedValues,
291                                 "The following attributes were reused although they share no common values with the existing attribute of the same names",
292                                 "The following attributes were not reused since they share no common values with the existing attribute of the same names"),
293                                (orange.Variable.MakeStatus.MissingValues,
294                                 "The following attribute(s) were reused although some values needed to be added",
295                                 "The following attribute(s) were not reused since they miss some values")
296                                ]:
297            if self.createNewOn > status:
298                message = messageUsed
299            else:
300                message = messageNotUsed
301            if not message:
302                continue
303            attrs = [attr.name for attr, stat in zip(data.domain, data.attributeLoadStatus) if stat == status] \
304                  + [attr.name for id, attr in metas.items() if data.metaAttributeLoadStatus.get(id, -99) == status]
305            if attrs:
306                jattrs = ", ".join(attrs)
307                if len(jattrs) > 80:
308                    jattrs = jattrs[:80] + "..."
309                if len(jattrs) > 30: 
310                    warnings += "<li>%s:<br/> %s</li>" % (message, jattrs)
311                else:
312                    warnings += "<li>%s: %s</li>" % (message, jattrs)
313
314        self.warnings.setText(warnings)
315        #qApp.processEvents()
316        #self.adjustSize()
317
318        addOrigin(data, fn)
319        # make new data and send it
320        fName = os.path.split(fn)[1]
321        if "." in fName:
322            data.name = fName[:fName.rfind('.')]
323        else:
324            data.name = fName
325
326        self.dataReport = self.prepareDataReport(data)
327        self.send("Data", data)
328        if self.processingHandler: self.processingHandler(self, 0)    # remove focus from this widget
329
330    def sendReport(self):
331        if hasattr(self, "dataReport"):
332            self.reportSettings("File",
333                                [("File name", self.loadedFile),
334                                 ("Format", self.formats.get(os.path.splitext(self.loadedFile)[1], "unknown format"))])
335            self.reportData(self.dataReport)
336
337if __name__ == "__main__":
338    a = QApplication(sys.argv)
339    ow = OWFile()
340    ow.show()
341    a.exec_()
342    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.