Skip to content

ENH: Add PyDMWindow widget to configure hiding the nav bar, menu bar, and status bar components on first load #1220

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion docs/source/channel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
Channel
========================

.. autoclass:: channel.PyDMChannel
.. autoclass:: pydm.widgets.channel.PyDMChannel
:members:
1 change: 1 addition & 0 deletions docs/source/widgets/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Container Widgets
frame.rst
tab_widget.rst
template_repeater.rst
window.rst

Drawing Widgets
---------------
Expand Down
32 changes: 32 additions & 0 deletions docs/source/widgets/window.rst
Original file line number Diff line number Diff line change
@@ -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:
52 changes: 52 additions & 0 deletions examples/window/window.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Display</class>
<widget class="PyDMWindow" name="Display">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>PyDMWindow</string>
</property>
<property name="hideMenuBar" stdset="0">
<bool>true</bool>
</property>
<property name="hideNavBar" stdset="0">
<bool>true</bool>
</property>
<property name="hideStatusBar" stdset="0">
<bool>true</bool>
</property>
<widget class="QLabel" name="Label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>380</width>
<height>280</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This display is using a PyDMWindow widget as the root widget. This allows it to customize some otherwise unavailable properties.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>PyDMWindow</class>
<extends>QWidget</extends>
<header>pydm.widgets.window</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
34 changes: 24 additions & 10 deletions pydm/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions pydm/tests/test_plugins_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions pydm/tests/widgets/test_window.py
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions pydm/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"PyDMTabWidget",
"PyDMTemplateRepeater",
"PyDMNTTable",
"PyDMWindow",
]

from .channel import PyDMChannel
Expand Down Expand Up @@ -75,3 +76,4 @@
from .tab_bar import PyDMTabWidget
from .template_repeater import PyDMTemplateRepeater
from .nt_table import PyDMNTTable
from .window import PyDMWindow
2 changes: 1 addition & 1 deletion pydm/widgets/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand Down
6 changes: 6 additions & 0 deletions pydm/widgets/qtplugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
106 changes: 106 additions & 0 deletions pydm/widgets/window.py
Original file line number Diff line number Diff line change
@@ -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