diff --git a/bec_widgets/bec_dispatcher.py b/bec_widgets/bec_dispatcher.py index d654e7e5..e700866e 100644 --- a/bec_widgets/bec_dispatcher.py +++ b/bec_widgets/bec_dispatcher.py @@ -5,7 +5,7 @@ from typing import Callable from bec_lib import BECClient, messages, ServiceConfig from bec_lib.redis_connector import RedisConsumerThreaded -from PyQt5.QtCore import QObject, pyqtSignal +from qtpy.QtCore import QObject, Signal as pyqtSignal # Adding a new pyqt signal requres a class factory, as they must be part of the class definition # and cannot be dynamically added as class attributes after the class has been defined. diff --git a/bec_widgets/ctrl_c.py b/bec_widgets/ctrl_c.py index d3e7fe47..f755e8ab 100644 --- a/bec_widgets/ctrl_c.py +++ b/bec_widgets/ctrl_c.py @@ -1,38 +1,39 @@ -import signal -import socket -from PyQt5.QtNetwork import QAbstractSocket - - -def setup(app): - app.signalwatchdog = SignalWatchdog() # need to store to keep socket pair alive - signal.signal(signal.SIGINT, make_quit_handler(app)) - - -def make_quit_handler(app): - def handler(*args): - print() # make ^C appear on its own line - app.quit() - - return handler - - -class SignalWatchdog(QAbstractSocket): - def __init__(self): - """ - Propagates system signals from Python to QEventLoop - adapted from https://stackoverflow.com/a/65802260/655404 - """ - super().__init__(QAbstractSocket.SctpSocket, None) - - self.writer, self.reader = writer, reader = socket.socketpair() - writer.setblocking(False) - - fd_writer = writer.fileno() - fd_reader = reader.fileno() - - signal.set_wakeup_fd(fd_writer) # Python hook - self.setSocketDescriptor(fd_reader) # Qt hook - - self.readyRead.connect( - lambda: None - ) # dummy function call that lets the Python interpreter run +# TODO haven't found yet how to deal with QAbstractSocket in qtpy +# import signal +# import socket +# from PyQt5.QtNetwork import QAbstractSocket +# +# +# def setup(app): +# app.signalwatchdog = SignalWatchdog() # need to store to keep socket pair alive +# signal.signal(signal.SIGINT, make_quit_handler(app)) +# +# +# def make_quit_handler(app): +# def handler(*args): +# print() # make ^C appear on its own line +# app.quit() +# +# return handler +# +# +# class SignalWatchdog(QAbstractSocket): +# def __init__(self): +# """ +# Propagates system signals from Python to QEventLoop +# adapted from https://stackoverflow.com/a/65802260/655404 +# """ +# super().__init__(QAbstractSocket.SctpSocket, None) +# +# self.writer, self.reader = writer, reader = socket.socketpair() +# writer.setblocking(False) +# +# fd_writer = writer.fileno() +# fd_reader = reader.fileno() +# +# signal.set_wakeup_fd(fd_writer) # Python hook +# self.setSocketDescriptor(fd_reader) # Qt hook +# +# self.readyRead.connect( +# lambda: None +# ) # dummy function call that lets the Python interpreter run diff --git a/bec_widgets/display_ui_file.py b/bec_widgets/display_ui_file.py index b791c481..527bcdf7 100644 --- a/bec_widgets/display_ui_file.py +++ b/bec_widgets/display_ui_file.py @@ -1,7 +1,7 @@ import os import sys -from PyQt5 import QtWidgets, uic +from qtpy import QtWidgets, uic class UI(QtWidgets.QWidget): diff --git a/bec_widgets/examples/eiger_plot/eiger_plot.py b/bec_widgets/examples/eiger_plot/eiger_plot.py index c0f70acc..c51ab685 100644 --- a/bec_widgets/examples/eiger_plot/eiger_plot.py +++ b/bec_widgets/examples/eiger_plot/eiger_plot.py @@ -6,10 +6,10 @@ import h5py import numpy as np import pyqtgraph as pg import zmq -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import pyqtSlot -from PyQt5.QtGui import QKeySequence -from PyQt5.QtWidgets import ( +from qtpy.QtCore import Signal as pyqtSignal +from qtpy.QtCore import Slot as pyqtSlot +from qtpy.QtGui import QKeySequence +from qtpy.QtWidgets import ( QWidget, QFileDialog, QShortcut, @@ -290,7 +290,7 @@ class EigerPlot(QWidget): if __name__ == "__main__": import sys - from PyQt5.QtWidgets import QApplication + from qtpy.QtWidgets import QApplication app = QApplication(sys.argv) plot = EigerPlot() diff --git a/bec_widgets/examples/extreme/extreme.py b/bec_widgets/examples/extreme/extreme.py index 808d5cae..297113ac 100644 --- a/bec_widgets/examples/extreme/extreme.py +++ b/bec_widgets/examples/extreme/extreme.py @@ -5,8 +5,10 @@ import os import pyqtgraph import pyqtgraph as pg -from PyQt5.QtCore import pyqtSignal, pyqtSlot -from PyQt5.QtWidgets import ( + +from qtpy.QtCore import Signal as pyqtSignal, Slot as pyqtSlot + +from qtpy.QtWidgets import ( QApplication, QWidget, QTableWidgetItem, diff --git a/bec_widgets/examples/mca_readout/mca_plot.py b/bec_widgets/examples/mca_readout/mca_plot.py index 8d4c746c..1b0ad090 100644 --- a/bec_widgets/examples/mca_readout/mca_plot.py +++ b/bec_widgets/examples/mca_readout/mca_plot.py @@ -1,8 +1,8 @@ # import simulation_progress as SP import numpy as np import pyqtgraph as pg -from PyQt5.QtCore import pyqtSignal, pyqtSlot -from PyQt5.QtWidgets import ( +from qtpy.QtCore import Signal as pyqtSignal, Slot as pyqtSlot +from qtpy.QtWidgets import ( QApplication, QVBoxLayout, QWidget, diff --git a/bec_widgets/examples/modular_app/modular_app.py b/bec_widgets/examples/modular_app/modular_app.py index 3978a9b8..2e83d504 100644 --- a/bec_widgets/examples/modular_app/modular_app.py +++ b/bec_widgets/examples/modular_app/modular_app.py @@ -1,7 +1,7 @@ import os -from PyQt5 import uic -from PyQt5.QtWidgets import QMainWindow, QApplication, QVBoxLayout +from qtpy import uic +from qtpy.QtWidgets import QMainWindow, QApplication, QVBoxLayout from bec_widgets.widgets.monitor import BECMonitor diff --git a/bec_widgets/examples/motor_movement/motor_example.py b/bec_widgets/examples/motor_movement/motor_example.py index 727e41eb..3b7fdd20 100644 --- a/bec_widgets/examples/motor_movement/motor_example.py +++ b/bec_widgets/examples/motor_movement/motor_example.py @@ -5,12 +5,12 @@ from functools import partial import numpy as np import pyqtgraph as pg -from PyQt5 import QtGui -from PyQt5.QtCore import QThread, pyqtSlot -from PyQt5.QtCore import pyqtSignal, Qt -from PyQt5.QtGui import QDoubleValidator -from PyQt5.QtGui import QKeySequence -from PyQt5.QtWidgets import ( +from qtpy import QtGui +from qtpy.QtCore import QThread, Slot as pyqtSlot +from qtpy.QtCore import Signal as pyqtSignal, Qt +from qtpy.QtGui import QDoubleValidator +from qtpy.QtGui import QKeySequence +from qtpy.QtWidgets import ( QApplication, QWidget, QFileDialog, @@ -20,8 +20,8 @@ from PyQt5.QtWidgets import ( QPushButton, QFrame, ) -from PyQt5.QtWidgets import QMessageBox -from PyQt5.QtWidgets import QShortcut +from qtpy.QtWidgets import QMessageBox +from qtpy.QtWidgets import QShortcut from pyqtgraph.Qt import QtWidgets, uic, QtCore from bec_lib import MessageEndpoints, messages diff --git a/bec_widgets/examples/oneplot/oneplot.py b/bec_widgets/examples/oneplot/oneplot.py index b28e0c34..442b8abf 100644 --- a/bec_widgets/examples/oneplot/oneplot.py +++ b/bec_widgets/examples/oneplot/oneplot.py @@ -1,11 +1,11 @@ import os import numpy as np -import PyQt5.QtWidgets +import qtpy.QtWidgets import pyqtgraph as pg from bec_lib import MessageEndpoints -from PyQt5.QtCore import pyqtSignal, pyqtSlot -from PyQt5.QtWidgets import QApplication, QTableWidgetItem, QWidget +from qtpy.QtCore import Signal as pyqtSignal, Slot as pyqtSlot +from qtpy.QtWidgets import QApplication, QTableWidgetItem, QWidget from pyqtgraph import mkBrush, mkColor, mkPen from pyqtgraph.Qt import QtCore, uic @@ -123,7 +123,7 @@ class PlotApp(QWidget): ) def update_table( - self, table_widget: PyQt5.QtWidgets.QTableWidget, x: float, y_values: list, column: int + self, table_widget: qtpy.QtWidgets.QTableWidget, x: float, y_values: list, column: int ) -> None: for i, y in enumerate(y_values): table_widget.setItem(i, column, QTableWidgetItem(f"({x}, {y})")) diff --git a/bec_widgets/examples/plotting/crosshair_example.py b/bec_widgets/examples/plotting/crosshair_example.py index f573ad69..77373fbf 100644 --- a/bec_widgets/examples/plotting/crosshair_example.py +++ b/bec_widgets/examples/plotting/crosshair_example.py @@ -1,6 +1,6 @@ import numpy as np import pyqtgraph as pg -from PyQt5.QtWidgets import ( +from qtpy.QtWidgets import ( QApplication, QVBoxLayout, QLabel, diff --git a/bec_widgets/examples/stream_plot/stream_plot.py b/bec_widgets/examples/stream_plot/stream_plot.py index 5bf5b20e..08308676 100644 --- a/bec_widgets/examples/stream_plot/stream_plot.py +++ b/bec_widgets/examples/stream_plot/stream_plot.py @@ -9,8 +9,8 @@ import pyqtgraph import pyqtgraph as pg from bec_lib import messages, MessageEndpoints from bec_lib.redis_connector import MessageObject, RedisConnector -from PyQt5.QtCore import pyqtSlot -from PyQt5.QtWidgets import QCheckBox, QTableWidgetItem +from qtpy.QtCore import Slot as pyqtSlot +from qtpy.QtWidgets import QCheckBox, QTableWidgetItem from pyqtgraph import mkBrush, mkColor, mkPen from pyqtgraph.Qt import QtCore, QtWidgets, uic from pyqtgraph.Qt.QtCore import pyqtSignal @@ -305,7 +305,7 @@ class StreamPlot(QtWidgets.QWidget): if __name__ == "__main__": import argparse - from bec_widgets import ctrl_c + # from bec_widgets import ctrl_c # TODO uncomment when ctrl_c is ready to be compatible with qtpy parser = argparse.ArgumentParser() parser.add_argument( @@ -319,7 +319,7 @@ if __name__ == "__main__": client = bec_dispatcher.client app = QtWidgets.QApplication([]) - ctrl_c.setup(app) + # ctrl_c.setup(app) # TODO uncomment when ctrl_c is ready to be compatible with qtpy plot = StreamPlot(y_value_list=value.signals, client=client) bec_dispatcher.connect_slot(plot.new_proj, "px_stream/proj_nr") diff --git a/bec_widgets/qt_utils/bec_table.py b/bec_widgets/qt_utils/bec_table.py index 10908c6c..f30ec3ed 100644 --- a/bec_widgets/qt_utils/bec_table.py +++ b/bec_widgets/qt_utils/bec_table.py @@ -1,5 +1,5 @@ -from PyQt5.QtWidgets import QTableWidget -from PyQt5.QtCore import Qt +from qtpy.QtWidgets import QTableWidget +from qtpy.QtCore import Qt class BECTable(QTableWidget): diff --git a/bec_widgets/qt_utils/crosshair.py b/bec_widgets/qt_utils/crosshair.py index 72cfe3a5..de708ce6 100644 --- a/bec_widgets/qt_utils/crosshair.py +++ b/bec_widgets/qt_utils/crosshair.py @@ -1,6 +1,8 @@ import numpy as np import pyqtgraph as pg -from PyQt5.QtCore import QObject, pyqtSignal + +# from qtpy.QtCore import QObject, pyqtSignal +from qtpy.QtCore import QObject, Signal as pyqtSignal class Crosshair(QObject): diff --git a/bec_widgets/qt_utils/validator_delegate.py b/bec_widgets/qt_utils/validator_delegate.py index a6195ddc..ac8dcdf4 100644 --- a/bec_widgets/qt_utils/validator_delegate.py +++ b/bec_widgets/qt_utils/validator_delegate.py @@ -1,5 +1,8 @@ -from PyQt5.QtGui import QDoubleValidator -from PyQt5.QtWidgets import QStyledItemDelegate, QLineEdit +# from qtpy.QtGui import QDoubleValidator +# from qtpy.QtWidgets import QStyledItemDelegate, QLineEdit + +from qtpy.QtGui import QDoubleValidator +from qtpy.QtWidgets import QStyledItemDelegate, QLineEdit class DoubleValidationDelegate(QStyledItemDelegate): diff --git a/bec_widgets/qt_utils/widget_io.py b/bec_widgets/qt_utils/widget_io.py index d99714d3..0a22b76e 100644 --- a/bec_widgets/qt_utils/widget_io.py +++ b/bec_widgets/qt_utils/widget_io.py @@ -1,4 +1,4 @@ -from PyQt5.QtWidgets import ( +from qtpy.QtWidgets import ( QApplication, QWidget, QLineEdit, diff --git a/bec_widgets/qt_utils/yaml_dialog.py b/bec_widgets/qt_utils/yaml_dialog.py index 492ee4b0..5e342486 100644 --- a/bec_widgets/qt_utils/yaml_dialog.py +++ b/bec_widgets/qt_utils/yaml_dialog.py @@ -1,5 +1,5 @@ import yaml -from PyQt5.QtWidgets import QFileDialog +from qtpy.QtWidgets import QFileDialog def load_yaml(instance) -> dict: diff --git a/bec_widgets/qtdesigner_plugins/scan2d_plot_plugin.py b/bec_widgets/qtdesigner_plugins/scan2d_plot_plugin.py index 1ff71401..9adbbd40 100644 --- a/bec_widgets/qtdesigner_plugins/scan2d_plot_plugin.py +++ b/bec_widgets/qtdesigner_plugins/scan2d_plot_plugin.py @@ -1,5 +1,5 @@ -from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin -from PyQt5.QtGui import QIcon +from qtpy.QtDesigner import QPyDesignerCustomWidgetPlugin +from qtpy.QtGui import QIcon from bec_widgets.scan2d_plot import BECScanPlot2D diff --git a/bec_widgets/qtdesigner_plugins/scan_plot_plugin.py b/bec_widgets/qtdesigner_plugins/scan_plot_plugin.py index 38412190..3757b9cc 100644 --- a/bec_widgets/qtdesigner_plugins/scan_plot_plugin.py +++ b/bec_widgets/qtdesigner_plugins/scan_plot_plugin.py @@ -1,5 +1,5 @@ -from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin -from PyQt5.QtGui import QIcon +from qtpy.QtDesigner import QPyDesignerCustomWidgetPlugin +from qtpy.QtGui import QIcon from bec_widgets.scan_plot import BECScanPlot diff --git a/bec_widgets/scan2d_plot.py b/bec_widgets/scan2d_plot.py index e1b917c8..50657c63 100644 --- a/bec_widgets/scan2d_plot.py +++ b/bec_widgets/scan2d_plot.py @@ -4,7 +4,7 @@ import numpy as np import pyqtgraph as pg from bec_lib import MessageEndpoints from bec_lib.logger import bec_logger -from PyQt5.QtCore import pyqtProperty, pyqtSlot +from qtpy.QtCore import Property as pyqtProperty, Slot as pyqtSlot from bec_widgets.bec_dispatcher import bec_dispatcher @@ -139,7 +139,7 @@ class BECScanPlot2D(pg.GraphicsView): if __name__ == "__main__": import sys - from PyQt5.QtWidgets import QApplication + from qtpy.QtWidgets import QApplication app = QApplication(sys.argv) diff --git a/bec_widgets/scan_plot.py b/bec_widgets/scan_plot.py index 94e02e57..c15f130f 100644 --- a/bec_widgets/scan_plot.py +++ b/bec_widgets/scan_plot.py @@ -4,7 +4,7 @@ from threading import RLock import pyqtgraph as pg from bec_lib import MessageEndpoints from bec_lib.logger import bec_logger -from PyQt5.QtCore import pyqtProperty, pyqtSlot +from qtpy.QtCore import Property as pyqtProperty, Slot as pyqtSlot from bec_widgets.bec_dispatcher import bec_dispatcher @@ -138,7 +138,7 @@ class BECScanPlot(pg.GraphicsView): if __name__ == "__main__": import sys - from PyQt5.QtWidgets import QApplication + from qtpy.QtWidgets import QApplication app = QApplication(sys.argv) diff --git a/bec_widgets/widgets/monitor/config_dialog.py b/bec_widgets/widgets/monitor/config_dialog.py index 9adf414e..0edf9205 100644 --- a/bec_widgets/widgets/monitor/config_dialog.py +++ b/bec_widgets/widgets/monitor/config_dialog.py @@ -1,8 +1,8 @@ import os -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import ( +from qtpy import uic +from qtpy.QtCore import Signal as pyqtSignal +from qtpy.QtWidgets import ( QApplication, QWidget, QVBoxLayout, diff --git a/bec_widgets/widgets/monitor/monitor.py b/bec_widgets/widgets/monitor/monitor.py index b53bc400..29ff6190 100644 --- a/bec_widgets/widgets/monitor/monitor.py +++ b/bec_widgets/widgets/monitor/monitor.py @@ -5,11 +5,11 @@ import pyqtgraph as pg from pydantic import ValidationError from bec_lib import MessageEndpoints -from PyQt5 import QtCore -from PyQt5.QtCore import pyqtSignal, pyqtSlot -from PyQt5.QtWidgets import QApplication, QTableWidgetItem, QWidget, QMessageBox +from qtpy import QtCore +from qtpy.QtCore import Signal as pyqtSignal, Slot as pyqtSlot +from qtpy.QtWidgets import QApplication, QTableWidgetItem, QWidget, QMessageBox from pyqtgraph import mkPen, mkBrush -from PyQt5 import uic +from qtpy import uic from bec_widgets.bec_dispatcher import bec_dispatcher from bec_widgets.qt_utils import Crosshair, Colors diff --git a/bec_widgets/widgets/scan_control/scan_control.py b/bec_widgets/widgets/scan_control/scan_control.py index dabaa0b7..dd30bcac 100644 --- a/bec_widgets/widgets/scan_control/scan_control.py +++ b/bec_widgets/widgets/scan_control/scan_control.py @@ -1,5 +1,5 @@ import msgpack -from PyQt5.QtWidgets import ( +from qtpy.QtWidgets import ( QApplication, QWidget, QComboBox, diff --git a/setup.py b/setup.py index 3bb7e917..2e191ca0 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ __version__ = "0.31.0" if __name__ == "__main__": setup( - install_requires=["pydantic", "pyqt5", "pyqtgraph", "bec_lib", "zmq", "h5py"], + install_requires=["pydantic", "PyQt6>=6.0", "qtpy", "pyqtgraph", "bec_lib", "zmq", "h5py"], extras_require={"dev": ["pytest", "pytest-random-order", "coverage", "pytest-qt", "black"]}, version=__version__, ) diff --git a/tests/test_config_dialog.py b/tests/test_config_dialog.py index 5e28c285..9279d1c8 100644 --- a/tests/test_config_dialog.py +++ b/tests/test_config_dialog.py @@ -2,7 +2,7 @@ import os import yaml import pytest -from PyQt5.QtWidgets import QTabWidget, QTableWidgetItem +from qtpy.QtWidgets import QTabWidget, QTableWidgetItem from bec_widgets.widgets import ConfigDialog diff --git a/tests/test_crosshair.py b/tests/test_crosshair.py index 78a8d8ac..7b63bb7e 100644 --- a/tests/test_crosshair.py +++ b/tests/test_crosshair.py @@ -1,6 +1,6 @@ import numpy as np import pyqtgraph as pg -from PyQt5.QtCore import QPointF +from qtpy.QtCore import QPointF from bec_widgets.qt_utils import Crosshair diff --git a/tests/test_extreme.py b/tests/test_extreme.py index e16971e1..5e8d058b 100644 --- a/tests/test_extreme.py +++ b/tests/test_extreme.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock, patch import pyqtgraph as pg import pytest -from PyQt5.QtWidgets import QMessageBox +from qtpy.QtWidgets import QMessageBox from bec_widgets.examples.extreme.extreme import PlotApp, ErrorHandler diff --git a/tests/test_scan_control.py b/tests/test_scan_control.py index 0cb95f46..3375d24c 100644 --- a/tests/test_scan_control.py +++ b/tests/test_scan_control.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock import msgpack import pytest -from PyQt5.QtWidgets import QLineEdit +from qtpy.QtWidgets import QLineEdit from bec_widgets.widgets import ScanControl from bec_widgets.qt_utils.widget_io import WidgetIO diff --git a/tests/test_widget_io.py b/tests/test_widget_io.py index c6b822d7..a60842bf 100644 --- a/tests/test_widget_io.py +++ b/tests/test_widget_io.py @@ -1,5 +1,5 @@ import pytest -from PyQt5.QtWidgets import ( +from qtpy.QtWidgets import ( QWidget, QVBoxLayout, QLineEdit, diff --git a/tests/test_yaml_dialog.py b/tests/test_yaml_dialog.py index 5f63c0e1..d74332e7 100644 --- a/tests/test_yaml_dialog.py +++ b/tests/test_yaml_dialog.py @@ -3,7 +3,7 @@ import tempfile from unittest.mock import patch import pytest import yaml -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton +from qtpy.QtWidgets import QWidget, QVBoxLayout, QPushButton from bec_widgets.qt_utils.yaml_dialog import load_yaml, save_yaml @@ -38,7 +38,7 @@ def test_load_yaml(qtbot, example_widget): example_widget.import_button.clicked.connect(load_yaml_wrapper) # Mock user selecting the file in the dialog - with patch("PyQt5.QtWidgets.QFileDialog.getOpenFileName", return_value=(temp_file.name, "")): + with patch("qtpy.QtWidgets.QFileDialog.getOpenFileName", return_value=(temp_file.name, "")): example_widget.import_button.click() assert example_widget.config == {"name": "test", "value": 42} @@ -59,7 +59,7 @@ def test_save_yaml(qtbot, example_widget): example_widget.export_button.clicked.connect(save_yaml_wrapper) # Mock user selecting the file in the dialog - with patch("PyQt5.QtWidgets.QFileDialog.getSaveFileName", return_value=(temp_file_path, "")): + with patch("qtpy.QtWidgets.QFileDialog.getSaveFileName", return_value=(temp_file_path, "")): example_widget.export_button.click() # Check if the data was saved correctly