Skip to content

Commit 3e30c97

Browse files
committed
File: Allow selecting a file with an arbitrary extension
1 parent f3d9e19 commit 3e30c97

File tree

2 files changed

+66
-25
lines changed

2 files changed

+66
-25
lines changed

Orange/widgets/data/owfile.py

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,11 @@ class Warning(widget.OWWidget.Warning):
147147
class Error(widget.OWWidget.Error):
148148
file_not_found = Msg("File not found.")
149149
missing_reader = Msg("Missing reader.")
150+
select_file_type = Msg("Select file type.")
150151
sheet_error = Msg("Error listing available sheets.")
151152
unknown = Msg("Read error:\n{}")
153+
unknown_select = Msg(
154+
"Read error, possibly due to incorrect choice of file type:\n{}")
152155

153156
UserAdviceMessages = [
154157
widget.Message(
@@ -264,7 +267,7 @@ def package(w):
264267
self.reader_combo = QComboBox(self)
265268
self.reader_combo.setSizePolicy(Policy.Expanding, Policy.Fixed)
266269
self.reader_combo.setMinimumSize(QSize(100, 1))
267-
self.reader_combo.activated[int].connect(self.select_reader)
270+
self.reader_combo.activated[int].connect(self.on_reader_change)
268271

269272
box.layout().addWidget(self.reader_combo)
270273
layout.addWidget(box, 0, 1)
@@ -327,6 +330,10 @@ def select_sheet(self):
327330
self.recent_paths[0].sheet = self.sheet_combo.currentText()
328331
self.load_data()
329332

333+
def on_reader_change(self, n):
334+
self.select_reader(n)
335+
self.load_data()
336+
330337
def select_reader(self, n):
331338
if self.source != self.LOCAL_FILE:
332339
return # ignore for URL's
@@ -335,14 +342,11 @@ def select_reader(self, n):
335342
path = self.recent_paths[0]
336343
if n == 0: # default
337344
path.file_format = None
338-
self.load_data()
339345
elif n <= len(self.available_readers):
340346
reader = self.available_readers[n - 1]
341347
path.file_format = reader.qualified_name()
342-
self.load_data()
343348
else: # the rest include just qualified names
344349
path.file_format = self.reader_combo.itemText(n)
345-
self.load_data()
346350

347351
def _url_set(self):
348352
index = self.url_combo.currentIndex()
@@ -373,7 +377,9 @@ def browse_file(self, in_demos=False):
373377
else:
374378
start_file = self.last_path() or os.path.expanduser("~/")
375379

376-
filename, reader, _ = open_filename_dialog(start_file, None, self.available_readers)
380+
filename, reader, _ = open_filename_dialog(
381+
start_file, None, self.available_readers,
382+
add_all="*")
377383
if not filename:
378384
return
379385
self.add_path(filename)
@@ -415,20 +421,20 @@ def _try_load(self):
415421
if not url:
416422
return self.Information.no_file_selected
417423

418-
def mark_problematic_reader():
419-
self.reader_combo.setItemData(self.reader_combo.currentIndex(),
420-
QBrush(Qt.red), Qt.ForegroundRole)
421-
422424
try:
423425
self.reader = self._get_reader() # also sets current reader index
424426
assert self.reader is not None
425427
except MissingReaderException:
426-
mark_problematic_reader()
427-
return self.Error.missing_reader
428+
if self.reader_combo.currentIndex() > 0:
429+
return self.Error.missing_reader
430+
else:
431+
return self.Error.select_file_type
428432
except Exception as ex:
429-
mark_problematic_reader()
430433
log.exception(ex)
431-
return lambda x=ex: self.Error.unknown(str(x))
434+
if self.reader_combo.currentIndex() > 0:
435+
return lambda x=ex: self.Error.unknown(str(x))
436+
else:
437+
return lambda x=ex: self.Error.unknown_select(str(x))
432438

433439
try:
434440
self._update_sheet_combo()
@@ -439,7 +445,6 @@ def mark_problematic_reader():
439445
try:
440446
data = self.reader.read()
441447
except Exception as ex:
442-
mark_problematic_reader()
443448
log.exception(ex)
444449
return lambda x=ex: self.Error.unknown(str(x))
445450
if warnings:
@@ -455,9 +460,25 @@ def mark_problematic_reader():
455460
return None
456461

457462
def _get_reader(self) -> FileFormat:
463+
"""
464+
Get the reader for the current file.
465+
466+
For local files, this also observes the stored settings and the reader
467+
combo, as follows:
468+
469+
1. If the file format is known (from stored settings), use it and set
470+
the reader combo to the corresponding index (as in settings)
471+
2. Otherwise, detect it from the extension and set the combo to
472+
Auto detect, overriding any previous user-set choice
473+
3. Otherwise, use the current combo state.
474+
475+
Returns:
476+
FileFormat: reader instance
477+
"""
458478
if self.source == self.LOCAL_FILE:
459479
path = self.last_path()
460480
self.reader_combo.setEnabled(True)
481+
461482
if self.recent_paths and self.recent_paths[0].file_format:
462483
qname = self.recent_paths[0].file_format
463484
qname_index = {r.qualified_name(): i for i, r in enumerate(self.available_readers)}
@@ -471,11 +492,22 @@ def _get_reader(self) -> FileFormat:
471492
try:
472493
reader_class = class_from_qualified_name(qname)
473494
except Exception as ex:
474-
raise MissingReaderException(f'Can not find reader "{qname}"') from ex
495+
raise MissingReaderException(f'Can not fdind reader "{qname}"') from ex
475496
reader = reader_class(path)
497+
476498
else:
477-
self.reader_combo.setCurrentIndex(0)
478-
reader = FileFormat.get_reader(path)
499+
old_idx = self.reader_combo.currentIndex()
500+
try:
501+
self.reader_combo.setCurrentIndex(0)
502+
reader = FileFormat.get_reader(path)
503+
except MissingReaderException:
504+
if old_idx == 0:
505+
raise
506+
# Set the path for the current file format,
507+
# and repeat the call to return the corresponding reader
508+
self.select_reader(old_idx)
509+
return self._get_reader()
510+
479511
if self.recent_paths and self.recent_paths[0].sheet:
480512
reader.select_sheet(self.recent_paths[0].sheet)
481513
return reader
@@ -504,12 +536,21 @@ def _select_active_sheet(self):
504536
self.sheet_combo.setCurrentIndex(0)
505537

506538
def _initialize_reader_combo(self):
507-
self.reader_combo.clear()
508-
filters = [format_filter(f) for f in self.available_readers]
509-
self.reader_combo.addItems([DEFAULT_READER_TEXT] + filters)
510-
self.reader_combo.setCurrentIndex(0)
511-
self.reader_combo.setDisabled(True)
512-
# additional readers may be added in self._get_reader()
539+
# Reset to initial state without losing the current index or
540+
# emitting any signals.
541+
combo = self.reader_combo
542+
if not combo.count():
543+
filters = [format_filter(f) for f in self.available_readers]
544+
combo.addItems([DEFAULT_READER_TEXT] + filters)
545+
combo.setCurrentIndex(0)
546+
else:
547+
# additional readers may be added in self._get_reader()
548+
n = len(self.available_readers) + 1
549+
if combo.currentIndex() >= n:
550+
combo.setCurrentIndex(0)
551+
while combo.count() > n:
552+
combo.removeItem(combo.count() - 1)
553+
combo.setDisabled(True)
513554

514555
@staticmethod
515556
def _describe(table):

Orange/widgets/data/tests/test_owfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,13 +361,13 @@ def test_reader_custom_tab(self):
361361
outdata = self.get_output(self.widget.Outputs.data)
362362
self.assertEqual(len(outdata), 150) # loaded iris
363363

364-
def test_no_reader_extension(self):
364+
def test_unknown_extension(self):
365365
with named_file("", suffix=".xyz_unknown") as fn:
366366
no_reader = RecentPath(fn, None, None)
367367
self.widget = self.create_widget(OWFile,
368368
stored_settings={"recent_paths": [no_reader]})
369369
self.widget.load_data()
370-
self.assertTrue(self.widget.Error.missing_reader.is_shown())
370+
self.assertTrue(self.widget.Error.select_file_type.is_shown())
371371

372372
def test_fail_sheets(self):
373373
with named_file("", suffix=".failed_sheet") as fn:

0 commit comments

Comments
 (0)