aboutsummaryrefslogtreecommitdiffstats
path: root/examples/widgets
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2022-09-16 13:19:47 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2022-09-17 11:49:34 +0200
commited51341bec119d4c7c5d019e0fd8a6d184f7b877 (patch)
treef5a6534b41da7e5e566cf12b9db5f252da1d9144 /examples/widgets
parent37dcc13a8389f2f9a76376538b48007a39cf7147 (diff)
Add the character map example
Task-number: PYSIDE-841 Change-Id: I1aa30657b904d3814c21f16c2404e057e754a960 Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'examples/widgets')
-rw-r--r--examples/widgets/widgets/charactermap/charactermap.pyproject4
-rw-r--r--examples/widgets/widgets/charactermap/characterwidget.py133
-rw-r--r--examples/widgets/widgets/charactermap/doc/charactermap.rst8
-rw-r--r--examples/widgets/widgets/charactermap/fontinfodialog.py47
-rw-r--r--examples/widgets/widgets/charactermap/main.py17
-rw-r--r--examples/widgets/widgets/charactermap/mainwindow.py167
6 files changed, 376 insertions, 0 deletions
diff --git a/examples/widgets/widgets/charactermap/charactermap.pyproject b/examples/widgets/widgets/charactermap/charactermap.pyproject
new file mode 100644
index 000000000..c2b2c2068
--- /dev/null
+++ b/examples/widgets/widgets/charactermap/charactermap.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "characterwidget.py", "fontinfodialog.py",
+ "mainwindow.py"]
+}
diff --git a/examples/widgets/widgets/charactermap/characterwidget.py b/examples/widgets/widgets/charactermap/characterwidget.py
new file mode 100644
index 000000000..1df2a3b74
--- /dev/null
+++ b/examples/widgets/widgets/charactermap/characterwidget.py
@@ -0,0 +1,133 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from textwrap import dedent
+
+from PySide6.QtCore import QSize, Qt, Slot, Signal
+from PySide6.QtGui import (QBrush, QFont, QFontDatabase, QFontMetrics,
+ QPainter, QPen)
+from PySide6.QtWidgets import QToolTip, QWidget
+
+COLUMNS = 16
+
+
+class CharacterWidget(QWidget):
+
+ character_selected = Signal(str)
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self._display_font = QFont()
+ self._last_key = -1
+ self._square_size = int(0)
+
+ self.calculate_square_size()
+ self.setMouseTracking(True)
+
+ @Slot(QFont)
+ def update_font(self, font):
+ self._display_font.setFamily(font.family())
+ self.calculate_square_size()
+ self.adjustSize()
+ self.update()
+
+ @Slot(str)
+ def update_size(self, fontSize):
+ self._display_font.setPointSize(int(fontSize))
+ self.calculate_square_size()
+ self.adjustSize()
+ self.update()
+
+ @Slot(str)
+ def update_style(self, fontStyle):
+ old_strategy = self._display_font.styleStrategy()
+ self._display_font = QFontDatabase.font(self._display_font.family(),
+ fontStyle,
+ self._display_font.pointSize())
+ self._display_font.setStyleStrategy(old_strategy)
+ self.calculate_square_size()
+ self.adjustSize()
+ self.update()
+
+ @Slot(bool)
+ def update_font_merging(self, enable):
+ if enable:
+ self._display_font.setStyleStrategy(QFont.PreferDefault)
+ else:
+ self._display_font.setStyleStrategy(QFont.NoFontMerging)
+ self.adjustSize()
+ self.update()
+
+ def calculate_square_size(self):
+ h = QFontMetrics(self._display_font, self).height()
+ self._square_size = max(16, 4 + h)
+
+ def sizeHint(self):
+ return QSize(COLUMNS * self._square_size,
+ (65536 / COLUMNS) * self._square_size)
+
+ def _unicode_from_pos(self, point):
+ row = int(point.y() / self._square_size)
+ return row * COLUMNS + int(point.x() / self._square_size)
+
+ def mouseMoveEvent(self, event):
+ widget_position = self.mapFromGlobal(event.globalPosition().toPoint())
+ key = self._unicode_from_pos(widget_position)
+ c = chr(key)
+ family = self._display_font.family()
+ text = dedent(f'''
+ <p>Character: <span style="font-size: 24pt; font-family: {family}">
+ {c}</span><p>Value: 0x{key:x}
+ ''')
+ QToolTip.showText(event.globalPosition().toPoint(), text, self)
+
+ def mousePressEvent(self, event):
+ if event.button() == Qt.LeftButton:
+ self._last_key = self._unicode_from_pos(event.position().toPoint())
+ if self._last_key != -1:
+ c = chr(self._last_key)
+ self.character_selected.emit(f"{c}")
+ self.update()
+ else:
+ super().mousePressEvent(event)
+
+ def paintEvent(self, event):
+ with QPainter(self) as painter:
+ self.render(event, painter)
+
+ def render(self, event, painter):
+ painter = QPainter(self)
+ painter.fillRect(event.rect(), QBrush(Qt.white))
+ painter.setFont(self._display_font)
+ redraw_rect = event.rect()
+ begin_row = int(redraw_rect.top() / self._square_size)
+ end_row = int(redraw_rect.bottom() / self._square_size)
+ begin_column = int(redraw_rect.left() / self._square_size)
+ end_column = int(redraw_rect.right() / self._square_size)
+ painter.setPen(QPen(Qt.gray))
+ for row in range(begin_row, end_row + 1):
+ for column in range(begin_column, end_column + 1):
+ x = int(column * self._square_size)
+ y = int(row * self._square_size)
+ painter.drawRect(x, y, self._square_size, self._square_size)
+
+ font_metrics = QFontMetrics(self._display_font)
+ painter.setPen(QPen(Qt.black))
+ for row in range(begin_row, end_row + 1):
+ for column in range(begin_column, end_column + 1):
+ key = int(row * COLUMNS + column)
+ painter.setClipRect(column * self._square_size,
+ row * self._square_size,
+ self._square_size, self._square_size)
+
+ if key == self._last_key:
+ painter.fillRect(column * self._square_size + 1,
+ row * self._square_size + 1,
+ self._square_size, self._square_size, QBrush(Qt.red))
+
+ text = chr(key)
+ painter.drawText(column * self._square_size + (self._square_size / 2) -
+ font_metrics.horizontalAdvance(text) / 2,
+ row * self._square_size + 4 + font_metrics.ascent(),
+ text)
diff --git a/examples/widgets/widgets/charactermap/doc/charactermap.rst b/examples/widgets/widgets/charactermap/doc/charactermap.rst
new file mode 100644
index 000000000..1a38615c4
--- /dev/null
+++ b/examples/widgets/widgets/charactermap/doc/charactermap.rst
@@ -0,0 +1,8 @@
+Character Map Example
+=====================
+
+The example displays an array of characters which the user can click on
+to enter text in a line edit. The contents of the line edit can then be
+copied into the clipboard, and pasted into other applications. The
+purpose behind this sort of tool is to allow users to enter characters
+that may be unavailable or difficult to locate on their keyboards.
diff --git a/examples/widgets/widgets/charactermap/fontinfodialog.py b/examples/widgets/widgets/charactermap/fontinfodialog.py
new file mode 100644
index 000000000..aa874884f
--- /dev/null
+++ b/examples/widgets/widgets/charactermap/fontinfodialog.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import Qt, qVersion, qFuzzyCompare
+from PySide6.QtGui import QGuiApplication, QFontDatabase
+from PySide6.QtWidgets import (QDialog, QDialogButtonBox,
+ QPlainTextEdit, QVBoxLayout)
+
+
+def _format_font(font):
+ family = font.family()
+ size = font.pointSizeF()
+ return f"{family}, {size}pt"
+
+
+class FontInfoDialog(QDialog):
+
+ def __init__(self, parent):
+ super().__init__(parent)
+ self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
+ main_layout = QVBoxLayout(self)
+ text_edit = QPlainTextEdit(self.text(), self)
+ text_edit.setReadOnly(True)
+ text_edit.setFont(QFontDatabase.systemFont(QFontDatabase.FixedFont))
+ main_layout.addWidget(text_edit)
+ button_box = QDialogButtonBox(QDialogButtonBox.Close, self)
+ button_box.rejected.connect(self.reject)
+ main_layout.addWidget(button_box)
+
+ def text(self):
+ default_font = QFontDatabase.systemFont(QFontDatabase.GeneralFont)
+ fixed_font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
+ title_font = QFontDatabase.systemFont(QFontDatabase.TitleFont)
+ smallest_readable_font = QFontDatabase.systemFont(QFontDatabase.SmallestReadableFont)
+
+ v = qVersion()
+ platform = QGuiApplication.platformName()
+ dpi = self.logicalDpiX()
+ dpr = self.devicePixelRatio()
+ text = f"Qt {v} on {platform}, {dpi}DPI"
+ if not qFuzzyCompare(dpr, float(1)):
+ text += f", device pixel ratio: {dpr}"
+ text += ("\n\nDefault font : " + _format_font(default_font)
+ + "\nFixed font : " + _format_font(fixed_font)
+ + "\nTitle font : " + _format_font(title_font)
+ + "\nSmallest font: " + _format_font(smallest_readable_font))
+ return text
diff --git a/examples/widgets/widgets/charactermap/main.py b/examples/widgets/widgets/charactermap/main.py
new file mode 100644
index 000000000..e84a1d8af
--- /dev/null
+++ b/examples/widgets/widgets/charactermap/main.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtWidgets import QApplication
+
+from mainwindow import MainWindow
+
+"""PySide6 port of the widgets/widgets/ charactermap example from Qt6"""
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ window = MainWindow()
+ window.show()
+ sys.exit(app.exec())
diff --git a/examples/widgets/widgets/charactermap/mainwindow.py b/examples/widgets/widgets/charactermap/mainwindow.py
new file mode 100644
index 000000000..5f0e2bce4
--- /dev/null
+++ b/examples/widgets/widgets/charactermap/mainwindow.py
@@ -0,0 +1,167 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import Qt, QSignalBlocker, Slot
+from PySide6.QtGui import QGuiApplication, QClipboard, QFont, QFontDatabase
+from PySide6.QtWidgets import (QCheckBox, QComboBox, QFontComboBox,
+ QHBoxLayout, QLabel, QLineEdit, QMainWindow,
+ QPushButton, QScrollArea,
+ QVBoxLayout, QWidget)
+
+from characterwidget import CharacterWidget
+from fontinfodialog import FontInfoDialog
+
+
+class MainWindow(QMainWindow):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self._character_widget = CharacterWidget()
+ self._filter_combo = QComboBox()
+ self._style_combo = QComboBox()
+ self._size_combo = QComboBox()
+ self._font_combo = QFontComboBox()
+ self._line_edit = QLineEdit()
+ self._scroll_area = QScrollArea()
+ self._font_merging = QCheckBox()
+
+ file_menu = self.menuBar().addMenu("File")
+ file_menu.addAction("Quit", self.close)
+ help_menu = self.menuBar().addMenu("Help")
+ help_menu.addAction("Show Font Info", self.show_info)
+ help_menu.addAction("About &Qt", qApp.aboutQt)
+
+ central_widget = QWidget()
+
+ self._filter_label = QLabel("Filter:")
+ self._filter_combo = QComboBox()
+ self._filter_combo.addItem("All", int(QFontComboBox.AllFonts.value))
+ self._filter_combo.addItem("Scalable", int(QFontComboBox.ScalableFonts.value))
+ self._filter_combo.addItem("Monospaced", int(QFontComboBox.MonospacedFonts.value))
+ self._filter_combo.addItem("Proportional", int(QFontComboBox.ProportionalFonts.value))
+ self._filter_combo.setCurrentIndex(0)
+ self._filter_combo.currentIndexChanged.connect(self.filter_changed)
+
+ self._font_label = QLabel("Font:")
+ self._font_combo = QFontComboBox()
+ self._size_label = QLabel("Size:")
+ self._size_combo = QComboBox()
+ self._style_label = QLabel("Style:")
+ self._style_combo = QComboBox()
+ self._font_merging_label = QLabel("Automatic Font Merging:")
+ self._font_merging = QCheckBox()
+ self._font_merging.setChecked(True)
+
+ self._scroll_area = QScrollArea()
+ self._character_widget = CharacterWidget()
+ self._scroll_area.setWidget(self._character_widget)
+ self.find_styles(self._font_combo.currentFont())
+ self.find_sizes(self._font_combo.currentFont())
+
+ self._line_edit = QLineEdit()
+ self._line_edit.setClearButtonEnabled(True)
+ self._clipboard_button = QPushButton("To clipboard")
+ self._font_combo.currentFontChanged.connect(self.find_styles)
+ self._font_combo.currentFontChanged.connect(self.find_sizes)
+ self._font_combo.currentFontChanged.connect(self._character_widget.update_font)
+ self._size_combo.currentTextChanged.connect(self._character_widget.update_size)
+ self._style_combo.currentTextChanged.connect(self._character_widget.update_style)
+ self._character_widget.character_selected.connect(self.insert_character)
+
+ self._clipboard_button.clicked.connect(self.update_clipboard)
+ self._font_merging.toggled.connect(self._character_widget.update_font_merging)
+
+ controls_layout = QHBoxLayout()
+ controls_layout.addWidget(self._filter_label)
+ controls_layout.addWidget(self._filter_combo, 1)
+ controls_layout.addWidget(self._font_label)
+ controls_layout.addWidget(self._font_combo, 1)
+ controls_layout.addWidget(self._size_label)
+ controls_layout.addWidget(self._size_combo, 1)
+ controls_layout.addWidget(self._style_label)
+ controls_layout.addWidget(self._style_combo, 1)
+ controls_layout.addWidget(self._font_merging_label)
+ controls_layout.addWidget(self._font_merging, 1)
+ controls_layout.addStretch(1)
+
+ line_layout = QHBoxLayout()
+ line_layout.addWidget(self._line_edit, 1)
+ line_layout.addSpacing(12)
+ line_layout.addWidget(self._clipboard_button)
+
+ central_layout = QVBoxLayout(central_widget)
+ central_layout.addLayout(controls_layout)
+ central_layout.addWidget(self._scroll_area, 1)
+ central_layout.addSpacing(4)
+ central_layout.addLayout(line_layout)
+
+ self.setCentralWidget(central_widget)
+ self.setWindowTitle("Character Map")
+
+ @Slot(QFont)
+ def find_styles(self, font):
+ current_item = self._style_combo.currentText()
+ self._style_combo.clear()
+ styles = QFontDatabase.styles(font.family())
+ for style in styles:
+ self._style_combo.addItem(style)
+
+ style_index = self._style_combo.findText(current_item)
+
+ if style_index == -1:
+ self._style_combo.setCurrentIndex(0)
+ else:
+ self._style_combo.setCurrentIndex(style_index)
+
+ @Slot(int)
+ def filter_changed(self, f):
+ filter = QFontComboBox.FontFilter(self._filter_combo.itemData(f))
+ self._font_combo.setFontFilters(filter)
+ count = self._font_combo.count()
+ self.statusBar().showMessage(f"{count} font(s) found")
+
+ @Slot(QFont)
+ def find_sizes(self, font):
+ current_size = self._size_combo.currentText()
+ with QSignalBlocker(self._size_combo):
+ # sizeCombo signals are now blocked until end of scope
+ self._size_combo.clear()
+
+ style = QFontDatabase.styleString(font)
+ if QFontDatabase.isSmoothlyScalable(font.family(), style):
+ sizes = QFontDatabase.standardSizes()
+ for size in sizes:
+ self._size_combo.addItem(f"{size}")
+ self._size_combo.setEditable(True)
+ else:
+ sizes = QFontDatabase.smoothSizes(font.family(), style)
+ for size in sizes:
+ self._size_combo.addItem(f"{size}")
+ self._size_combo.setEditable(False)
+
+ size_index = self._size_combo.findText(current_size)
+
+ if size_index == -1:
+ self._size_combo.setCurrentIndex(max(0, self._size_combo.count() / 3))
+ else:
+ self._size_combo.setCurrentIndex(size_index)
+
+ @Slot(str)
+ def insert_character(self, character):
+ self._line_edit.insert(character)
+
+ @Slot()
+ def update_clipboard(self):
+ clipboard = QGuiApplication.clipboard()
+ clipboard.setText(self._line_edit.text(), QClipboard.Clipboard)
+ clipboard.setText(self._line_edit.text(), QClipboard.Selection)
+
+ @Slot()
+ def show_info(self):
+ screen_geometry = self.screen().geometry()
+ dialog = FontInfoDialog(self)
+ dialog.setWindowTitle("Fonts")
+ dialog.setAttribute(Qt.WA_DeleteOnClose)
+ dialog.resize(screen_geometry.width() / 4, screen_geometry.height() / 4)
+ dialog.show()