Skip to content

Commit e3d5e76

Browse files
committed
OWSave: Change GUI as decided last week
1 parent 5df1827 commit e3d5e76

File tree

2 files changed

+77
-86
lines changed

2 files changed

+77
-86
lines changed

Orange/widgets/data/owsave.py

+74-55
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import os.path
22

3-
from AnyQt.QtWidgets import QFileDialog
4-
from AnyQt.QtCore import Qt
3+
from AnyQt.QtWidgets import QFileDialog, QGridLayout, QWidget
54

65
from Orange.data.table import Table
76
from Orange.data.io import TabReader, CSVReader, PickleReader, ExcelReader
@@ -21,29 +20,27 @@ class OWSave(widget.OWWidget):
2120
settings_version = 2
2221

2322
writers = [TabReader, CSVReader, PickleReader, ExcelReader]
24-
filters = [f"{w.DESCRIPTION} ({w.EXTENSIONS[0]})" for w in writers]
23+
filters = [f"{w.DESCRIPTION} (*{w.EXTENSIONS[0]})" for w in writers]
2524
filt_ext = {filter: w.EXTENSIONS[0] for filter, w in zip(filters, writers)}
2625
userhome = os.path.expanduser(f"~{os.sep}")
2726

2827
class Inputs:
2928
data = Input("Data", Table)
3029

3130
class Error(widget.OWWidget.Error):
32-
# This message is short to (almost) fit into the widget's width
33-
unsupported_sparse = widget.Msg("Format can't store sparse data.")
31+
unsupported_sparse = widget.Msg("Use .pkl format for sparse data.")
32+
no_file_name = widget.Msg("File name is not set.")
3433
general_error = widget.Msg("{}")
3534

3635
class Warning(widget.OWWidget.Warning):
37-
no_file_name = widget.Msg("Set the file name.")
38-
general_error = widget.Msg("{}")
36+
ignored_flag = widget.Msg("{} ignored for this format.")
3937

4038
want_main_area = False
4139
resizing_enabled = False
4240

4341
compress: bool
4442
add_type_annotations: bool
4543

46-
filetype = Setting(0)
4744
last_dir = Setting("")
4845
filter = Setting(filters[0])
4946
compress = Setting(False)
@@ -56,22 +53,31 @@ def __init__(self):
5653
self.filename = ""
5754
self.writer = self.writers[0]
5855

59-
box = gui.vBox(self.controlArea, True)
60-
box.layout().setSpacing(8)
61-
self.lb_filename = gui.widgetLabel(box)
62-
gui.checkBox(
63-
box, self, "add_type_annotations", "Save with type annotations")
64-
gui.checkBox(
65-
box, self, "compress", "Compress file (gzip)")
66-
self.bt_set_file = gui.button(
67-
None, self, "Set File Name", callback=self.set_file_name)
68-
box.layout().addWidget(self.bt_set_file, Qt.AlignRight)
69-
70-
box = gui.vBox(self.controlArea, box=True)
71-
box.layout().setSpacing(8)
72-
gui.checkBox(
73-
box, self, "auto_save", "Autosave when receiving new data")
74-
self.bt_save = gui.button(box, self, "Save", callback=self.save_file)
56+
grid = QGridLayout()
57+
gui.widgetBox(self.controlArea, box=True, orientation=grid)
58+
grid.setSpacing(8)
59+
self.bt_save = gui.button(None, self, "Save", callback=self.save_file)
60+
grid.addWidget(self.bt_save, 0, 0)
61+
grid.addWidget(
62+
gui.button(None, self, "Save as ...", callback=self.save_file_as),
63+
0, 1)
64+
grid.addWidget(
65+
gui.checkBox(None, self, "auto_save",
66+
"Autosave when receiving new data"),
67+
1, 0, 1, 2)
68+
grid.addWidget(QWidget(), 2, 0, 1, 2)
69+
70+
grid.addWidget(
71+
gui.checkBox(
72+
None, self, "add_type_annotations",
73+
"Save with type annotations", callback=self._update_controls),
74+
3, 0, 1, 2)
75+
grid.addWidget(
76+
gui.checkBox(
77+
None, self, "compress", "Compress file (gzip)",
78+
callback=self._update_controls),
79+
4, 0, 1, 2)
80+
7581
self.adjustSize()
7682
self._update_controls()
7783

@@ -91,7 +97,7 @@ def dataset(self, data):
9197
f"Data set {self.data.name or '(no name)'} "
9298
f"with {len(self.data)} instances")
9399

94-
def set_file_name(self):
100+
def save_file_as(self):
95101
if self.filename:
96102
start_dir = self.filename
97103
else:
@@ -100,56 +106,69 @@ def set_file_name(self):
100106
data_name += self.filt_ext[self.filter]
101107
start_dir = os.path.join(self.last_dir or self.userhome, data_name)
102108

103-
dlg = QFileDialog(None, "Set File", start_dir, ";;".join(self.filters))
104-
dlg.setLabelText(dlg.Accept, "Select")
105-
dlg.setAcceptMode(dlg.AcceptSave)
106-
dlg.setSupportedSchemes(["file"])
107-
dlg.selectNameFilter(self.filter)
108-
if dlg.exec() == dlg.Rejected:
109+
filename, selected_filter = QFileDialog.getSaveFileName(
110+
self, "Save data", start_dir, ";;".join(self.filters), self.filter)
111+
if not filename:
109112
return
110113

111-
self.filename = dlg.selectedFiles()[0]
112-
self.last_dir = os.path.split(self.filename)[0]
113-
self.filter = dlg.selectedNameFilter()
114+
self.filename = filename
115+
self.last_dir = os.path.split(filename)[0]
116+
self.filter = selected_filter
114117
self.writer = self.writers[self.filters.index(self.filter)]
115118
self._update_controls()
119+
self.save_file()
116120

117121
def save_file(self):
122+
if not self.filename:
123+
self.save_file_as()
124+
return
118125
self.Error.general_error.clear()
119126
if not self._can_save():
120127
return
121-
name = self.filename \
122-
+ ".gz" * self.writer.SUPPORT_COMPRESSED * self.compress
123128
try:
124-
self.writer.write(name, self.data, self.add_type_annotations)
129+
self.writer.write(
130+
self._fullname(), self.data, self.add_type_annotations)
125131
except IOError as err_value:
126132
self.Error.general_error(str(err_value))
127133

134+
def _fullname(self):
135+
return self.filename \
136+
+ ".gz" * self.writer.SUPPORT_COMPRESSED * self.compress
137+
128138
def _update_controls(self):
129-
has_file = bool(self.filename)
130-
self.lb_filename.setVisible(has_file)
131-
self.Warning.no_file_name(shown=not has_file)
132139
if self.filename:
133-
name = self.filename
134-
if name.startswith(self.userhome):
135-
name = name[len(self.userhome):]
136-
self.lb_filename.setText(f"Save to: {name}")
137-
138-
self.controls.add_type_annotations.setVisible(
139-
has_file and self.writer.OPTIONAL_TYPE_ANNOTATIONS)
140-
self.controls.compress.setVisible(
141-
has_file and self.writer.SUPPORT_COMPRESSED)
140+
self.bt_save.setText(
141+
f"Save as {os.path.split(self._fullname())[1]}")
142+
else:
143+
self.bt_save.setText("Save")
144+
self.Error.no_file_name(shown=not self.filename)
142145

143146
self.Error.unsupported_sparse(
144147
shown=self.data is not None and self.data.is_sparse()
145-
and self.filename
146-
and not self.writer.SUPPORT_SPARSE_DATA)
147-
self.bt_save.setEnabled(self._can_save())
148+
and self.filename and not self.writer.SUPPORT_SPARSE_DATA)
149+
150+
if self.data is None or not self.filename:
151+
self.Warning.ignored_flag.clear()
152+
else:
153+
no_compress = self.compress \
154+
and not self.writer.SUPPORT_COMPRESSED
155+
no_anotation = self.add_type_annotations \
156+
and not self.writer.OPTIONAL_TYPE_ANNOTATIONS
157+
ignored = [
158+
"",
159+
"Compression flag is",
160+
"Type annotation flag is",
161+
"Compression and type annotation flags are"
162+
][no_compress + 2 * no_anotation]
163+
self.Warning.ignored_flag(ignored, shown=bool(ignored))
148164

149165
def _can_save(self):
150-
return self.data is not None \
151-
and bool(self.filename) \
152-
and (not self.data.is_sparse() or self.writer.SUPPORT_SPARSE_DATA)
166+
return not (
167+
self.data is None
168+
or not self.filename
169+
or self.data.is_sparse() and not self.writer.SUPPORT_SPARSE_DATA
170+
or self.compress and not self.writer.SUPPORT_COMPRESSED
171+
)
153172

154173
def send_report(self):
155174
self.report_data_brief(self.data)

Orange/widgets/data/tests/test_owsave.py

+3-31
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def test_dataset(self):
3838
datasig = widget.Inputs.data
3939
self.send_signal(datasig, self.iris)
4040
self.assertEqual(insum.call_args[0][0], "150")
41-
self.assertFalse(widget.bt_save.isEnabled())
4241
insum.reset_mock()
4342
savefile.reset_mock()
4443

@@ -47,18 +46,15 @@ def test_dataset(self):
4746
widget.auto_save = False
4847
self.send_signal(datasig, self.iris)
4948
self.assertEqual(insum.call_args[0][0], "150")
50-
self.assertTrue(widget.bt_save.isEnabled())
5149
savefile.assert_not_called()
5250

5351
widget.auto_save = True
5452
self.send_signal(datasig, self.iris)
5553
self.assertEqual(insum.call_args[0][0], "150")
56-
self.assertTrue(widget.bt_save.isEnabled())
5754
savefile.assert_called()
5855

5956
self.send_signal(datasig, None)
6057
insum.assert_called_with(widget.info.NoInput)
61-
self.assertFalse(widget.bt_save.isEnabled())
6258

6359
@patch("Orange.widgets.data.owsave.QFileDialog")
6460
def test_set_file_name_start_dir(self, filedialog):
@@ -210,39 +206,15 @@ def test_file_label(self):
210206

211207
widget.filename = ""
212208
widget._update_controls()
213-
self.assertTrue(widget.lb_filename.isHidden())
214-
self.assertTrue(widget.Warning.no_file_name.is_shown())
209+
self.assertTrue(widget.Error.no_file_name.is_shown())
215210

216211
widget.filename = _w("/foo/bar/baz.csv")
217212
widget._update_controls()
218213
self.assertFalse(widget.lb_filename.isHidden())
219214
self.assertIn(
220-
widget.lb_filename.text(), _w("Save to: /foo/bar/baz.csv"))
221-
self.assertFalse(widget.Warning.no_file_name.is_shown())
215+
widget.lb_filename.text(), _w("File name: baz.csv"))
216+
self.assertFalse(widget.Error.no_file_name.is_shown())
222217

223-
widget.filename = os.path.expanduser(_w("~/baz/bar/foo.csv"))
224-
widget._update_controls()
225-
self.assertFalse(widget.lb_filename.isHidden())
226-
self.assertEqual(
227-
widget.lb_filename.text(), _w("Save to: baz/bar/foo.csv"))
228-
229-
def test_annotation_checkbox(self):
230-
widget = self.widget
231-
for _, widget.writer in FILE_TYPES:
232-
widget.filename = f"foo.{widget.writer.EXTENSIONS[0]}"
233-
widget._update_controls()
234-
self.assertIsNot(widget.controls.add_type_annotations.isHidden(),
235-
widget.writer.OPTIONAL_TYPE_ANNOTATIONS,
236-
msg=f"for {widget.writer}")
237-
self.assertIsNot(widget.controls.compress.isHidden(),
238-
widget.writer.SUPPORT_COMPRESSED,
239-
msg=f"for {widget.writer}")
240-
241-
widget.writer = TabReader
242-
widget.filename = ""
243-
self.widget._update_controls()
244-
self.assertFalse(widget.controls.add_type_annotations.isVisible())
245-
self.assertFalse(widget.controls.compress.isVisible())
246218

247219
def test_sparse_error(self):
248220
widget = self.widget

0 commit comments

Comments
 (0)