Skip to content

Commit

Permalink
Merge pull request #1038 from nstelter-slac/standard_icon_support_des…
Browse files Browse the repository at this point in the history
…igner

ENH: Add propertry for setting Qt Standard Icons in designer
  • Loading branch information
jbellister-slac authored Nov 4, 2023
2 parents ec627c2 + 951bd24 commit e1dcbe0
Show file tree
Hide file tree
Showing 8 changed files with 730 additions and 16 deletions.
403 changes: 403 additions & 0 deletions examples/icons/icons.ui

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions pydm/tests/widgets/test_pushbutton.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,28 @@ def test_construct(qtbot, label, press_value, relative, init_channel, icon_font_
button_icon_pixmap = pydm_pushbutton.icon().pixmap(size)
assert icon_pixmap.toImage() == button_icon_pixmap.toImage()

# verify that qt standard icons can be set through our custom property
style = pydm_pushbutton.style()
test_icon = style.standardIcon(style.SP_DesktopIcon)
test_icon_image = test_icon.pixmap(size).toImage()

pydm_pushbutton.PyDMIcon = "SP_DesktopIcon"
push_btn_icon = pydm_pushbutton.icon()
push_btn_icon_image = push_btn_icon.pixmap(size).toImage()

assert test_icon_image == push_btn_icon_image

# verify that "Font Awesome" icons can be set through our custom property
icon_f = IconFont()
test_icon = icon_f.icon("eye-slash", color=None)
test_icon_image = test_icon.pixmap(size).toImage()

pydm_pushbutton.PyDMIcon = "eye-slash"
push_btn_icon = pydm_pushbutton.icon()
push_btn_icon_image = push_btn_icon.pixmap(size).toImage()

assert test_icon_image == push_btn_icon_image

assert pydm_pushbutton.showConfirmDialog is False
assert pydm_pushbutton.confirmMessage == PyDMPushButton.DEFAULT_CONFIRM_MESSAGE
assert pydm_pushbutton.passwordProtected is False
Expand Down
37 changes: 36 additions & 1 deletion pydm/tests/widgets/test_related_display_button.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os
import pytest
import sys
from qtpy.QtCore import Qt
from qtpy.QtCore import Qt, QSize
from qtpy.QtWidgets import QApplication
from ...utilities.stylesheet import global_style
from ...widgets.related_display_button import PyDMRelatedDisplayButton
from ...utilities import IconFont

test_ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../test_data", "test.ui")
test_ui_path_with_stylesheet = os.path.join(
Expand Down Expand Up @@ -48,6 +49,40 @@ def test_press_with_filename(qtbot):
button._rebuild_menu()
qtbot.mouseRelease(button, Qt.LeftButton)

# verify default icon is set as expected
DEFAULT_ICON_NAME = "file"
DEFAULT_ICON_SIZE = QSize(16, 16)

default_icon = IconFont().icon(DEFAULT_ICON_NAME)

default_icon_pixmap = default_icon.pixmap(DEFAULT_ICON_SIZE)
related_display_button_icon_pixmap = button.icon().pixmap(DEFAULT_ICON_SIZE)

assert related_display_button_icon_pixmap.toImage() == default_icon_pixmap.toImage()
assert button.cursor().pixmap().toImage() == default_icon_pixmap.toImage()

# verify that qt standard icons can be set through our custom property
style = button.style()
test_icon = style.standardIcon(style.SP_DesktopIcon)
test_icon_image = test_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

button.PyDMIcon = "SP_DesktopIcon"
shell_cmd_icon = button.icon()
shell_cmd_icon_image = shell_cmd_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

assert test_icon_image == shell_cmd_icon_image

# verify that "Font Awesome" icons can be set through our custom property
icon_f = IconFont()
test_icon = icon_f.icon("eye-slash", color=None)
test_icon_image = test_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

button.PyDMIcon = "eye-slash"
button_icon = button.icon()
push_btn_icon_image = button_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

assert test_icon_image == push_btn_icon_image

def check_title():
assert "Form" in QApplication.instance().main_window.windowTitle()

Expand Down
22 changes: 22 additions & 0 deletions pydm/tests/widgets/test_shell_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,28 @@ def test_construct(qtbot, command, title):
assert shell_cmd_icon_pixmap.toImage() == default_icon_pixmap.toImage()
assert pydm_shell_command.cursor().pixmap().toImage() == default_icon_pixmap.toImage()

# verify that qt standard icons can be set through our custom property
style = pydm_shell_command.style()
test_icon = style.standardIcon(style.SP_DesktopIcon)
test_icon_image = test_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

pydm_shell_command.PyDMIcon = "SP_DesktopIcon"
shell_cmd_icon = pydm_shell_command.icon()
shell_cmd_icon_image = shell_cmd_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

assert test_icon_image == shell_cmd_icon_image

# verify that "Font Awesome" icons can be set through our custom property
icon_f = IconFont()
test_icon = icon_f.icon("eye-slash", color=None)
test_icon_image = test_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

pydm_shell_command.PyDMIcon = "eye-slash"
shell_cmd_icon = pydm_shell_command.icon()
shell_cmd_icon_image = shell_cmd_icon.pixmap(DEFAULT_ICON_SIZE).toImage()

assert test_icon_image == shell_cmd_icon_image


def test_deprecated_command_property_with_no_commands(qtbot):
pydm_shell_command = PyDMShellCommand()
Expand Down
90 changes: 84 additions & 6 deletions pydm/widgets/pushbutton.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import hashlib

from qtpy.QtWidgets import QPushButton, QMessageBox, QInputDialog, QLineEdit
from qtpy.QtGui import QColor
from qtpy.QtWidgets import QPushButton, QMessageBox, QInputDialog, QLineEdit, QStyle
from qtpy.QtCore import Slot, Property
from qtpy import QtDesigner
from .base import PyDMWritableWidget

from ..utilities import IconFont
import logging

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -73,6 +74,83 @@ def __init__(
self._released = False
self.clicked.connect(self.sendValue)

# Standard icons (which come with the qt install, and work cross-platform),
# and icons from the "Font Awesome" icon set (https://fontawesome.com/)
# can not be set with a widget's "icon" property in designer, only in python.
# so we provide our own property to specify standard icons and set them with python in the prop's setter.
self._pydm_icon_name = ""
# The color of "Font Awesome" icons can be set,
# but standard icons are already colored and can not be set.
self._pydm_icon_color = QColor(90, 90, 90)

@Property(str)
def PyDMIcon(self) -> str:
"""
Name of icon to be set from Qt provided standard icons or from the fontawesome icon-set.
See "enum QStyle::StandardPixmap" in Qt's QStyle documentation for full list of usable standard icons.
See https://fontawesome.com/icons?d=gallery for list of usable fontawesome icons.
Returns
-------
str
"""
return self._pydm_icon_name

@PyDMIcon.setter
def PyDMIcon(self, value: str) -> None:
"""
Name of icon to be set from Qt provided standard icons or from the "Font Awesome" icon-set.
See "enum QStyle::StandardPixmap" in Qt's QStyle documentation for full list of usable standard icons.
See https://fontawesome.com/icons?d=gallery for list of usable "Font Awesome" icons.
Parameters
----------
value : str
"""
if self._pydm_icon_name == value:
return

# We don't know if user is trying to use a standard icon or an icon from "Font Awesome",
# so 1st try to create a Font Awesome one, which hits exception if icon name is not valid.
try:
icon_f = IconFont()
i = icon_f.icon(value, color=self._pydm_icon_color)
self.setIcon(i)
except Exception:
icon = getattr(QStyle, value, None)
if icon:
self.setIcon(self.style().standardIcon(icon))

self._pydm_icon_name = value

@Property(QColor)
def PyDMIconColor(self) -> QColor:
"""
The color of the icon (color is only applied if using icon from the "Font Awesome" set)
Returns
-------
QColor
"""
return self._pydm_icon_color

@PyDMIconColor.setter
def PyDMIconColor(self, state_color: QColor) -> None:
"""
The color of the icon (color is only applied if using icon from the "Font Awesome" set)
Parameters
----------
new_color : QColor
"""
if state_color != self._pydm_icon_color:
self._pydm_icon_color = state_color
# apply the new color
try:
icon_f = IconFont()
i = icon_f.icon(self._pydm_icon_name, color=self._pydm_icon_color)
self.setIcon(i)
except Exception:
return

@Property(bool)
def passwordProtected(self):
"""
Expand Down Expand Up @@ -152,7 +230,7 @@ def protectedPassword(self, value):
@Property(bool)
def showConfirmDialog(self):
"""
Wether or not to display a confirmation dialog.
Whether or not to display a confirmation dialog.
Returns
-------
Expand All @@ -163,7 +241,7 @@ def showConfirmDialog(self):
@showConfirmDialog.setter
def showConfirmDialog(self, value):
"""
Wether or not to display a confirmation dialog.
Whether or not to display a confirmation dialog.
Parameters
----------
Expand Down Expand Up @@ -488,7 +566,7 @@ def updatePressValue(self, value):
@Property(bool)
def writeWhenRelease(self):
"""
Wether or not to write releaseValue on release
Whether or not to write releaseValue on release
Returns
-------
Expand All @@ -499,7 +577,7 @@ def writeWhenRelease(self):
@writeWhenRelease.setter
def writeWhenRelease(self, value):
"""
Wether or not to write releaseValue on release
Whether or not to write releaseValue on release
Parameters
----------
Expand Down
81 changes: 79 additions & 2 deletions pydm/widgets/related_display_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import warnings
from functools import partial
import hashlib
from qtpy.QtWidgets import QPushButton, QMenu, QAction, QMessageBox, QInputDialog, QLineEdit, QWidget
from qtpy.QtGui import QCursor, QIcon, QMouseEvent
from qtpy.QtWidgets import QPushButton, QMenu, QAction, QMessageBox, QInputDialog, QLineEdit, QWidget, QStyle
from qtpy.QtGui import QCursor, QIcon, QMouseEvent, QColor
from qtpy.QtCore import Slot, Property, Qt, QSize, QPoint
from qtpy import QtDesigner
from .base import PyDMWidget, only_if_channel_set
Expand Down Expand Up @@ -75,6 +75,15 @@ def __init__(

self._follow_symlinks = False

# Standard icons (which come with the qt install, and work cross-platform),
# and icons from the "Font Awesome" icon set (https://fontawesome.com/)
# can not be set with a widget's "icon" property in designer, only in python.
# so we provide our own property to specify standard icons and set them with python in the prop's setter.
self._pydm_icon_name = ""
# The color of "Font Awesome" icons can be set,
# but standard icons are already colored and can not be set.
self._pydm_icon_color = QColor(90, 90, 90)

@only_if_channel_set
def check_enable_state(self) -> None:
"""
Expand All @@ -92,6 +101,74 @@ def check_enable_state(self) -> None:

self.setToolTip(tooltip)

@Property(str)
def PyDMIcon(self) -> str:
"""
Name of icon to be set from Qt provided standard icons or from the fontawesome icon-set.
See "enum QStyle::StandardPixmap" in Qt's QStyle documentation for full list of usable standard icons.
See https://fontawesome.com/icons?d=gallery for list of usable fontawesome icons.
Returns
-------
str
"""
return self._pydm_icon_name

@PyDMIcon.setter
def PyDMIcon(self, value: str) -> None:
"""
Name of icon to be set from Qt provided standard icons or from the "Font Awesome" icon-set.
See "enum QStyle::StandardPixmap" in Qt's QStyle documentation for full list of usable standard icons.
See https://fontawesome.com/icons?d=gallery for list of usable "Font Awesome" icons.
Parameters
----------
value : str
"""
if self._pydm_icon_name == value:
return

# We don't know if user is trying to use a standard icon or an icon from "Font Awesome",
# so 1st try to create a Font Awesome one, which hits exception if icon name is not valid.
try:
icon_f = IconFont()
i = icon_f.icon(value, color=self._pydm_icon_color)
self.setIcon(i)
except Exception:
icon = getattr(QStyle, value, None)
if icon:
self.setIcon(self.style().standardIcon(icon))

self._pydm_icon_name = value

@Property(QColor)
def PyDMIconColor(self) -> QColor:
"""
The color of the icon (color is only applied if using icon from the "Font Awesome" set)
Returns
-------
QColor
"""
return self._pydm_icon_color

@PyDMIconColor.setter
def PyDMIconColor(self, state_color: QColor) -> None:
"""
The color of the icon (color is only applied if using icon from the "Font Awesome" set)
Parameters
----------
new_color : QColor
"""
if state_color != self._pydm_icon_color:
self._pydm_icon_color = state_color
# apply the new color
try:
icon_f = IconFont()
i = icon_f.icon(self._pydm_icon_name, color=self._pydm_icon_color)
self.setIcon(i)
except Exception:
return

@Property("QStringList")
def filenames(self) -> List[str]:
return self._filenames
Expand Down
Loading

0 comments on commit e1dcbe0

Please sign in to comment.