diff --git a/docs/source/channel.rst b/docs/source/channel.rst index 575012129..62cdb69f5 100644 --- a/docs/source/channel.rst +++ b/docs/source/channel.rst @@ -2,5 +2,5 @@ Channel ======================== -.. autoclass:: channel.PyDMChannel +.. autoclass:: pydm.widgets.channel.PyDMChannel :members: diff --git a/docs/source/widgets/index.rst b/docs/source/widgets/index.rst index 8287502bb..caf71468a 100644 --- a/docs/source/widgets/index.rst +++ b/docs/source/widgets/index.rst @@ -58,6 +58,7 @@ Container Widgets frame.rst tab_widget.rst template_repeater.rst + window.rst Drawing Widgets --------------- diff --git a/docs/source/widgets/window.rst b/docs/source/widgets/window.rst new file mode 100644 index 000000000..a163bfe2a --- /dev/null +++ b/docs/source/widgets/window.rst @@ -0,0 +1,32 @@ +####################### +PyDMWindow +####################### + +The PyDM Window Widget is a container widget that allows the display creator to set certain global display +properties. It is currently used to hide specific UI elements when the display is the first loaded display +in the running PyDM instance. + +Using the PyDM Window Widget in Designer +======================================== + +In designer, when creating a new display, select PyDMWindow as the base widget. + + +Widget Properties +================= + +============= ==== =========== +Property Type Description +============= ==== =========== +hideMenuBar bool Hide the menu bar if this is the first loaded display. +hideNavBar bool Hide the nav bar if this is the first loaded display. +hideStatusBar bool Hide the status bar if this is the first loaded display. +============= ==== =========== + + +API Documentation +================= + +.. autoclass:: pydm.widgets.window.PyDMWindow + :members: + :show-inheritance: diff --git a/examples/window/window.ui b/examples/window/window.ui new file mode 100644 index 000000000..f3bc6fa1c --- /dev/null +++ b/examples/window/window.ui @@ -0,0 +1,52 @@ + + + Display + + + + 0 + 0 + 400 + 300 + + + + PyDMWindow + + + true + + + true + + + true + + + + + 10 + 10 + 380 + 280 + + + + <html><head/><body><p>This display is using a PyDMWindow widget as the root widget. This allows it to customize some otherwise unavailable properties.</p><p><br/></p><p>Currently it is used for hiding specific parts of the PyDM interface, including the menu bar, nav bar, and status bar. Disabling these elements can make your popup displays look nicer!</p></body></html> + + + true + + + + + + PyDMWindow + QWidget +
pydm.widgets.window
+ 1 +
+
+ + +
diff --git a/pydm/main_window.py b/pydm/main_window.py index 0249e99a7..5eb48fcf2 100644 --- a/pydm/main_window.py +++ b/pydm/main_window.py @@ -39,6 +39,7 @@ def __init__( self.iconFont = IconFont() self._display_widget = None self._showing_file_path_in_title_bar = False + self._display_widget_has_been_shown = False # style sheet change flag self.isSS_Changed = False @@ -91,16 +92,7 @@ def __init__( self.showMacros.triggered.connect(self.show_macro_window) self.ui.actionQuit.triggered.connect(self.quit_main_window) - if hide_nav_bar: - self.toggle_nav_bar(False) - self.ui.actionShow_Navigation_Bar.setChecked(False) - if hide_menu_bar: - # Toggle the menu bar via the QAction so that the menu item - # stays in sync with menu visibility. - self.ui.actionShow_Menu_Bar.activate(QAction.Trigger) - if hide_status_bar: - self.toggle_status_bar(False) - self.ui.actionShow_Status_Bar.setChecked(False) + self.hide_window_components(hide_nav_bar, hide_menu_bar, hide_status_bar) # Try to find the designer binary. self.ui.actionEdit_in_Designer.setEnabled(False) @@ -140,6 +132,16 @@ def set_display_widget(self, new_widget): self.enable_disable_navigation() self.update_window_title() self.add_menu_items() + + # We want to respect the user's choices after the first display has loaded + if not self._display_widget_has_been_shown: + self.hide_window_components( + self._display_widget.property("hideNavBar"), + self._display_widget.property("hideMenuBar"), + self._display_widget.property("hideStatusBar"), + ) + self._display_widget_has_been_shown = True + # Resizing to the new widget's dimensions needs to be # done on the event loop for some reason - you can't # just do it here. @@ -264,6 +266,18 @@ def update_window_title(self): title += " [Read Only Mode]" self.setWindowTitle(title) + def hide_window_components(self, hide_nav_bar, hide_menu_bar, hide_status_bar): + if hide_nav_bar: + self.toggle_nav_bar(False) + self.ui.actionShow_Navigation_Bar.setChecked(False) + if hide_menu_bar: + # Toggle the menu bar via the QAction so that the menu item + # stays in sync with menu visibility. + self.ui.actionShow_Menu_Bar.activate(QAction.Trigger) + if hide_status_bar: + self.toggle_status_bar(False) + self.ui.actionShow_Status_Bar.setChecked(False) + @property def showing_file_path_in_title_bar(self): return self._showing_file_path_in_title_bar diff --git a/pydm/tests/test_plugins_import.py b/pydm/tests/test_plugins_import.py index 3dfaeaa10..78abbf801 100644 --- a/pydm/tests/test_plugins_import.py +++ b/pydm/tests/test_plugins_import.py @@ -60,6 +60,13 @@ def test_import_frame_plugin(): qtplugin_factory(PyDMFrame, is_container=True) +def test_import_window_plugin(): + # Window plugin + from ..widgets.window import PyDMWindow + + qtplugin_factory(PyDMWindow, is_container=True) + + def test_import_enum_button_plugin(): # Enum Button plugin from ..widgets.enum_button import PyDMEnumButton diff --git a/pydm/tests/widgets/test_window.py b/pydm/tests/widgets/test_window.py new file mode 100644 index 000000000..c8d03bbdc --- /dev/null +++ b/pydm/tests/widgets/test_window.py @@ -0,0 +1,28 @@ +# Unit Tests for the Window Widget + +from ...widgets.window import PyDMWindow + + +# -------------------- +# POSITIVE TEST CASES +# -------------------- + + +def test_construct(qtbot): + """ + Test the construction of the widget. + + Expectations: + The correct default values are assigned to the widget's attributes. + + Parameters + ---------- + qtbot : fixture + pytest-qt window for widget test + """ + pydm_window = PyDMWindow() + qtbot.addWidget(pydm_window) + + assert pydm_window._hide_menu_bar is False + assert pydm_window._hide_nav_bar is False + assert pydm_window._hide_status_bar is False diff --git a/pydm/widgets/__init__.py b/pydm/widgets/__init__.py index 199ff295e..ee0a321fc 100644 --- a/pydm/widgets/__init__.py +++ b/pydm/widgets/__init__.py @@ -35,6 +35,7 @@ "PyDMTabWidget", "PyDMTemplateRepeater", "PyDMNTTable", + "PyDMWindow", ] from .channel import PyDMChannel @@ -75,3 +76,4 @@ from .tab_bar import PyDMTabWidget from .template_repeater import PyDMTemplateRepeater from .nt_table import PyDMNTTable +from .window import PyDMWindow diff --git a/pydm/widgets/frame.py b/pydm/widgets/frame.py index 8210ed432..e9d4ac492 100644 --- a/pydm/widgets/frame.py +++ b/pydm/widgets/frame.py @@ -11,7 +11,7 @@ class PyDMFrame(QFrame, PyDMWidget): Parameters ---------- parent : QWidget - The parent widget for the Label + The parent widget for the Frame init_channel : str, optional The channel to be used by the widget. """ diff --git a/pydm/widgets/qtplugins.py b/pydm/widgets/qtplugins.py index 28806c288..c18c91ab5 100644 --- a/pydm/widgets/qtplugins.py +++ b/pydm/widgets/qtplugins.py @@ -25,6 +25,7 @@ from .enum_button import PyDMEnumButton from .enum_combo_box import PyDMEnumComboBox from .frame import PyDMFrame +from .window import PyDMWindow from .image import PyDMImageView from .label import PyDMLabel from .line_edit import PyDMLineEdit @@ -201,6 +202,11 @@ PyDMFrame, group=WidgetCategory.CONTAINER, is_container=True, extensions=BASE_EXTENSIONS, icon=ifont.icon("expand") ) +# Window plugin +PyDMWindowPlugin = qtplugin_factory( + PyDMWindow, group=WidgetCategory.CONTAINER, is_container=True, extensions=BASE_EXTENSIONS, icon=ifont.icon("expand") +) + # Image plugin PyDMImageViewPlugin = qtplugin_factory( PyDMImageView, group=WidgetCategory.DISPLAY, extensions=BASE_EXTENSIONS, icon=ifont.icon("camera") diff --git a/pydm/widgets/window.py b/pydm/widgets/window.py new file mode 100644 index 000000000..fce71c5b5 --- /dev/null +++ b/pydm/widgets/window.py @@ -0,0 +1,106 @@ +import warnings +from qtpy.QtWidgets import QWidget +from qtpy.QtCore import Property +from typing import Optional +from .base import is_qt_designer + + +class PyDMWindow(QWidget): + """ + QWidget with support for some custom PyDM properties. Right now it only + supports disabling the menu bar, nav bar, and status bar by default. This + widget will only function if it is at the root of the UI hierarchy. + This class inherits from QWidget. It is NOT a PyDMWidget. + + Parameters + ---------- + parent : QWidget + The parent widget for the Window. Should ideally be None + """ + + def __init__(self, parent: Optional[QWidget] = None): + if parent is not None and not is_qt_designer(): + warnings.warn("PyDMWindow must be at the root of the UI hierarchy, or it will not function properly!") + + super().__init__(parent) + self._hide_menu_bar = False + self._hide_nav_bar = False + self._hide_status_bar = False + + @Property(bool) + def hideMenuBar(self): + """ + Whether or not the widget should automatically disable the + menu bar when the display is loaded. + + Returns + ------- + hide_menu_bar : bool + The configured value + """ + return self._hide_menu_bar + + @hideMenuBar.setter + def hideMenuBar(self, new_val): + """ + Whether or not the widget should automatically disable the + menu bar when the display is loaded. + + Parameters + ---------- + new_val : bool + The new configuration to use + """ + self._hide_menu_bar = new_val + + @Property(bool) + def hideNavBar(self): + """ + Whether or not the widget should automatically disable the + nav bar when the display is loaded. + + Returns + ------- + hide_nav_bar : bool + The configured value + """ + return self._hide_nav_bar + + @hideNavBar.setter + def hideNavBar(self, new_val): + """ + Whether or not the widget should automatically disable the + nav bar when the display is loaded. + + Parameters + ---------- + new_val : bool + The new configuration to use + """ + self._hide_nav_bar = new_val + + @Property(bool) + def hideStatusBar(self): + """ + Whether or not the widget should automatically disable the + status bar when the display is loaded. + + Returns + ------- + hide_status_bar : bool + The configured value + """ + return self._hide_status_bar + + @hideStatusBar.setter + def hideStatusBar(self, new_val): + """ + Whether or not the widget should automatically disable the + status bar when the display is loaded. + + Parameters + ---------- + new_val : bool + The new configuration to use + """ + self._hide_status_bar = new_val