Skip to content

Commit 7aaae76

Browse files
authored
Merge pull request #1405 from janezd/listview-models
gui: Add listView
2 parents 186597f + 547383a commit 7aaae76

File tree

5 files changed

+191
-118
lines changed

5 files changed

+191
-118
lines changed

Orange/widgets/gui.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
import pkg_resources
1313

1414
from PyQt4 import QtGui, QtCore
15-
from PyQt4.QtCore import Qt, pyqtSignal as Signal
15+
from PyQt4.QtCore import Qt, pyqtSignal as Signal, QSize
1616
from PyQt4.QtGui import QCursor, QApplication, QTableView, QHeaderView, \
17-
QStyledItemDelegate, QSizePolicy, QColor
17+
QStyledItemDelegate, QSizePolicy, QColor, QListView
1818

1919
# Some Orange widgets might expect this here
2020
from Orange.widgets.webview import WebView as WebviewWidget # pylint: disable=unused-import
@@ -1198,6 +1198,35 @@ def attributeItem(var):
11981198
return attributeIconDict[var], var.name
11991199

12001200

1201+
class ListViewWithSizeHint(QListView):
1202+
def __init__(self, *args, preferred_size=None, **kwargs):
1203+
super().__init__(*args, **kwargs)
1204+
if isinstance(preferred_size, tuple):
1205+
preferred_size = QSize(*preferred_size)
1206+
self.preferred_size = preferred_size
1207+
1208+
def sizeHint(self):
1209+
return self.preferred_size or super().sizeHint()
1210+
1211+
1212+
def listView(widget, master, value=None, model=None, box=None, callback=None,
1213+
sizeHint=None, **misc):
1214+
if box:
1215+
bg = vBox(widget, box, addToLayout=False)
1216+
else:
1217+
bg = widget
1218+
view = ListViewWithSizeHint(preferred_size=sizeHint)
1219+
view.setModel(model)
1220+
if value is not None:
1221+
connectControl(master, value, callback,
1222+
view.selectionModel().selectionChanged,
1223+
CallFrontListView(view),
1224+
CallBackListView(model, master, value))
1225+
misc.setdefault('addSpace', True)
1226+
miscellanea(view, bg, widget, **misc)
1227+
return view
1228+
1229+
12011230
def listBox(widget, master, value=None, labels=None, box=None, callback=None,
12021231
selectionMode=QtGui.QListWidget.SingleSelection,
12031232
enableDragDrop=False, dragDropCallback=None,
@@ -2443,6 +2472,23 @@ def __call__(self, *value):
24432472
self.func(**kwds)
24442473

24452474

2475+
class CallBackListView(ControlledCallback):
2476+
def __init__(self, model, widget, attribute):
2477+
super().__init__(widget, attribute)
2478+
self.model = model
2479+
2480+
# triggered by selectionModel().selectionChanged()
2481+
def __call__(self, newSelection, _):
2482+
# This must be imported locally to avoid circular imports
2483+
from Orange.widgets.utils.itemmodels import PyListModel
2484+
indexes = newSelection.indexes()
2485+
if indexes:
2486+
value = newSelection.indexes()[0].row()
2487+
if isinstance(self.model, PyListModel):
2488+
value = self.model[value]
2489+
self.acyclic_setattr(value)
2490+
2491+
24462492
class CallBackListBox:
24472493
def __init__(self, control, widget):
24482494
self.control = control
@@ -2607,6 +2653,25 @@ def action(self, value):
26072653
self.control.buttons[value].setChecked(1)
26082654

26092655

2656+
class CallFrontListView(ControlledCallFront):
2657+
def action(self, value):
2658+
model = self.control.model()
2659+
if not isinstance(value, int):
2660+
if isinstance(value, str):
2661+
search_role = Qt.DisplayRole
2662+
elif isinstance(value, Variable):
2663+
search_role = TableVariable
2664+
else:
2665+
search_role = Qt.DisplayRole
2666+
value = str(value)
2667+
for i in range(model.rowCount()):
2668+
if model.data(model.index(i), search_role) == value:
2669+
value = i
2670+
break
2671+
sel_model = self.control.selectionModel()
2672+
sel_model.select(model.index(value), sel_model.ClearAndSelect)
2673+
2674+
26102675
class CallFrontListBox(ControlledCallFront):
26112676
def action(self, value):
26122677
if value is not None:

Orange/widgets/settings.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -916,12 +916,11 @@ def filter_value(self, setting, data, domain, attrs, metas):
916916
del data[setting.name]
917917

918918
def settings_to_widget(self, widget, domain, *args):
919-
super().settings_to_widget(widget, domain, *args)
920-
921919
context = widget.current_context
922920
if context is None:
923921
return
924922

923+
widget.retrieveSpecificSettings()
925924
excluded = set()
926925

927926
for setting, data, instance in \
@@ -930,6 +929,9 @@ def settings_to_widget(self, widget, domain, *args):
930929
continue
931930

932931
value = self.decode_setting(setting, data[setting.name], domain)
932+
setattr(instance, setting.name, value)
933+
if hasattr(setting, "selected") and setting.selected in data:
934+
setattr(instance, setting.selected, data[setting.selected])
933935

934936
if isinstance(value, list):
935937
excluded |= set(value)

Orange/widgets/tests/test_gui.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from Orange.data import ContinuousVariable
12
from Orange.widgets import gui
23
from Orange.widgets.tests.base import GuiTest
4+
from Orange.widgets.utils.itemmodels import VariableListModel
35
from Orange.widgets.widget import OWWidget
46

57

6-
class OWTestDoubleSpin(GuiTest):
8+
class TestDoubleSpin(GuiTest):
79
some_param = 0
810
some_option = False
911

@@ -13,3 +15,23 @@ def test_checked_extension(self):
1315
widget = OWWidget()
1416
gui.doubleSpin(widget=widget, master=self, value="some_param",
1517
minv=1, maxv=10, checked="some_option")
18+
19+
20+
class TestListModel(GuiTest):
21+
def test_select(self):
22+
widget = OWWidget()
23+
widget.foo = None
24+
self.attrs = VariableListModel()
25+
view = gui.listView(widget.controlArea, widget, "foo", model=self.attrs)
26+
self.assertIsNone(widget.foo)
27+
a, b, c = (ContinuousVariable(x) for x in "abc")
28+
self.attrs[:] = [a, b, c]
29+
view.setCurrentIndex(self.attrs.index(0, 0))
30+
self.assertIs(widget.foo, a)
31+
view.setCurrentIndex(self.attrs.index(2, 0))
32+
self.assertIs(widget.foo, c)
33+
34+
widget.foo = b
35+
selection = view.selectedIndexes()
36+
self.assertEqual(len(selection), 1)
37+
self.assertEqual(selection[0].row(), 1)

Orange/widgets/utils/itemmodels.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,12 +586,13 @@ def data(self, index, role=Qt.DisplayRole):
586586

587587

588588
class VariableListModel(PyListModel):
589-
590589
MIME_TYPE = "application/x-Orange-VariableList"
591590

592591
def data(self, index, role=Qt.DisplayRole):
593592
if self._is_index_valid_for(index, self):
594593
var = self[index.row()]
594+
if var is None and role == Qt.DisplayRole:
595+
return "None"
595596
if not isinstance(var, Variable):
596597
return super().data(index, role)
597598
elif role == Qt.DisplayRole:
@@ -600,6 +601,8 @@ def data(self, index, role=Qt.DisplayRole):
600601
return gui.attributeIconDict[var]
601602
elif role == Qt.ToolTipRole:
602603
return self.variable_tooltip(var)
604+
elif role == gui.TableVariable:
605+
return var
603606
else:
604607
return PyListModel.data(self, index, role)
605608

0 commit comments

Comments
 (0)