PyQt5 - QWidgets部件进阶教程之字符映射表
来源:互联网 发布:淘宝布头论斤卖 编辑:程序博客网 时间:2024/06/15 15:20
- 废话
- 定义CharacterWidget类
- 定义主视窗类
- 最终代码
- 最终效果
- 问题说明
废话
本案例显示一个字符数组,用户可以通过点击在行编辑内键入文本。行编辑的内容可以随后拷贝进剪贴板,并粘贴到其他应用。这类工具的目的,是允许用户键入那些难以找到或在键盘定位的字符。
该案例包含以下类:
- CharacterWidget类显示用当前字体和风格下的字符
- MainWindow类提供一个标准的主窗口,包括字体风格信息、字符映射图像、行编辑和一个可以提交文本到剪贴板的按钮
定义CharacterWidget类
- CharacterWidget用来显示在用户指定字体和风格下的字符集,为了灵活性,我们子类化QWidget,并只重写基础渲染和交互特征的函数。
- 该部件不需要包含其他部件,所以它必须提供自有的sizeHint(),来让内容正确显示。
- 我们重写paintEvent()来绘制自定义内容,同样重写mousePressEvent()用来允许用户与部件交互。
- updateFont()和updateStyle()槽,当用户在应用内更改设置时,用来更新部件内字符的字体和风格。
- 该类定义了characterSelected()信号,当用户在部件内选择了一个字符,会通知应用的其他部分。
- 出于习惯,不见提供了一个工具提示,用来显示当前字符的值。我们重写了mouseMoveEvent()事件处理程序,并定义了showToolTip()来实现这个特性。
- columns、displayFont和currentKey用来记录显示的列数、当前字体和部件内当前高亮的字符
from PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *import sysimport unicodedataclass CharacterWidget(QWidget): characterSelected = pyqtSignal(str) def __init__(self): super(CharacterWidget, self).__init__() self.displayFont = QFont() self.squareSize = int(24) self.columns = int(16) self.lastKey = int(-1) self.setMouseTracking(True)
- 初始化当前键值为-1,表明没有字符会在初始状态被选择。开启鼠标跟踪来允许我们跟踪穿过部件的光标移动。
def updateFont(self, font): self.displayFont.setFamily(font.family()) self.squareSize = max(24, QFontMetrics(self.displayFont).xHeight() * 3) self.adjustSize() self.update() def updateSize(self, fontSize): self.displayFont.setPointSize(int(fontSize)) self.squareSize = max(24, QFontMetrics(self.displayFont).xHeight() * 3) self.adjustSize() self.update() def updateStyle(self, fontStyle): fontDatabase = QFontDatabase() oldStrategy = self.displayFont.styleStrategy() self.displayFont = fontDatabase.font(self.displayFont.family(), fontStyle, self.displayFont.pointSize()) self.displayFont.setStyleStrategy(oldStrategy) self.squareSize = max(24, QFontMetrics(self.displayFont).xHeight() * 3) self.adjustSize() self.update() def updateFontMerging(self, enable): if enable: self.displayFont.setStyleStrategy(QFont.PreferDefault) else: self.displayFont.setStyleStrategy(QFont.NoFontMerging) self.adjustSize() self.update()
- 以上四个方法允许设置字体、尺寸、样式和字体合并,最后调用update()进行修改。
def sizeHint(self): return QSize(self.columns*self.squareSize, (65536/self.columns)*self.squareSize)
- 设置固定的sizeHint()
def paintEvent(self, QPaintEvent): painter = QPainter() painter.begin(self) painter.fillRect(QPaintEvent.rect(), QBrush(Qt.white)) painter.setFont(self.displayFont) redrawRect = QPaintEvent.rect() beginRow = int(redrawRect.top()/self.squareSize) endRow = int(redrawRect.bottom()/self.squareSize) beginColumn = int(redrawRect.left()/self.squareSize) endColumn = int(redrawRect.right()/self.squareSize) painter.setPen(QPen(Qt.gray)) for i in range(beginRow, endRow+1): for j in range(beginColumn, endColumn+1): painter.drawRect(j*self.squareSize, i*self.squareSize, self.squareSize, self.squareSize) fontMetrics = QFontMetrics(self.displayFont) painter.setPen(QPen(Qt.black)) for r in range(beginRow, endRow+1): for c in range(beginColumn, endColumn+1): key = int(r * self.columns + c) painter.setClipRect(c*self.squareSize, r*self.squareSize, self.squareSize, self.squareSize) if key == self.lastKey: painter.fillRect(c*self.squareSize + 1, r*self.squareSize + 1, self.squareSize, self.squareSize, QBrush(Qt.red)) painter.drawText(c*self.squareSize + (self.squareSize / 2) - fontMetrics.width(chr(key)) / 2, r*self.squareSize + 4 + fontMetrics.ascent(), chr(key)) painter.end()
- paintEvent()展示如何排列部件内容和显示
- 为部件创建QPainter,并在所有情况下确保部件的背景色是被绘制的。painter的字体设置为用户指定的显示字体。部件的区域需要重绘,这用来决定那些字体需要显示。
- 使用整数除法获取每个字符应显示的行列数,并在部件上为每个字符绘制一个方块。
- 集合中每个字符的标记在每个方块内绘制,并将每个当前被选中的标记显示为红色。
- 我们不需要考虑视口显示区域和所绘制区域的不同,因为所有超出可见区域的都会被剪切掉。
- mousePressEvent()定义如何对鼠标点击进行相应,我们只关心当用户用左键点击部件。当这个发生时,我们计算哪个字符被点击,并发射characterSelected()信号。字符的序列会通过整除所点击的字符网格块尺寸的x、y坐标获得。当部件的列数被定义后,我们用该值乘行索引数并加上所在列序列来获得字符序列。如果点击任何鼠标按钮,事件被传送到QWdiget基础类,这可以确保事件可以被其它相关部件适当的处理。
- mouseMoveEvent()映射鼠标在部件全局坐标的光标位置,决定通过运行计算而被点击字符。
- 在全局坐标中定义的给定位置显示工具提示。
定义主视窗类
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.centralWidget = QWidget() self.fontLabel = QLabel('Font:') self.fontCombo = QFontComboBox() self.sizeLabel = QLabel('Size:') self.sizeCombo = QComboBox() self.styleLabel = QLabel('Style:') self.styleCombo = QComboBox() self.fontMergingLabel = QLabel('Automatic Font Merging:') self.fontMerging = QCheckBox() self.fontMerging.setChecked(True) self.charaterWidget = CharacterWidget() self.scrollArea = QScrollArea() self.scrollArea.setWidget(self.charaterWidget) self.findStyles(self.fontCombo.currentFont()) self.findSizes(self.fontCombo.currentFont()) self.lineEdit = QLineEdit() self.clipboard = QApplication.clipboard() self.clipboardButton = QPushButton('&To Clipboard') self.fontCombo.currentFontChanged.connect(self.findStyles) self.fontCombo.currentFontChanged.connect(self.findSizes) self.fontCombo.currentFontChanged.connect(self.charaterWidget.updateFont) self.sizeCombo.currentIndexChanged.connect(self.charaterWidget.updateSize) self.styleCombo.currentIndexChanged.connect(self.charaterWidget.updateStyle) self.charaterWidget.characterSelected.connect(self.insertCharacter) self.fontMerging.toggled.connect(self.charaterWidget.updateFontMerging) self.clipboardButton.clicked.connect(self.updateClipboard) self.controlsLayout = QHBoxLayout() self.controlsLayout.addWidget(self.fontLabel) self.controlsLayout.addWidget(self.fontCombo, 1) self.controlsLayout.addWidget(self.sizeLabel) self.controlsLayout.addWidget(self.sizeCombo, 1) self.controlsLayout.addWidget(self.styleLabel) self.controlsLayout.addWidget(self.styleCombo, 1) self.controlsLayout.addWidget(self.fontMergingLabel) self.controlsLayout.addWidget(self.fontMerging, 1) self.controlsLayout.addStretch(1) self.lineLayout = QHBoxLayout() self.lineLayout.addWidget(self.lineEdit, 1) self.lineLayout.addSpacing(12) self.lineLayout.addWidget(self.clipboardButton) self.centralLayout = QVBoxLayout() self.centralLayout.addLayout(self.controlsLayout) self.centralLayout.addWidget(self.scrollArea, 1) self.centralLayout.addSpacing(4) self.centralLayout.addLayout(self.lineLayout) self.centralWidget.setLayout(self.centralLayout) self.setCentralWidget(self.centralWidget) self.setWindowTitle('Character Map')
- 主窗口类提供一个简单的用户界面,只有一个构造器、相应标准部件发射信号的槽和一些简单的用于设置用户界面函数
- 主窗口包含的各类部件用于控制字符将会如何显示,并定义findFonts()函数。部件通过决定可用样式来调用findStyles()槽,insertCharacter()将一个用户选择的字符插入到窗口的行编辑,updateClipboard()用行编辑的内容同步粘贴板。
- 构造器内,我们设置视窗的中心部件,用一些标准部件进行填充。同样会构建一个CharacterWidget自定义部件。
- 为了方便我们可以浏览它的内容,再增加一个QScrollArea。QScrollArea提供一个CharacterWidget的滚动视口。
- 字体组合框自动弹出一个可用的字体列表,我们在样式组合框中,为当前字体列出可用样式。
- 行编辑和按钮用于为粘贴板提供文本。
- 我们也获取一个粘贴板对象,这样就能为其他应用发送用户输入的文本。
- 案例中大部分信号的发射是通过标准部件,将这些信号与该类中的槽和其他部件的槽进行连接。
- 字体组合框的currentFontChanged()链接到findStyles()函数,这样列出可用样式就能为每个字体使用。当字体和样式都能被用户改变时,字体组合框的currentFontChanged()信号和样式组合框的currentIndexChanged()则直接链接到字符部件。
- 最后两个连接允许字符部件中的字符可以被选择,并插入文本到粘贴板。
- 当用户点击字符时,字符部件发射characterSelected()自定义信号,这会被该类中的insertCharacter()函数处理。当按钮发射clicked()信号时,粘贴板被改变,并通过updateClipboard()函数处理。
- 构造器中剩下的代码是设置中心部件的布局,和提供视窗标题。
def findStyles(self, font): fontDatabase = QFontDatabase() currentItem = self.styleCombo.currentText() self.styleCombo.clear() for style in fontDatabase.styles(font.family()): self.styleCombo.addItem(style) styleIndex = self.styleCombo.findText(currentItem) if styleIndex == -1: self.styleCombo.setCurrentIndex(0) else: self.styleCombo.setCurrentIndex(styleIndex) def findSizes(self, font): fontDataBase = QFontDatabase() currentSize = self.sizeCombo.currentText() self.sizeCombo.blockSignals(True) self.sizeCombo.clear() if fontDataBase.isSmoothlyScalable(font.family(), fontDataBase.styleString(font)): for size in fontDataBase.standardSizes(): self.sizeCombo.addItem(str(QVariant(size).value())) self.sizeCombo.setEditable(True) else: for size in fontDataBase.smoothSizes(font.family(), fontDataBase.styleString(font)): self.sizeCombo.addItem(str(QVariant(size))) self.sizeCombo.setEditable(False) self.sizeCombo.blockSignals(False) sizeIndex = self.sizeCombo.findText(currentSize) if sizeIndex == -1: self.sizeCombo.setCurrentIndex(max(0, self.sizeCombo.count() / 3)) else: self.sizeCombo.setCurrentIndex(sizeIndex) def insertCharacter(self, character): self.lineEdit.insert(character) def updateClipboard(self): self.clipboard.setText(self.lineEdit.text(), QClipboard.Clipboard) self.clipboard.setText(self.lineEdit.text(), QClipboard.Selection)
- 字体组合框自动弹出字体系列列表,可以通过findStyles()函数使用每种字体的样式,无论用户选择什么字体,这个函数都会调用。
- 我们记录当前选择的样式,并清除央视组合框,这样就能插入与当前字体系列相关的样式。
- 使用字体数据库收集当前字体的样式,并把它们插入到样式组合框。如果原样式对当前字体不可用,则清除当前项。
- 最后两个函数是相应字符部件和主视窗按钮信号的槽,当用户点击一个字符时,insertCharacter()函数用于插入字符部件的字符,字符插入到行编辑的当前光标位置。
- 主视窗的”To clipboard”按钮链接到updateClipboard(),当它被点击时,粘贴板包含的行编辑内容将被更新。我们拷贝行编辑的所有文本到粘贴板,但不清除行编辑。
最终代码
from PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *import sysimport unicodedataclass CharacterWidget(QWidget): characterSelected = pyqtSignal(str) def __init__(self): super(CharacterWidget, self).__init__() self.displayFont = QFont() self.squareSize = int(24) self.columns = int(16) self.lastKey = int(-1) self.setMouseTracking(True) def updateFont(self, font): self.displayFont.setFamily(font.family()) self.squareSize = max(24, QFontMetrics(self.displayFont).xHeight() * 3) self.adjustSize() self.update() def updateSize(self, fontSize): self.displayFont.setPointSize(int(fontSize)) self.squareSize = max(24, QFontMetrics(self.displayFont).xHeight() * 3) self.adjustSize() self.update() def updateStyle(self, fontStyle): fontDatabase = QFontDatabase() oldStrategy = self.displayFont.styleStrategy() self.displayFont = fontDatabase.font(self.displayFont.family(), fontStyle, self.displayFont.pointSize()) self.displayFont.setStyleStrategy(oldStrategy) self.squareSize = max(24, QFontMetrics(self.displayFont).xHeight() * 3) self.adjustSize() self.update() def updateFontMerging(self, enable): if enable: self.displayFont.setStyleStrategy(QFont.PreferDefault) else: self.displayFont.setStyleStrategy(QFont.NoFontMerging) self.adjustSize() self.update() def sizeHint(self): return QSize(self.columns*self.squareSize, (65536/self.columns)*self.squareSize) def paintEvent(self, QPaintEvent): painter = QPainter() painter.begin(self) painter.fillRect(QPaintEvent.rect(), QBrush(Qt.white)) painter.setFont(self.displayFont) redrawRect = QPaintEvent.rect() beginRow = int(redrawRect.top()/self.squareSize) endRow = int(redrawRect.bottom()/self.squareSize) beginColumn = int(redrawRect.left()/self.squareSize) endColumn = int(redrawRect.right()/self.squareSize) painter.setPen(QPen(Qt.gray)) for i in range(beginRow, endRow+1): for j in range(beginColumn, endColumn+1): painter.drawRect(j*self.squareSize, i*self.squareSize, self.squareSize, self.squareSize) fontMetrics = QFontMetrics(self.displayFont) painter.setPen(QPen(Qt.black)) for r in range(beginRow, endRow+1): for c in range(beginColumn, endColumn+1): key = int(r * self.columns + c) painter.setClipRect(c*self.squareSize, r*self.squareSize, self.squareSize, self.squareSize) if key == self.lastKey: painter.fillRect(c*self.squareSize + 1, r*self.squareSize + 1, self.squareSize, self.squareSize, QBrush(Qt.red)) painter.drawText(c*self.squareSize + (self.squareSize / 2) - fontMetrics.width(chr(key)) / 2, r*self.squareSize + 4 + fontMetrics.ascent(), chr(key)) painter.end() def mousePressEvent(self, QMouseEvent): if QMouseEvent.button() == Qt.LeftButton: self.lastKey = int((QMouseEvent.y()/self.squareSize)*self.columns + QMouseEvent.x()/self.squareSize) if unicodedata.category(chr(self.lastKey)) != 'Cn': self.characterSelected.emit(chr(self.lastKey)) self.update() else: self.mousePressEvent(QMouseEvent) def mouseMoveEvent(self, QMouseEvent): widgetPosition = self.mapFromGlobal(QMouseEvent.globalPos()) key = int((widgetPosition.y()/self.squareSize)*self.columns + widgetPosition.x()/self.squareSize) text = ("<p>Character: <span style=\"font-size: 24pt; font-family: %s\">" % self.displayFont.family()) \ + chr(key) \ + "</span><p>Value: 0x" \ + hex(key) QToolTip.showText(QMouseEvent.globalPos(), text)class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.centralWidget = QWidget() self.fontLabel = QLabel('Font:') self.fontCombo = QFontComboBox() self.sizeLabel = QLabel('Size:') self.sizeCombo = QComboBox() self.styleLabel = QLabel('Style:') self.styleCombo = QComboBox() self.fontMergingLabel = QLabel('Automatic Font Merging:') self.fontMerging = QCheckBox() self.fontMerging.setChecked(True) self.charaterWidget = CharacterWidget() self.scrollArea = QScrollArea() self.scrollArea.setWidget(self.charaterWidget) self.findStyles(self.fontCombo.currentFont()) self.findSizes(self.fontCombo.currentFont()) self.lineEdit = QLineEdit() self.clipboard = QApplication.clipboard() self.clipboardButton = QPushButton('&To Clipboard') self.fontCombo.currentFontChanged.connect(self.findStyles) self.fontCombo.currentFontChanged.connect(self.findSizes) self.fontCombo.currentFontChanged.connect(self.charaterWidget.updateFont) self.sizeCombo.currentIndexChanged.connect(self.charaterWidget.updateSize) self.styleCombo.currentIndexChanged.connect(self.charaterWidget.updateStyle) self.charaterWidget.characterSelected.connect(self.insertCharacter) self.fontMerging.toggled.connect(self.charaterWidget.updateFontMerging) self.clipboardButton.clicked.connect(self.updateClipboard) self.controlsLayout = QHBoxLayout() self.controlsLayout.addWidget(self.fontLabel) self.controlsLayout.addWidget(self.fontCombo, 1) self.controlsLayout.addWidget(self.sizeLabel) self.controlsLayout.addWidget(self.sizeCombo, 1) self.controlsLayout.addWidget(self.styleLabel) self.controlsLayout.addWidget(self.styleCombo, 1) self.controlsLayout.addWidget(self.fontMergingLabel) self.controlsLayout.addWidget(self.fontMerging, 1) self.controlsLayout.addStretch(1) self.lineLayout = QHBoxLayout() self.lineLayout.addWidget(self.lineEdit, 1) self.lineLayout.addSpacing(12) self.lineLayout.addWidget(self.clipboardButton) self.centralLayout = QVBoxLayout() self.centralLayout.addLayout(self.controlsLayout) self.centralLayout.addWidget(self.scrollArea, 1) self.centralLayout.addSpacing(4) self.centralLayout.addLayout(self.lineLayout) self.centralWidget.setLayout(self.centralLayout) self.setCentralWidget(self.centralWidget) self.setWindowTitle('Character Map') def findStyles(self, font): fontDatabase = QFontDatabase() currentItem = self.styleCombo.currentText() self.styleCombo.clear() for style in fontDatabase.styles(font.family()): self.styleCombo.addItem(style) styleIndex = self.styleCombo.findText(currentItem) if styleIndex == -1: self.styleCombo.setCurrentIndex(0) else: self.styleCombo.setCurrentIndex(styleIndex) def findSizes(self, font): fontDataBase = QFontDatabase() currentSize = self.sizeCombo.currentText() self.sizeCombo.blockSignals(True) self.sizeCombo.clear() if fontDataBase.isSmoothlyScalable(font.family(), fontDataBase.styleString(font)): for size in fontDataBase.standardSizes(): self.sizeCombo.addItem(str(QVariant(size).value())) self.sizeCombo.setEditable(True) else: for size in fontDataBase.smoothSizes(font.family(), fontDataBase.styleString(font)): self.sizeCombo.addItem(str(QVariant(size))) self.sizeCombo.setEditable(False) self.sizeCombo.blockSignals(False) sizeIndex = self.sizeCombo.findText(currentSize) if sizeIndex == -1: self.sizeCombo.setCurrentIndex(max(0, self.sizeCombo.count() / 3)) else: self.sizeCombo.setCurrentIndex(sizeIndex) def insertCharacter(self, character): self.lineEdit.insert(character) def updateClipboard(self): self.clipboard.setText(self.lineEdit.text(), QClipboard.Clipboard) self.clipboard.setText(self.lineEdit.text(), QClipboard.Selection)app = QApplication(sys.argv)char = MainWindow()char.show()app.exec_()
最终效果
问题说明
该案例存在Bug,经过分析后感觉问题出在mouseMoveEvent()和mousePressEvent()中的算法,目前正在修改,希望可以改进,同样希望广大网友给出建设性意见。
0 0
- PyQt5 - QWidgets部件进阶教程之字符映射表
- PyQt5 - QWidgets部件进阶教程之计算器
- PyQt5 - QWidgets部件进阶教程
- PyQt5 - QWidgets部件进阶教程之日历窗口部件
- PyQt5 - QWidgets部件进阶教程之模拟时钟
- PyQt5 - QWidgets部件进阶教程之数字时钟
- PyQt5 - QWidgets部件进阶教程之塑形时钟
- PyQt5 - QWidgets部件进阶教程之分组框
- PyQt5 - QWidgets部件进阶教程之行编辑
- PyQt5 - QWidgets部件入门教程
- python3+PyQt5 自定义窗口部件--使用窗口部件样式表
- PyQt5:QCalendarWidget日历部件(27)
- hibernate进阶之组件映射
- Vim教程之进阶
- PyQt5初级教程--PyQt5中的部件II[9/13]
- python3+PyQt5 自定义窗口部件--创建复合窗口部件
- python3+PyQt5 自定义窗口部件--子类化内置窗口部件
- python3+PyQt5 实现自定义窗口部件--流体混合窗口部件
- 整理下2015-2016年在搭乘交通工具所读过的一些书
- 了解CSS
- 文件管理者NSFileManager && 写入文件存储 &&字符串操作 && NSInputStream
- iOS使用的一些库 原理分析
- 【android】IntentService
- PyQt5 - QWidgets部件进阶教程之字符映射表
- 看看异常
- ftell、fread、fseek、feof 详细的使用介绍与注意细节
- java 类型转换
- Gstreamer框架中使用gst-launch进行流媒体播放
- 欢迎使用CSDN-markdown编辑器
- Java并发
- 第三题:等式变换 输入一个正整数X,在下面的等式左边的数字之间添加+号或者-号,使得等式成立。 1 2 3 4 5 6 7 8 9 = X
- Http头介绍:Expires,Cache-Control,Last-Modified,ETag