字符映射示例¶
该示例显示了一个字符数组,用户可以点击这些字符以在行编辑器中输入文本。然后,行编辑器的内容可以复制到剪贴板,并粘贴到其他应用程序中。这种工具的目的是允许用户输入可能无法使用或难以在键盘上找到的字符。
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
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())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
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)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
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
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
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) # noqa: F821
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()