Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use QtPy abstraction layer (#144). GHA tests with PySide2 and PyQt5 #146

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ jobs:
strategy:
matrix:
python: [3.7, 3.11]
qtbindings: ['PySide2', 'PyQt5']
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
qt-bindings: ${{ matrix.qtbindings }}
- run: pip install -U .[dev]
- run: pip install ${{ matrix.qtbindings }}
- run: pytest
deploy:
needs: [test]
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 2.0.0

- Use `qtpy` as virtual Qt binding package. GHA unit tests are run with PySide2 and PyQt5 (#146)

# Version 1.0.0
- Update "pre-commit-config.yaml" (#136)
- Change order of widget states (#129)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
4 changes: 2 additions & 2 deletions eqt/threading/QtThreading.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion eqt/ui/FormDialog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PySide2 import QtWidgets
from qtpy import QtWidgets

from . import UIFormFactory

Expand Down
6 changes: 3 additions & 3 deletions eqt/ui/MainWindowWithProgressDialogs.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions eqt/ui/MainWindowWithSessionManagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions eqt/ui/ProgressTimerDialog.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion eqt/ui/ReOrderableListWidget.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PySide2 import QtCore, QtWidgets
from qtpy import QtCore, QtWidgets


class ReOrderableListWidget(QtWidgets.QTableWidget):
Expand Down
4 changes: 2 additions & 2 deletions eqt/ui/SessionDialogs.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
2 changes: 1 addition & 1 deletion eqt/ui/UIFormWidget.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from warnings import warn

from PySide2 import QtWidgets
from qtpy import QtWidgets

from .UISliderWidget import UISliderWidget

Expand Down
6 changes: 3 additions & 3 deletions eqt/ui/UIMultiStepWidget.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down
4 changes: 2 additions & 2 deletions eqt/ui/UISliderWidget.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down
6 changes: 3 additions & 3 deletions eqt/ui/UIStackedWidget.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
4 changes: 2 additions & 2 deletions examples/MainWindowWithSessionManagement_example.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/advanced_dialog_example.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/dialog_example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

from PySide2 import QtWidgets
from qtpy import QtWidgets

from eqt.ui import UIFormFactory

Expand Down
2 changes: 1 addition & 1 deletion examples/dialog_example_2.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

from PySide2 import QtWidgets
from qtpy import QtWidgets

from eqt.ui import FormDialog

Expand Down
2 changes: 1 addition & 1 deletion examples/dialog_example_3_save_default.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys

import utilitiesForExamples as utex
from PySide2 import QtWidgets
from qtpy import QtWidgets

from eqt.ui import FormDialog

Expand Down
2 changes: 1 addition & 1 deletion examples/dialog_multistep_example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

from PySide2 import QtWidgets
from qtpy import QtWidgets

from eqt.ui import UIFormFactory, UIMultiStepFactory

Expand Down
2 changes: 1 addition & 1 deletion examples/dialog_save_state_example.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/insert_widgets_example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

from PySide2 import QtWidgets
from qtpy import QtWidgets

from eqt.ui import FormDialog, UIFormWidget

Expand Down
4 changes: 2 additions & 2 deletions examples/progress_timer_dialog_example.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/remove_widgets_example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

from PySide2 import QtWidgets
from qtpy import QtWidgets

from eqt.ui import FormDialog, UIFormWidget

Expand Down
2 changes: 1 addition & 1 deletion examples/reorderable_list_widget_example.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion examples/utilitiesForExamples.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PySide2 import QtWidgets
from qtpy import QtWidgets

from eqt.ui.UISliderWidget import UISliderWidget

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion scripts/eqt_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ channels:
dependencies:
- python
- pip
- pyside2
- qtpy
- qdarkstyle
2 changes: 1 addition & 1 deletion test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os

from PySide2 import QtWidgets
from pytest import skip
from qtpy import QtWidgets

from eqt.ui import FormDialog

Expand Down
6 changes: 3 additions & 3 deletions test/dialog_example_2_test.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
4 changes: 2 additions & 2 deletions test/test_MainWindowWithSessionManagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 5 additions & 7 deletions test/test_SessionDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"
Expand Down
6 changes: 3 additions & 3 deletions test/test__formUI_status_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading