Skip to content
Open
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
9 changes: 5 additions & 4 deletions pydm/register_pydm_designer_plugin.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
print("Loading PyDM Widgets")

from pydm.utilities import ACTIVE_QT_WRAPPER, QtWrapperTypes
from pydm.widgets.qtplugins import get_all_custom_widgets_in_order


if ACTIVE_QT_WRAPPER == QtWrapperTypes.PYQT5:
from pydm.widgets.qtplugins import * # noqa: E402, F403
elif ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from pydm.widgets import qtplugins
globals().update({pl.plugin_name: pl for pl in get_all_custom_widgets_in_order()})

elif ACTIVE_QT_WRAPPER == QtWrapperTypes.PYSIDE6:
from PySide6.QtDesigner import QDesignerCustomWidgetInterface, QPyDesignerCustomWidgetCollection

import inspect

for _, value in inspect.getmembers(qtplugins):
for value in get_all_custom_widgets_in_order():
if inspect.isclass(value) and issubclass(value, QDesignerCustomWidgetInterface):
QPyDesignerCustomWidgetCollection.addCustomWidget(value())
6 changes: 6 additions & 0 deletions pydm/widgets/qtplugin_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ def qtplugin_factory(
class Plugin(PyDMDesignerPlugin):
__doc__ = "PyDMDesigner Plugin for {}".format(cls.__name__)

plugin_name = cls.__name__
plugin_group = group

def __init__(self):
super().__init__(cls, is_container, group, extensions, icon)

Expand All @@ -102,6 +105,9 @@ class PyDMDesignerPlugin(BASE_PLUGIN_CLASS):
All functions have default returns that can be overridden as necessary.
"""

plugin_name: str
plugin_group: str

def __init__(
self,
cls: Type[QtWidgets.QWidget],
Expand Down
67 changes: 64 additions & 3 deletions pydm/widgets/qtplugins.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import inspect
import logging
import os
from collections import defaultdict

from pydm.utilities.iconfont import IconFont
from .archiver_time_plot import PyDMArchiverTimePlot
Expand Down Expand Up @@ -30,7 +32,7 @@
from .line_edit import PyDMLineEdit
from .logdisplay import PyDMLogDisplay
from .pushbutton import PyDMPushButton
from .qtplugin_base import WidgetCategory, get_widgets_from_entrypoints, qtplugin_factory
from .qtplugin_base import PyDMDesignerPlugin, WidgetCategory, get_widgets_from_entrypoints, qtplugin_factory
from .qtplugin_extensions import (
ArchiveTimeCurveEditorExtension,
BasicSettingsExtension,
Expand Down Expand Up @@ -279,9 +281,68 @@
# Terminator Widget plugin
PyDMTerminatorPlugin = qtplugin_factory(PyDMTerminator, group=WidgetCategory.MISC, extensions=BASE_EXTENSIONS)


# **********************************************
# NOTE: Add in new PyDM widgets above this line.
# **********************************************
def is_designer_widget(WidgetCls: type) -> bool:
"""
Returns True if the object is a designer plugin class that is usable in designer.
"""
return (
inspect.isclass(WidgetCls) and issubclass(WidgetCls, PyDMDesignerPlugin) and WidgetCls is not PyDMDesignerPlugin
)

# Add in designer widget plugins from other classes via entrypoints:
globals().update(**get_widgets_from_entrypoints())

def get_pydm_custom_widgets() -> dict[str, type[PyDMDesignerPlugin]]:
"""
Returns a dictionary of all the widgets defined by PyDM itself.
"""
return {key: value for key, value in globals().items() if is_designer_widget(value)}


def get_all_custom_widgets_in_order() -> list[type[PyDMDesignerPlugin]]:
"""
Yields all custom widgets in the order they should be presented to designer.

The order matters and determines how the groups are ordered and how
widgets within each group are order.

Widgets are added in the order they are encountered, creating new groups as necessary.
There is no sorting done. This is especially unfortunate for entrypoint widgets,
which always load alphabetically by widget name in per-library chunks, leading to a
chaotic group ordering by default.

Here we will sort in the following way:
1. PyDM widget groups in the same order as their corresponding built-in groups
2. PyDM widget groups with no corresponding built-in groups in alphabetical order
3. Entrypoint widgets groups in alphabetical order

Within each group, we will sort widget names alphabetically,
because doing this manually or randomly lead to extra work or confusing results.
"""
pydm_widgets_by_group: dict[str, list[type[PyDMDesignerPlugin]]] = defaultdict(list)
for plugin in get_pydm_custom_widgets().values():
pydm_widgets_by_group[plugin.plugin_group].append(plugin)

entrypoint_widgets_by_group: dict[str, list[type[PyDMDesignerPlugin]]] = defaultdict(list)
for plugin in get_widgets_from_entrypoints().values():
entrypoint_widgets_by_group[plugin.plugin_group].append(plugin)

all_custom_widgets = []
standard_category_order = (
WidgetCategory.CONTAINER,
WidgetCategory.INPUT,
WidgetCategory.DISPLAY,
WidgetCategory.PLOT,
WidgetCategory.DRAWING,
WidgetCategory.MISC,
)
for category in standard_category_order:
all_custom_widgets.extend(sorted(pydm_widgets_by_group[category], key=lambda pl: pl.plugin_name))
for category in pydm_widgets_by_group:
if category not in standard_category_order:
all_custom_widgets.extend(sorted(pydm_widgets_by_group[category], key=lambda pl: pl.plugin_name))
for category in sorted(entrypoint_widgets_by_group):
all_custom_widgets.extend(sorted(entrypoint_widgets_by_group[category], key=lambda pl: pl.plugin_name))
return all_custom_widgets
3 changes: 3 additions & 0 deletions pydm/widgets/tab_bar_qtplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ class TabWidgetPlugin(PyDMDesignerPlugin):

TabClass = PyDMTabWidget

plugin_name = "TabWidgetPlugin"
plugin_group = WidgetCategory.CONTAINER

def __init__(self, extensions=None):
super().__init__(self.TabClass, group=WidgetCategory.CONTAINER, extensions=extensions)

Expand Down
Loading