From ef43e2287be60ad96f4427cbbe63d6119b9f06d8 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Wed, 24 Apr 2024 16:30:09 +0100 Subject: [PATCH 1/4] Import qt modules via qtpy --- eqt/threading/QtThreading.py | 4 ++-- eqt/ui/FormDialog.py | 2 +- eqt/ui/MainWindowWithProgressDialogs.py | 6 +++--- eqt/ui/MainWindowWithSessionManagement.py | 4 ++-- eqt/ui/ProgressTimerDialog.py | 6 +++--- eqt/ui/ReOrderableListWidget.py | 2 +- eqt/ui/SessionDialogs.py | 4 ++-- eqt/ui/UIFormWidget.py | 2 +- eqt/ui/UIMultiStepWidget.py | 6 +++--- eqt/ui/UISliderWidget.py | 4 ++-- eqt/ui/UIStackedWidget.py | 6 +++--- examples/MainWindowWithSessionManagement_example.py | 4 ++-- examples/advanced_dialog_example.py | 2 +- examples/dialog_example.py | 2 +- examples/dialog_example_2.py | 2 +- examples/dialog_example_3_save_default.py | 2 +- examples/dialog_multistep_example.py | 2 +- examples/dialog_save_state_example.py | 2 +- examples/insert_widgets_example.py | 2 +- examples/progress_timer_dialog_example.py | 4 ++-- examples/remove_widgets_example.py | 2 +- examples/reorderable_list_widget_example.py | 2 +- examples/utilitiesForExamples.py | 2 +- test/__init__.py | 2 +- test/dialog_example_2_test.py | 6 +++--- test/test_MainWindowWithSessionManagement.py | 4 ++-- test/test_SessionDialogs.py | 2 +- test/test__formUI_status_test.py | 6 +++--- 28 files changed, 47 insertions(+), 47 deletions(-) diff --git a/eqt/threading/QtThreading.py b/eqt/threading/QtThreading.py index faec586..7804228 100644 --- a/eqt/threading/QtThreading.py +++ b/eqt/threading/QtThreading.py @@ -7,8 +7,8 @@ # https://www.geeksforgeeks.org/migrate-pyqt5-app-to-pyside2 import traceback -from PySide2 import QtCore -from PySide2.QtCore import Slot +from qtpy import QtCore +from qtpy.QtCore import Slot class Worker(QtCore.QRunnable): diff --git a/eqt/ui/FormDialog.py b/eqt/ui/FormDialog.py index ca83bfb..635909b 100644 --- a/eqt/ui/FormDialog.py +++ b/eqt/ui/FormDialog.py @@ -1,4 +1,4 @@ -from PySide2 import QtWidgets +from qtpy import QtWidgets from . import UIFormFactory diff --git a/eqt/ui/MainWindowWithProgressDialogs.py b/eqt/ui/MainWindowWithProgressDialogs.py index 0bc3575..1893eaa 100644 --- a/eqt/ui/MainWindowWithProgressDialogs.py +++ b/eqt/ui/MainWindowWithProgressDialogs.py @@ -1,9 +1,9 @@ import qdarkstyle -from PySide2.QtCore import QSettings, QThreadPool -from PySide2.QtGui import QKeySequence -from PySide2.QtWidgets import QAction, QMainWindow from qdarkstyle.dark.palette import DarkPalette from qdarkstyle.light.palette import LightPalette +from qtpy.QtCore import QSettings, QThreadPool +from qtpy.QtGui import QKeySequence +from qtpy.QtWidgets import QAction, QMainWindow from .ProgressTimerDialog import ProgressTimerDialog from .SessionDialogs import AppSettingsDialog diff --git a/eqt/ui/MainWindowWithSessionManagement.py b/eqt/ui/MainWindowWithSessionManagement.py index 75bea8f..0365cf0 100644 --- a/eqt/ui/MainWindowWithSessionManagement.py +++ b/eqt/ui/MainWindowWithSessionManagement.py @@ -4,8 +4,8 @@ from datetime import datetime from functools import partial -from PySide2.QtGui import QCloseEvent, QKeySequence -from PySide2.QtWidgets import QAction +from qtpy.QtGui import QCloseEvent, QKeySequence +from qtpy.QtWidgets import QAction from ..io import zip_directory from ..threading import Worker diff --git a/eqt/ui/ProgressTimerDialog.py b/eqt/ui/ProgressTimerDialog.py index edf370a..0968f4a 100644 --- a/eqt/ui/ProgressTimerDialog.py +++ b/eqt/ui/ProgressTimerDialog.py @@ -1,9 +1,9 @@ import time from time import sleep -from PySide2 import QtCore -from PySide2.QtCore import Qt, QThreadPool -from PySide2.QtWidgets import QProgressDialog +from qtpy import QtCore +from qtpy.QtCore import Qt, QThreadPool +from qtpy.QtWidgets import QProgressDialog from ..threading import Worker diff --git a/eqt/ui/ReOrderableListWidget.py b/eqt/ui/ReOrderableListWidget.py index 68de6da..6bd3fdc 100644 --- a/eqt/ui/ReOrderableListWidget.py +++ b/eqt/ui/ReOrderableListWidget.py @@ -1,4 +1,4 @@ -from PySide2 import QtCore, QtWidgets +from qtpy import QtCore, QtWidgets class ReOrderableListWidget(QtWidgets.QTableWidget): diff --git a/eqt/ui/SessionDialogs.py b/eqt/ui/SessionDialogs.py index cf5a35d..b8b0032 100644 --- a/eqt/ui/SessionDialogs.py +++ b/eqt/ui/SessionDialogs.py @@ -1,7 +1,7 @@ import os -from PySide2 import QtWidgets -from PySide2.QtWidgets import ( +from qtpy import QtWidgets +from qtpy.QtWidgets import ( QCheckBox, QComboBox, QFileDialog, diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index 3e067db..0a085e7 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -1,6 +1,6 @@ from warnings import warn -from PySide2 import QtWidgets +from qtpy import QtWidgets from .UISliderWidget import UISliderWidget diff --git a/eqt/ui/UIMultiStepWidget.py b/eqt/ui/UIMultiStepWidget.py index 359d00b..2165721 100644 --- a/eqt/ui/UIMultiStepWidget.py +++ b/eqt/ui/UIMultiStepWidget.py @@ -1,6 +1,6 @@ -from PySide2 import QtWidgets -from PySide2.QtCore import Qt -from PySide2.QtWidgets import QGroupBox, QHBoxLayout, QPushButton +from qtpy import QtWidgets +from qtpy.QtCore import Qt +from qtpy.QtWidgets import QGroupBox, QHBoxLayout, QPushButton class UIMultiStepWidget(object): diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index c5722cb..86f39dc 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -1,5 +1,5 @@ -from PySide2 import QtCore -from PySide2.QtWidgets import QSlider +from qtpy import QtCore +from qtpy.QtWidgets import QSlider class UISliderWidget(QSlider): diff --git a/eqt/ui/UIStackedWidget.py b/eqt/ui/UIStackedWidget.py index ba39374..f2bdb57 100644 --- a/eqt/ui/UIStackedWidget.py +++ b/eqt/ui/UIStackedWidget.py @@ -1,6 +1,6 @@ -from PySide2 import QtWidgets -from PySide2.QtCore import Qt -from PySide2.QtWidgets import QHBoxLayout, QListWidget, QStackedWidget, QVBoxLayout, QWidget +from qtpy import QtWidgets +from qtpy.QtCore import Qt +from qtpy.QtWidgets import QHBoxLayout, QListWidget, QStackedWidget, QVBoxLayout, QWidget from .UIFormWidget import UIFormFactory diff --git a/examples/MainWindowWithSessionManagement_example.py b/examples/MainWindowWithSessionManagement_example.py index ff52f45..91d138d 100644 --- a/examples/MainWindowWithSessionManagement_example.py +++ b/examples/MainWindowWithSessionManagement_example.py @@ -1,7 +1,7 @@ import sys -from PySide2 import QtWidgets -from PySide2.QtWidgets import QApplication +from qtpy import QtWidgets +from qtpy.QtWidgets import QApplication from eqt import __version__ from eqt.ui.MainWindowWithSessionManagement import MainWindowWithSessionManagement diff --git a/examples/advanced_dialog_example.py b/examples/advanced_dialog_example.py index ea59e6a..8f451d3 100644 --- a/examples/advanced_dialog_example.py +++ b/examples/advanced_dialog_example.py @@ -1,7 +1,7 @@ import sys import utilitiesForExamples -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui.FormDialog import AdvancedFormDialog from eqt.ui.UIFormWidget import FormWidget diff --git a/examples/dialog_example.py b/examples/dialog_example.py index b1b3dc0..1f6cf74 100644 --- a/examples/dialog_example.py +++ b/examples/dialog_example.py @@ -1,6 +1,6 @@ import sys -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import UIFormFactory diff --git a/examples/dialog_example_2.py b/examples/dialog_example_2.py index 4dec5e0..a0c736c 100644 --- a/examples/dialog_example_2.py +++ b/examples/dialog_example_2.py @@ -1,6 +1,6 @@ import sys -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import FormDialog diff --git a/examples/dialog_example_3_save_default.py b/examples/dialog_example_3_save_default.py index 27c8cc5..faa3f30 100644 --- a/examples/dialog_example_3_save_default.py +++ b/examples/dialog_example_3_save_default.py @@ -1,7 +1,7 @@ import sys import utilitiesForExamples as utex -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import FormDialog diff --git a/examples/dialog_multistep_example.py b/examples/dialog_multistep_example.py index 5b4aac0..fd1a7e0 100644 --- a/examples/dialog_multistep_example.py +++ b/examples/dialog_multistep_example.py @@ -1,6 +1,6 @@ import sys -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import UIFormFactory, UIMultiStepFactory diff --git a/examples/dialog_save_state_example.py b/examples/dialog_save_state_example.py index d0ccf2c..18d3670 100644 --- a/examples/dialog_save_state_example.py +++ b/examples/dialog_save_state_example.py @@ -1,6 +1,6 @@ import sys -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import FormDialog from eqt.ui.UISliderWidget import UISliderWidget diff --git a/examples/insert_widgets_example.py b/examples/insert_widgets_example.py index 29e8cba..c5ddda1 100644 --- a/examples/insert_widgets_example.py +++ b/examples/insert_widgets_example.py @@ -1,6 +1,6 @@ import sys -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import FormDialog, UIFormWidget diff --git a/examples/progress_timer_dialog_example.py b/examples/progress_timer_dialog_example.py index 675fcce..be2666b 100644 --- a/examples/progress_timer_dialog_example.py +++ b/examples/progress_timer_dialog_example.py @@ -1,8 +1,8 @@ import sys from time import sleep -from PySide2 import QtCore, QtWidgets -from PySide2.QtCore import QThreadPool +from qtpy import QtCore, QtWidgets +from qtpy.QtCore import QThreadPool from eqt.threading import Worker from eqt.ui import ProgressTimerDialog diff --git a/examples/remove_widgets_example.py b/examples/remove_widgets_example.py index 74e0b57..8c4933f 100644 --- a/examples/remove_widgets_example.py +++ b/examples/remove_widgets_example.py @@ -1,6 +1,6 @@ import sys -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import FormDialog, UIFormWidget diff --git a/examples/reorderable_list_widget_example.py b/examples/reorderable_list_widget_example.py index 58f12c4..73f2d2f 100644 --- a/examples/reorderable_list_widget_example.py +++ b/examples/reorderable_list_widget_example.py @@ -1,8 +1,8 @@ import sys import qdarkstyle -from PySide2 import QtWidgets from qdarkstyle.dark.palette import DarkPalette +from qtpy import QtWidgets from eqt.ui.ReOrderableListWidget import ReOrderableListWidget diff --git a/examples/utilitiesForExamples.py b/examples/utilitiesForExamples.py index 4f409f5..b4c8c9d 100644 --- a/examples/utilitiesForExamples.py +++ b/examples/utilitiesForExamples.py @@ -1,4 +1,4 @@ -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui.UISliderWidget import UISliderWidget diff --git a/test/__init__.py b/test/__init__.py index bb1eb5b..9bbd41c 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,7 +1,7 @@ import os -from PySide2 import QtWidgets from pytest import skip +from qtpy import QtWidgets from eqt.ui import FormDialog diff --git a/test/dialog_example_2_test.py b/test/dialog_example_2_test.py index d15e7f1..61f3d9d 100644 --- a/test/dialog_example_2_test.py +++ b/test/dialog_example_2_test.py @@ -1,9 +1,9 @@ import sys import unittest -from PySide2 import QtWidgets -from PySide2.QtCore import Qt -from PySide2.QtTest import QTest +from qtpy import QtWidgets +from qtpy.QtCore import Qt +from qtpy.QtTest import QTest from eqt.ui import FormDialog diff --git a/test/test_MainWindowWithSessionManagement.py b/test/test_MainWindowWithSessionManagement.py index 777014c..afac7cd 100644 --- a/test/test_MainWindowWithSessionManagement.py +++ b/test/test_MainWindowWithSessionManagement.py @@ -6,8 +6,8 @@ from unittest import mock from unittest.mock import patch -from PySide2.QtCore import QSettings, QThreadPool -from PySide2.QtWidgets import QMenu, QMenuBar +from qtpy.QtCore import QSettings, QThreadPool +from qtpy.QtWidgets import QMenu, QMenuBar import eqt from eqt.io import zip_directory diff --git a/test/test_SessionDialogs.py b/test/test_SessionDialogs.py index 5a3c352..0dbfa9c 100644 --- a/test/test_SessionDialogs.py +++ b/test/test_SessionDialogs.py @@ -3,7 +3,7 @@ from pathlib import Path from unittest.mock import patch -from PySide2.QtWidgets import QFileDialog +from qtpy.QtWidgets import QFileDialog from eqt.ui.SessionDialogs import ( AppSettingsDialog, diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index f5600e7..ebae54d 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -2,9 +2,9 @@ import unittest from unittest import mock -from PySide2 import QtWidgets -from PySide2.QtCore import Qt -from PySide2.QtTest import QTest +from qtpy import QtWidgets +from qtpy.QtCore import Qt +from qtpy.QtTest import QTest from eqt.ui.FormDialog import AdvancedFormDialog, FormDialog from eqt.ui.UIFormWidget import FormDockWidget, FormWidget From a5abab5b0fbbd93e55a20c846afc565a6a88aea3 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Wed, 24 Apr 2024 16:31:05 +0100 Subject: [PATCH 2/4] Update mocking in test_SessionDialogs for qtpy In test_browse_button_calls_browse_for_dir only the browse_for_dir() method needs mocking --- test/test_SessionDialogs.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/test_SessionDialogs.py b/test/test_SessionDialogs.py index 0dbfa9c..20fa03a 100644 --- a/test/test_SessionDialogs.py +++ b/test/test_SessionDialogs.py @@ -83,19 +83,17 @@ def test_select_session_directory_label_when_app_name_set(self): sdsd.getWidget("select_session_directory").text(), "Select a session directory to save and retrieve all Test App Sessions:") - @patch("PySide2.QtWidgets.QFileDialog.getExistingDirectory") + @patch("qtpy.QtWidgets.QFileDialog.getExistingDirectory") def test_browse_for_dir_button_makes_file_dialog_for_getting_dir(self, mock_dialog_call): sdsd = SessionDirectorySelectionDialog() sdsd.browse_for_dir() mock_dialog_call.assert_called_once() - @patch("PySide2.QtWidgets.QFileDialog.getExistingDirectory") - def test_browse_button_calls_browse_for_dir(self, mock_dialog_call): + @patch.object(SessionDirectorySelectionDialog, "browse_for_dir") + def test_browse_button_calls_browse_for_dir(self, mock_browse): sdsd = SessionDirectorySelectionDialog() - sdsd.browse_for_dir = unittest.mock.Mock() - QFileDialog.getExistingDirectory = unittest.mock.Mock() sdsd.getWidget("selected_dir").click() - sdsd.browse_for_dir.assert_called_once() + mock_browse.assert_called_once() def test_browse_dialog_updates_session_directory_label(self): example_dir = "C:\\Users\\test_user\\Documents\\test_dir" From 2eb6bc15a742bfcd53532f1e5cb3c1bc3adaade1 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Wed, 24 Apr 2024 16:34:04 +0100 Subject: [PATCH 3/4] Depend on qtpy instead of pyside --- pyproject.toml | 2 +- scripts/eqt_env.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d22dca0..3082427 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3 :: Only"] -dependencies = ["pyside2", "qdarkstyle"] +dependencies = ["qtpy", "qdarkstyle"] [project.optional-dependencies] dev = ["pytest>=6", "pytest-cov", "pytest-timeout"] diff --git a/scripts/eqt_env.yml b/scripts/eqt_env.yml index 185385b..20f64e3 100644 --- a/scripts/eqt_env.yml +++ b/scripts/eqt_env.yml @@ -5,5 +5,5 @@ channels: dependencies: - python - pip - - pyside2 + - qtpy - qdarkstyle From b72d2a2e2c4a934275f6d6558b8e389aeef506ff Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Wed, 24 Apr 2024 16:51:23 +0100 Subject: [PATCH 4/4] Document qtpy in README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8d2325c..aad9e8a 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Via `pip`/`conda`/`mamba`, i.e. any of the following: - `conda install -c conda-forge eqt` - `mamba install -c conda-forge eqt` +Note that `eqt` use the [`qtpy`](https://github.com/spyder-ide/qtpy) abstraction layer for Qt bindings, so can work with either PySide or PyQt bindings. Therefore the package does not depend on either. If the environment does not already have a Qt binding then the user must install either `pyside2` or `pyqt5`. + ## Examples See the [`examples`](examples) directory, e.g. how to launch a `QDialog` with a form inside using `eqt`'s [`QWidget`](examples/dialog_example.py) or [`FormDialog`](examples/dialog_example_2.py).