Skip to content

Commit

Permalink
Merge pull request slaclab#1155 from nstelter-slac/support_both_pyqt5…
Browse files Browse the repository at this point in the history
…_pyside6_enums

Support both pyqt5 pyside6 enums
  • Loading branch information
jbellister-slac authored Feb 20, 2025
2 parents 239d255 + 6294391 commit 0f0d71d
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def close(self):
self.pv.connection_callbacks = []
self.pv.disconnect()
except KeyError:
# The PV was no longer availbale.
# The PV was no longer available.
pass


Expand Down
29 changes: 29 additions & 0 deletions pydm/utilities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import errno

from typing import List, Optional
from enum import IntEnum

from qtpy import QtCore, QtGui, QtWidgets

Expand Down Expand Up @@ -39,6 +40,34 @@
logger = logging.getLogger(__name__)


# The qtpy abstraction layer decides which qt python wrapper to use by the QT_API environment variable.
# Currently, we only intend for PyDM to support PyQt5 (legacy) and PySide6.
# ACTIVE_QT_WRAPPER is implemented for easier access to the QT_API env variable,
# since we need to know which wrapper is currently being used to support both pyqt5 and pyside6.
class QtWrapperTypes(IntEnum):
UNSUPPORTED = 0
PYSIDE6 = 1
PYQT5 = 2


ACTIVE_QT_WRAPPER = QtWrapperTypes.UNSUPPORTED

# QT_API should be set according to the qtpy docs: https://github.com/spyder-ide/qtpy?tab=readme-ov-file#requirements
qt_api = os.getenv("QT_API", "").lower()
if qt_api == "pyside6":
ACTIVE_QT_WRAPPER = QtWrapperTypes.PYSIDE6
elif qt_api == "pyqt5":
ACTIVE_QT_WRAPPER = QtWrapperTypes.PYQT5

if ACTIVE_QT_WRAPPER == QtWrapperTypes.UNSUPPORTED:
error_message = (
"The QT_API variable is not set to a supported Qt Python wrapper "
"(PySide6 or PyQt5). Please set QT_API to 'pyside6' or 'pyqt5'."
)
logger.error(error_message)
raise RuntimeError(error_message)


def is_ssh_session():
"""
Whether or not this is a SSH session.
Expand Down
17 changes: 17 additions & 0 deletions pydm/widgets/colormaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

import numpy as np
from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes

__all__ = ["magma", "inferno", "plasma", "viridis", "jet", "monochrome", "hot"]

Expand Down Expand Up @@ -1079,6 +1080,22 @@ class PyDMColorMap(object):
Hot = 6


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtCore import QEnum
from enum import Enum

@QEnum
# overrides prev enum def
class PyDMColorMap(Enum): # noqa F811
Magma = 0
Inferno = 1
Plasma = 2
Viridis = 3
Jet = 4
Monochrome = 5
Hot = 6


for name, data in (
(PyDMColorMap.Magma, np.array(_magma_data)),
(PyDMColorMap.Inferno, np.array(_inferno_data)),
Expand Down
22 changes: 20 additions & 2 deletions pydm/widgets/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from qtpy import QtWidgets, QtCore

from .base import PyDMWritableWidget, PyDMWidget
from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes

logger = logging.getLogger(__name__)

Expand All @@ -11,8 +12,22 @@ class TimeBase(object):
Seconds = 1


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtCore import QEnum
from enum import Enum

@QEnum
# overrides prev enum def
class TimeBase(Enum): # noqa F811
Milliseconds = 0
Seconds = 1


class PyDMDateTimeEdit(QtWidgets.QDateTimeEdit, PyDMWritableWidget):
QtCore.Q_ENUMS(TimeBase)
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from PyQt5.QtCore import Q_ENUM

Q_ENUM(TimeBase)

# Make enum definitions known to this class
Milliseconds = TimeBase.Milliseconds
Expand Down Expand Up @@ -113,7 +128,10 @@ def value_changed(self, new_val):


class PyDMDateTimeLabel(QtWidgets.QLabel, PyDMWidget):
QtCore.Q_ENUMS(TimeBase)
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from PyQt5.QtCore import Q_ENUM

Q_ENUM(TimeBase)

# Make enum definitions known to this class
Milliseconds = TimeBase.Milliseconds
Expand Down
17 changes: 17 additions & 0 deletions pydm/widgets/display_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import logging
import warnings

from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes

logger = logging.getLogger(__name__)


Expand All @@ -24,6 +26,21 @@ class DisplayFormat(object):
Binary = 5


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtCore import QEnum
from enum import Enum

@QEnum
# overrides prev enum def
class DisplayFormat(Enum): # noqa F811
Default = 0
String = 1
Decimal = 2
Exponential = 3
Hex = 4
Binary = 5


def parse_value_for_display(
value: Any,
precision: int,
Expand Down
19 changes: 17 additions & 2 deletions pydm/widgets/enum_button.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
import logging

from qtpy.QtCore import Qt, QSize, Property, Slot, Q_ENUMS, QMargins
from qtpy.QtCore import Qt, QSize, Property, Slot, QMargins
from qtpy.QtGui import QPainter
from qtpy.QtWidgets import QWidget, QButtonGroup, QGridLayout, QPushButton, QRadioButton, QStyleOption, QStyle

from .base import PyDMWritableWidget
from .. import data_plugins
from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes


class WidgetType(object):
PushButton = 0
RadioButton = 1


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtCore import QEnum
from enum import Enum

@QEnum
# overrides prev enum def
class WidgetType(Enum): # noqa: F811
PushButton = 0
RadioButton = 1


class_for_type = [QPushButton, QRadioButton]

logger = logging.getLogger(__name__)
Expand All @@ -38,7 +50,10 @@ class PyDMEnumButton(QWidget, PyDMWritableWidget):
Emitted when the user changes the value.
"""

Q_ENUMS(WidgetType)
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from PyQt5.QtCore import Q_ENUM

Q_ENUM(WidgetType)
WidgetType = WidgetType

# Make enum definitions known to this class
Expand Down
34 changes: 30 additions & 4 deletions pydm/widgets/image.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from qtpy.QtWidgets import QActionGroup
from qtpy.QtCore import Signal, Slot, Property, QTimer, Q_ENUMS, QThread
from qtpy.QtCore import Signal, Slot, Property, QTimer, QThread
from pyqtgraph import ImageView, PlotItem
from pyqtgraph import ColorMap
from pyqtgraph.graphicsItems.ViewBox.ViewBoxMenu import ViewBoxMenu
Expand All @@ -8,6 +8,7 @@
from .channel import PyDMChannel
from .colormaps import cmaps, cmap_names, PyDMColorMap
from .base import PyDMWidget
from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes

logger = logging.getLogger(__name__)

Expand All @@ -19,6 +20,17 @@ class ReadingOrder(object):
Clike = 1


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtCore import QEnum
from enum import Enum

@QEnum
# overrides prev enum def
class ReadingOrder(Enum): # noqa F811
Fortranlike = 0
Clike = 1


class DimensionOrder(object):
"""
Class to build DimensionOrder ENUM property.
Expand All @@ -41,6 +53,17 @@ class DimensionOrder(object):
WidthFirst = 1


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtCore import QEnum
from enum import Enum

@QEnum
# overrides prev enum def
class DimensionOrder(Enum): # noqa F811
HeightFirst = 0
WidthFirst = 1


class ImageUpdateThread(QThread):
updateSignal = Signal(list)

Expand Down Expand Up @@ -126,9 +149,12 @@ class PyDMImageView(ImageView, PyDMWidget):
ReadingOrder = ReadingOrder
DimensionOrder = DimensionOrder

Q_ENUMS(ReadingOrder)
Q_ENUMS(DimensionOrder)
Q_ENUMS(PyDMColorMap)
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from PyQt5.QtCore import Q_ENUM

Q_ENUM(ReadingOrder)
Q_ENUM(DimensionOrder)
Q_ENUM(PyDMColorMap)

# Make enum definitions known to this class
Fortranlike = ReadingOrder.Fortranlike
Expand Down
14 changes: 10 additions & 4 deletions pydm/widgets/label.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from .base import PyDMWidget, TextFormatter, str_types
from qtpy.QtWidgets import QLabel, QApplication
from qtpy.QtCore import Qt, Property, Q_ENUMS
from qtpy.QtCore import Qt, Property
from .display_format import DisplayFormat, parse_value_for_display
from pydm.utilities import is_pydm_app, is_qt_designer
from pydm import config
from pydm.widgets.base import only_if_channel_set
from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes

_labelRuleProperties = {"Text": ["value_changed", str]}

Expand All @@ -27,16 +28,21 @@ class PyDMLabel(QLabel, TextFormatter, PyDMWidget, new_properties=_labelRuleProp
The channel to be used by the widget.
"""

if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from PyQt5.QtCore import Q_ENUM

Q_ENUM(DisplayFormat)

DisplayFormat = DisplayFormat

# Make enum definitions known to this class
Default = DisplayFormat.Default
String = DisplayFormat.String
Decimal = DisplayFormat.Decimal
Exponential = DisplayFormat.Exponential
Hex = DisplayFormat.Hex
Binary = DisplayFormat.Binary

Q_ENUMS(DisplayFormat)
DisplayFormat = DisplayFormat

def __init__(self, parent=None, init_channel=None):
QLabel.__init__(self, parent)
PyDMWidget.__init__(self, init_channel=init_channel)
Expand Down
8 changes: 6 additions & 2 deletions pydm/widgets/line_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import logging
from functools import partial
from qtpy.QtWidgets import QLineEdit, QMenu, QApplication
from qtpy.QtCore import Property, Q_ENUMS, Qt
from qtpy.QtCore import Property, Qt
from qtpy.QtGui import QFocusEvent
from .. import utilities
from .base import PyDMWritableWidget, TextFormatter, str_types
from .display_format import DisplayFormat, parse_value_for_display
from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes

logger = logging.getLogger(__name__)

Expand All @@ -29,7 +30,10 @@ class PyDMLineEdit(QLineEdit, TextFormatter, PyDMWritableWidget):
The channel to be used by the widget.
"""

Q_ENUMS(DisplayFormat)
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from PyQt5.QtCore import Q_ENUM

Q_ENUM(DisplayFormat)
DisplayFormat = DisplayFormat

# Make enum definitions known to this class
Expand Down
35 changes: 33 additions & 2 deletions pydm/widgets/logdisplay.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from collections import OrderedDict

from qtpy.QtCore import QObject, Slot, Signal, Property, Q_ENUMS, QSize
from qtpy.QtCore import QObject, Slot, Signal, Property, QSize
from qtpy.QtWidgets import (
QWidget,
QPlainTextEdit,
Expand All @@ -17,6 +17,8 @@
)
from qtpy.QtGui import QPainter

from ..utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -108,6 +110,32 @@ def as_dict():
return OrderedDict(sorted(entries, key=lambda x: x[1], reverse=False))


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtCore import QEnum
from enum import Enum

@QEnum
class LogLevels(Enum): # noqa F811
NOTSET = 0
DEBUG = 10
INFO = 20
WARNING = 30
ERROR = 40
CRITICAL = 50

@staticmethod
def as_dict():
"""
Returns an ordered dict of LogLevels ordered by value.
Returns
-------
OrderedDict
"""
# First let's remove the internals
entries = [(k, v.value) for k, v in LogLevels.__members__.items()]
return OrderedDict(sorted(entries, key=lambda x: x[1], reverse=False))


class PyDMLogDisplay(QWidget):
"""
Standard display for Log Output
Expand All @@ -129,7 +157,10 @@ class PyDMLogDisplay(QWidget):
"""

Q_ENUMS(LogLevels)
if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from PyQt5.QtCore import Q_ENUM

Q_ENUM(LogLevels)
LogLevels = LogLevels

# Make enum definitions known to this class
Expand Down
Loading

0 comments on commit 0f0d71d

Please sign in to comment.