From bc426a84bd5fbb43d2783f1b7627dc811aa27d60 Mon Sep 17 00:00:00 2001 From: Ashwolaa <51032965+Ashwolaa@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:36:59 +0200 Subject: [PATCH 1/2] Little patch to avoid issues with the use of regexp: - Replace regexp with simple str check as we were not benefiting from it (and it leads to problemwith qtpy) - adding filter options to check for name (user can decide if he wants additional details) --- src/pymodaq_plugin_manager/manager.py | 80 +++++++++++++++++++-------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/src/pymodaq_plugin_manager/manager.py b/src/pymodaq_plugin_manager/manager.py index 4c3c1de..12f679f 100644 --- a/src/pymodaq_plugin_manager/manager.py +++ b/src/pymodaq_plugin_manager/manager.py @@ -36,25 +36,25 @@ def flags(self, index): f |= Qt.ItemIsUserCheckable return f - def data(self, index, role=Qt.DisplayRole): + def data(self, index, role=Qt.ItemDataRole.DisplayRole): if index.isValid(): - if role == Qt.DisplayRole or role == Qt.EditRole: + if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole: if index.column() == 0: dat = self._data[index.row()][0] else: dat = self._data[index.row()][index.column()] return dat - elif role == Qt.CheckStateRole: + elif role == Qt.ItemDataRole.CheckStateRole: if index.column() == 0: if self._selected[index.row()]: - return Qt.Checked + return Qt.CheckState.Checked else: - return Qt.Unchecked + return Qt.CheckState.Unchecked return QVariant() def setData(self, index, value, role): if index.isValid(): - if role == Qt.EditRole: + if role == Qt.ItemDataRole.EditRole: if self.validate_data(index.row(), index.column(), value): self._data[index.row()][index.column()] = value self.dataChanged.emit(index, index, [role]) @@ -62,12 +62,12 @@ def setData(self, index, value, role): else: return False - if role == Qt.CheckStateRole: + if role == Qt.ItemDataRole.CheckStateRole: if index.column() == 0: # Qt.Checked is an enum in qt6 but an int in qt5 # it can be directly compared in qt5 but getting # the value attribute is needed with qt6 - qt_checked = Qt.Checked.value if isinstance(Qt.Checked, Enum) else Qt.Checked + qt_checked = Qt.CheckState.value self._selected[index.row()] = value == qt_checked self.dataChanged.emit(index, index, [role]) return True @@ -78,29 +78,29 @@ class FilterProxy(QtCore.QSortFilterProxyModel): """Utility to filter the View""" def __init__(self, parent=None): super().__init__(parent) - - self.textRegExp = QtCore.QRegularExpression() - self.textRegExp.setPatternOptions(QtCore.QRegularExpression.CaseInsensitiveOption) - #self.textRegExp.setPatternSyntax(QtCore.QRegularExpression.Wildcard) - + self.text = '' def filterAcceptsRow(self, sourcerow, parent_index): plugin_index = self.sourceModel().index(sourcerow, 0, parent_index) try: plugin = self.sourceModel().plugins[plugin_index.row()] - match = False - if not not plugin: - match = match or self.textRegExp.pattern().lower() in plugin['plugin-name'].lower() - match = match or self.textRegExp.pattern().lower() in plugin['display-name'].lower() - match = match or self.textRegExp.pattern().lower() in plugin['description'].lower() - for plug in plugin['instruments']: - match = match | any(self.textRegExp.pattern().lower() in p.lower() for p in plugin['instruments'][plug]) + match = self.text == '' + if not (match or not plugin): + if self.parent().filter_name_cb.isChecked(): + match = match or self.text in plugin['plugin-name'].lower() + match = match or self.text in plugin['display-name'].lower() + if self.parent().filter_description_cb.isChecked(): + match = match or self.text in plugin['description'].lower() + if self.parent().filter_instrument_cb.isChecked(): + for plug in plugin['instruments']: + match = match | any(self.text in p.lower() for p in plugin['instruments'][plug]) return match + except Exception as e: print(e) return True - def setTextFilter(self, regexp): - self.textRegExp.setPattern(regexp) + def setTextFilter(self, regexp: str): + self.text = regexp.lower() self.invalidateFilter() @@ -229,9 +229,31 @@ def setup_UI(self): self.action_button.clicked.connect(self.do_action) settings_widget.layout().addWidget(self.action_button) - self.parent.layout().addWidget(settings_widget) + search_widget = QtWidgets.QWidget() + search_widget.setLayout(QtWidgets.QHBoxLayout()) + search_widget.setSizePolicy( + QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed + ) + self.filter_options_cb = QtWidgets.QCheckBox("Search filter settings:") + self.filter_options_cb.stateChanged.connect(self.show_filter_settings) + search_widget.layout().addWidget(self.filter_options_cb) + + self.filter_name_cb = QtWidgets.QCheckBox('Name') + self.filter_name_cb.setCheckState(Qt.CheckState.Checked) + search_widget.layout().addWidget(self.filter_name_cb) + + self.filter_description_cb = QtWidgets.QCheckBox('Description') + self.filter_description_cb.setCheckState(Qt.CheckState.Unchecked) + search_widget.layout().addWidget(self.filter_description_cb) + + self.filter_instrument_cb = QtWidgets.QCheckBox('Instruments') + self.filter_instrument_cb.setCheckState(Qt.CheckState.Unchecked) + search_widget.layout().addWidget(self.filter_instrument_cb) + + self.parent.layout().addWidget(search_widget) + splitter = QtWidgets.QSplitter(Qt.Vertical) self.table_view = TableView() @@ -249,8 +271,15 @@ def setup_UI(self): splitter.addWidget(self.info_widget) self.parent.layout().addWidget(splitter) + + self.show_filter_settings(False) QtWidgets.QApplication.processEvents() + def show_filter_settings(self,status): + self.filter_instrument_cb.setVisible(status) + self.filter_description_cb.setVisible(status) + self.filter_name_cb.setVisible(status) + def setup_models(self, plugins: tuple): self.plugins_available, self.plugins_installed, self.plugins_update = plugins @@ -270,9 +299,12 @@ def setup_models(self, plugins: tuple): editable=[False, False], plugins=self.plugins_installed) - model_available_proxy = FilterProxy() + model_available_proxy = FilterProxy(self) model_available_proxy.setSourceModel(self.model_available) self.search_edit.textChanged.connect(model_available_proxy.setTextFilter) + self.filter_name_cb.stateChanged.connect(model_available_proxy.invalidateFilter) + self.filter_description_cb.stateChanged.connect(model_available_proxy.invalidateFilter) + self.filter_instrument_cb.stateChanged.connect(model_available_proxy.invalidateFilter) self.table_view.setModel(model_available_proxy) self.table_view.setSortingEnabled(True) self.table_view.clicked.connect(self.item_clicked) From 018f18bc0ac54aa76384955ce88a1f3506a70b73 Mon Sep 17 00:00:00 2001 From: Ashwola Date: Fri, 4 Jul 2025 14:01:33 +0200 Subject: [PATCH 2/2] additional check to avoid errors --- src/pymodaq_plugin_manager/manager.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pymodaq_plugin_manager/manager.py b/src/pymodaq_plugin_manager/manager.py index 12f679f..49317c4 100644 --- a/src/pymodaq_plugin_manager/manager.py +++ b/src/pymodaq_plugin_manager/manager.py @@ -33,7 +33,7 @@ def selected(self): def flags(self, index): f = super().flags(index) if index.column() == 0: - f |= Qt.ItemIsUserCheckable + f |= Qt.ItemFlag.ItemIsUserCheckable return f def data(self, index, role=Qt.ItemDataRole.DisplayRole): @@ -79,18 +79,20 @@ class FilterProxy(QtCore.QSortFilterProxyModel): def __init__(self, parent=None): super().__init__(parent) self.text = '' + def filterAcceptsRow(self, sourcerow, parent_index): plugin_index = self.sourceModel().index(sourcerow, 0, parent_index) + parent = self.parent() try: plugin = self.sourceModel().plugins[plugin_index.row()] match = self.text == '' if not (match or not plugin): - if self.parent().filter_name_cb.isChecked(): + if hasattr(parent, "filter_name_cb") and parent.filter_name_cb.isChecked(): match = match or self.text in plugin['plugin-name'].lower() match = match or self.text in plugin['display-name'].lower() - if self.parent().filter_description_cb.isChecked(): + if hasattr(parent, "filter_description_cb") and parent.filter_description_cb.isChecked(): match = match or self.text in plugin['description'].lower() - if self.parent().filter_instrument_cb.isChecked(): + if hasattr(parent, "filter_instrument_cb") and parent.filter_instrument_cb.isChecked(): for plug in plugin['instruments']: match = match | any(self.text in p.lower() for p in plugin['instruments'][plug]) return match