Skip to content

Commit

Permalink
Font configuration (timothycrosley#1)
Browse files Browse the repository at this point in the history
✨ feat: Add support for Font and Font size change


🐛 fix(config.py): fix DEFAULT_FONT path
🐛 fix(display/text_filter.py): remove unused import and fix font path
✨ feat(gui.py): add font selection combo box and support for setting button font

* 🎨 style(main.ui): add text input field and button for text vertical alignment
🎨 style(main.ui): add font size selection field
🎨 style(main.ui): adjust row and column numbers for some items
🎨 style(main.ui): adjust layout for text font selection and font size fields
🐛 fix(resources_rc.py): update resource file
🐛 fix(ui_main.py): update UI code to match changes in main.ui file

* 🔧 chore(gui.py): remove unnecessary comments and whitespace

* 🎨 style(api.py): reformat code to comply with PEP8 style guide
🐛 fix(api.py): fix font size not being set for text filters
✨ feat(api.py): add support for setting font size for buttons in state file

🐛 fix(gui.py): fix line length by splitting long lines
✨ feat(gui.py): add support for setting button text font size

* 🐛 fix(api.py): reorder imports to match the order in config.py
✨ feat(api.py): add support for button text vertical alignment
🐛 fix(gui.py): fix typo in dimmer_options dictionary
✨ feat(gui.py): add support for changing button text font size
  • Loading branch information
rkmax committed Jun 1, 2023
1 parent 9b9c269 commit 47d37e0
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 75 deletions.
32 changes: 30 additions & 2 deletions streamdeck_ui/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from StreamDeck.Devices import StreamDeck
from StreamDeck.Transport.Transport import TransportError

from streamdeck_ui.config import CONFIG_FILE_VERSION, DEFAULT_FONT, STATE_FILE
from streamdeck_ui.config import CONFIG_FILE_VERSION, DEFAULT_FONT, DEFAULT_FONT_SIZE, FONTS_PATH, STATE_FILE
from streamdeck_ui.dimmer import Dimmer
from streamdeck_ui.display.display_grid import DisplayGrid
from streamdeck_ui.display.filter import Filter
Expand Down Expand Up @@ -389,10 +389,34 @@ def set_button_keys(self, deck_id: str, page: int, button: int, keys: str) -> No
self._button_state(deck_id, page, button)["keys"] = keys
self._save_state()

def set_button_font(self, deck_id: str, page: int, button: int, font: str) -> None:
if self.get_button_font(deck_id, page, button) != font:
self._button_state(deck_id, page, button)["font"] = font
self._save_state()
self.update_button_filters(deck_id, page, button)
display_handler = self.display_handlers[deck_id]
display_handler.synchronize()

def get_button_font_size(self, deck_id: str, page: int, button: int) -> str:
"""Returns the font size set for the specified button"""
return self._button_state(deck_id, page, button).get("font_size", DEFAULT_FONT_SIZE)

def set_button_font_size(self, deck_id: str, page: int, button: int, font_size: int) -> None:
if self.get_button_font_size(deck_id, page, button) != font_size:
self._button_state(deck_id, page, button)["font_size"] = font_size
self._save_state()
self.update_button_filters(deck_id, page, button)
display_handler = self.display_handlers[deck_id]
display_handler.synchronize()

def get_button_keys(self, deck_id: str, page: int, button: int) -> str:
"""Returns the keys set for the specified button"""
return self._button_state(deck_id, page, button).get("keys", "")

def get_button_font(self, deck_id: str, page: int, button: int) -> str:
"""Returns the font set for the specified button"""
return self._button_state(deck_id, page, button).get("font", "")

def set_button_write(self, deck_id: str, page: int, button: int, write: str) -> None:
"""Sets the text meant to be written when button is pressed"""
if self.get_button_write(deck_id, page, button) != write:
Expand Down Expand Up @@ -506,9 +530,13 @@ def update_button_filters(self, serial_number: str, page: int, button: int):

text = button_settings.get("text")
font = button_settings.get("font", DEFAULT_FONT)
font_size = button_settings.get("font_size", DEFAULT_FONT_SIZE)
if font == "":
font = DEFAULT_FONT
font = os.path.join(FONTS_PATH, font)
vertical_align = button_settings.get("text_vertical_align", "")

if text:
filters.append(TextFilter(text, font, vertical_align))
filters.append(TextFilter(text, font, font_size, vertical_align))

display_handler.replace(page, button, filters)
5 changes: 3 additions & 2 deletions streamdeck_ui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

PROJECT_PATH = os.path.dirname(os.path.abspath(__file__))
LOGO = os.path.join(PROJECT_PATH, "logo.png")
FONTS_PATH = os.path.join(PROJECT_PATH, "fonts")
DEFAULT_FONT = os.path.join("roboto", "Roboto-Regular.ttf")
FONTS_PATH = os.path.join(PROJECT_PATH, "fonts", "roboto")
DEFAULT_FONT = "Roboto-Regular.ttf"
DEFAULT_FONT_SIZE = 14
STATE_FILE = os.environ.get("STREAMDECK_UI_CONFIG", os.path.expanduser("~/.streamdeck_ui.json"))
CONFIG_FILE_VERSION = 1 # Update only if backward incompatible changes are made to the config file
8 changes: 3 additions & 5 deletions streamdeck_ui/display/text_filter.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import os
from fractions import Fraction
from typing import Callable, Tuple

from PIL import Image, ImageDraw, ImageFilter, ImageFont

from streamdeck_ui.config import FONTS_PATH
from streamdeck_ui.display.filter import Filter


Expand All @@ -14,11 +12,11 @@ class TextFilter(Filter):

image: Image

def __init__(self, text: str, font: str, vertical_align: str):
def __init__(self, text: str, font: str, font_size: int, vertical_align: str):
super(TextFilter, self).__init__()
self.text = text
self.vertical_align = vertical_align
self.true_font = ImageFont.truetype(os.path.join(FONTS_PATH, font), 14)
self.true_font = ImageFont.truetype(font, font_size)
# fmt: off
kernel = [
0, 1, 2, 1, 0,
Expand All @@ -33,7 +31,7 @@ def __init__(self, text: str, font: str, vertical_align: str):
self.image = None

# Hashcode should be created for anything that makes this frame unique
self.hashcode = hash((self.__class__, text, font, vertical_align))
self.hashcode = hash((self.__class__, text, font, font_size, vertical_align))

def initialize(self, size: Tuple[int, int]):
self.image = Image.new("RGBA", size)
Expand Down
49 changes: 46 additions & 3 deletions streamdeck_ui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
import pkg_resources
from PySide6 import QtWidgets
from PySide6.QtCore import QMimeData, QSignalBlocker, QSize, Qt, QTimer, QUrl
from PySide6.QtGui import QAction, QDesktopServices, QDrag, QIcon
from PySide6.QtGui import QAction, QDesktopServices, QDrag, QFontDatabase, QIcon
from PySide6.QtWidgets import QApplication, QDialog, QFileDialog, QMainWindow, QMenu, QMessageBox, QSizePolicy, QSystemTrayIcon

from streamdeck_ui.api import StreamDeckServer
from streamdeck_ui.config import LOGO, STATE_FILE
from streamdeck_ui.config import FONTS_PATH, LOGO, STATE_FILE
from streamdeck_ui.semaphore import Semaphore, SemaphoreAcquireError
from streamdeck_ui.ui_main import Ui_MainWindow
from streamdeck_ui.ui_settings import Ui_SettingsDialog
Expand All @@ -36,7 +36,6 @@
print("")
print(f"For troubleshooting purposes, the actual error is: \n{pynput_error}")


api: StreamDeckServer

BUTTON_STYLE = """
Expand Down Expand Up @@ -411,6 +410,8 @@ def button_clicked(ui, clicked_button, buttons) -> None:
ui.command.setText(api.get_button_command(deck_id, _page(ui), button_id))
ui.keys.setCurrentText(api.get_button_keys(deck_id, _page(ui), button_id))
ui.write.setPlainText(api.get_button_write(deck_id, _page(ui), button_id))
ui.text_font.setCurrentText(api.get_button_font(deck_id, _page(ui), button_id))
ui.text_font_size.setValue(api.get_button_font_size(deck_id, _page(ui), button_id))
ui.change_brightness.setValue(api.get_button_change_brightness(deck_id, _page(ui), button_id))
ui.switch_page.setValue(api.get_button_switch_page(deck_id, _page(ui), button_id))
api.reset_dimmer(deck_id)
Expand All @@ -423,6 +424,8 @@ def enable_button_configuration(ui, enabled: bool):
ui.text.setEnabled(enabled)
ui.command.setEnabled(enabled)
ui.keys.setEnabled(enabled)
ui.text_font.setEnabled(enabled)
ui.text_font_size.setEnabled(enabled)
ui.write.setEnabled(enabled)
ui.change_brightness.setEnabled(enabled)
ui.switch_page.setEnabled(enabled)
Expand All @@ -442,6 +445,8 @@ def reset_button_configuration(ui):
ui.text.clear()
ui.command.clear()
ui.keys.clearEditText()
ui.text_font.clearEditText()
ui.text_font_size.setValue(0)
ui.write.clear()
ui.change_brightness.setValue(0)
ui.switch_page.setValue(0)
Expand Down Expand Up @@ -630,6 +635,30 @@ def about_dialog(self):
QtWidgets.QMessageBox.about(self, title, "\n".join(body))


def update_button_text_font(ui, font: str) -> None:
if not selected_button:
return
deck_id = _deck_id(ui)
if deck_id is None:
return
api.set_button_font(deck_id, _page(ui), selected_button.index, font) # type: ignore # Index property added
icon = api.get_button_icon_pixmap(deck_id, _page(ui), selected_button.index) # type: ignore # Index property added
if icon:
selected_button.setIcon(icon)


def update_button_text_font_size(ui, font_size: int) -> None:
if not selected_button:
return
deck_id = _deck_id(ui)
if deck_id is None:
return
api.set_button_font_size(deck_id, _page(ui), selected_button.index, font_size) # type: ignore # Index property added
icon = api.get_button_icon_pixmap(deck_id, _page(ui), selected_button.index) # type: ignore # Index property added
if icon:
selected_button.setIcon(icon)


def queue_update_button_text(ui, text: str) -> None:
"""Instead of directly updating the text (label) associated with
the button, add a small delay. If this is called before the
Expand Down Expand Up @@ -736,6 +765,8 @@ def create_main_window(logo: QIcon, app: QApplication) -> MainWindow:
ui.keys.currentTextChanged.connect(partial(update_button_keys, ui))
ui.write.textChanged.connect(partial(update_button_write, ui))
ui.change_brightness.valueChanged.connect(partial(update_change_brightness, ui))
ui.text_font_size.valueChanged.connect(partial(update_button_text_font_size, ui))
set_button_text_font_list(ui)
ui.switch_page.valueChanged.connect(partial(update_switch_page, ui))
ui.imageButton.clicked.connect(partial(select_image, main_window))
ui.textButton.clicked.connect(partial(align_text_vertical, main_window))
Expand All @@ -752,6 +783,18 @@ def create_main_window(logo: QIcon, app: QApplication) -> MainWindow:
return main_window


def set_button_text_font_list(ui: Ui_MainWindow) -> None:
"""Prepares the font selection combo box with all available fonts"""
ui.text_font.currentTextChanged.connect(partial(update_button_text_font, ui))
ui.text_font.clear()
font_files = [f for f in os.listdir(os.path.join(FONTS_PATH)) if f.endswith(".ttf")]

ui.text_font.addItem("")
for font_file in font_files:
# remove extension from font_file
ui.text_font.addItem(font_file)


def create_tray(logo: QIcon, app: QApplication, main_window: MainWindow) -> QSystemTrayIcon:
"""Creates a system tray with the provided icon and parent. The main
window passed will be activated when clicked.
Expand Down
94 changes: 62 additions & 32 deletions streamdeck_ui/main.ui
Original file line number Diff line number Diff line change
Expand Up @@ -289,24 +289,64 @@
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="text"/>
</item>
<item>
<widget class="QPushButton" name="textButton">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Text vertical alignment</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/icons/vertical-align.png</normaloff>:/icons/icons/vertical-align.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Font:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Command:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QLineEdit" name="command"/>
</item>
<item row="3" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Press Keys:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="5" column="1">
<widget class="QComboBox" name="keys">
<property name="editable">
<bool>true</bool>
Expand Down Expand Up @@ -393,14 +433,14 @@
</item>
</widget>
</item>
<item row="4" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Switch Page:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="6" column="1">
<widget class="QSpinBox" name="switch_page">
<property name="minimum">
<number>0</number>
Expand All @@ -413,58 +453,48 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Brightness +/-:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="7" column="1">
<widget class="QSpinBox" name="change_brightness">
<property name="minimum">
<number>-99</number>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="8" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Write Text:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="8" column="1">
<widget class="QPlainTextEdit" name="write"/>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="text"/>
<widget class="QComboBox" name="text_font"/>
</item>
<item>
<widget class="QPushButton" name="textButton">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Text vertical alignment</string>
<widget class="QSpinBox" name="text_font_size">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
<property name="minimum">
<number>12</number>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/icons/vertical-align.png</normaloff>:/icons/icons/vertical-align.png</iconset>
<property name="maximum">
<number>72</number>
</property>
</widget>
</item>
Expand Down
6 changes: 3 additions & 3 deletions streamdeck_ui/resources_rc.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,11 @@
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00(\x00\x00\x00\x00\x00\x01\x00\x00\x04=\
\x00\x00\x01\x86\x83\xe4\x1a\xfb\
\x00\x00\x01\x88PX\x91\x1a\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01y*\x8dIj\
\x00\x00\x01\x88PX\x91\x1a\
\x00\x00\x00R\x00\x00\x00\x00\x00\x01\x00\x00\x05-\
\x00\x00\x01y*\x8dIj\
\x00\x00\x01\x88PX\x91\x1a\
"

def qInitResources():
Expand Down
Loading

0 comments on commit 47d37e0

Please sign in to comment.