python - pyqt5 孩子使用 qml 文件访问
问题描述
我想运行 qt QSyntaxHighlight 示例,但使用 QML 构建窗口,而不是使用 python 代码。虽然我仍然设法这样做,但代码很尴尬,我相信我的 QML 或 API 理解是错误的。
我创建了名为 TextEdits 的简单 QML 文件main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello")
TextEdit {
id: text_view
objectName: "text_view"
x: 0
y: 0
width: parent.width
height: parent.height * 0.8
color: "black"
text: qsTr("long class")
font.pixelSize: 12
anchors.margins: 5
}
TextEdit {
id: text_input
anchors.top: text_view.bottom
width: parent.width
height: parent.height - text_view.height
color: "#ef1d1d"
text: qsTr("")
font.capitalization: Font.MixedCase
font.pixelSize: 12
}
}
然后添加main.py
:
#!/bin/python3
import sys
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import QObject
from PyQt5.QtGui import QGuiApplication, QSyntaxHighlighter, QTextDocument
from PyQt5.QtWidgets import QTextEdit
from PyQt5.QtQml import QQmlApplicationEngine, qmlRegisterType
class Highlighter(QSyntaxHighlighter):
def __init__(self, parent=None):
super(Highlighter, self).__init__(parent)
keywordFormat = QtGui.QTextCharFormat()
keywordFormat.setForeground(QtCore.Qt.darkBlue)
keywordFormat.setFontWeight(QtGui.QFont.Bold)
keywordPatterns = ["\\bchar\\b", "\\bclass\\b", "\\bconst\\b",
"\\bdouble\\b", "\\benum\\b", "\\bexplicit\\b", "\\bfriend\\b",
"\\binline\\b", "\\bint\\b", "\\blong\\b", "\\bnamespace\\b",
"\\boperator\\b", "\\bprivate\\b", "\\bprotected\\b",
"\\bpublic\\b", "\\bshort\\b", "\\bsignals\\b", "\\bsigned\\b",
"\\bslots\\b", "\\bstatic\\b", "\\bstruct\\b",
"\\btemplate\\b", "\\btypedef\\b", "\\btypename\\b",
"\\bunion\\b", "\\bunsigned\\b", "\\bvirtual\\b", "\\bvoid\\b",
"\\bvolatile\\b"]
self.highlightingRules = [(QtCore.QRegExp(pattern), keywordFormat)
for pattern in keywordPatterns]
classFormat = QtGui.QTextCharFormat()
classFormat.setFontWeight(QtGui.QFont.Bold)
classFormat.setForeground(QtCore.Qt.darkMagenta)
self.highlightingRules.append((QtCore.QRegExp("\\bQ[A-Za-z]+\\b"),
classFormat))
singleLineCommentFormat = QtGui.QTextCharFormat()
singleLineCommentFormat.setForeground(QtCore.Qt.red)
self.highlightingRules.append((QtCore.QRegExp("//[^\n]*"),
singleLineCommentFormat))
self.multiLineCommentFormat = QtGui.QTextCharFormat()
self.multiLineCommentFormat.setForeground(QtCore.Qt.red)
quotationFormat = QtGui.QTextCharFormat()
quotationFormat.setForeground(QtCore.Qt.darkGreen)
self.highlightingRules.append((QtCore.QRegExp("\".*\""), quotationFormat))
functionFormat = QtGui.QTextCharFormat()
functionFormat.setFontItalic(True)
functionFormat.setForeground(QtCore.Qt.blue)
self.highlightingRules.append((QtCore.QRegExp("\\b[A-Za-z0-9_]+(?=\\()"), functionFormat))
self.commentStartExpression = QtCore.QRegExp("/\\*")
self.commentEndExpression = QtCore.QRegExp("\\*/")
def highlightBlock(self, text):
for pattern, format in self.highlightingRules:
expression = QtCore.QRegExp(pattern)
index = expression.indexIn(text)
while index >= 0:
length = expression.matchedLength()
self.setFormat(index, length, format)
index = expression.indexIn(text, index + length)
self.setCurrentBlockState(0)
startIndex = 0
if self.previousBlockState() != 1:
startIndex = self.commentStartExpression.indexIn(text)
while startIndex >= 0:
endIndex = self.commentEndExpression.indexIn(text, startIndex)
if endIndex == -1:
self.setCurrentBlockState(1)
commentLength = len(text) - startIndex
else:
commentLength = endIndex - startIndex + self.commentEndExpression.matchedLength()
self.setFormat(startIndex, commentLength, self.multiLineCommentFormat)
startIndex = self.commentStartExpression.indexIn(text, startIndex + commentLength)
def print_child(el):
print(" " * 2 * print_child.max + "type: {}".format(type(el)))
print(" " * 2 * print_child.max + "name: {}".format(el.objectName()))
print_child.max += 1
try:
for subel in el.children():
print_child(subel)
except TypeError:
pass
print_child.max -= 1
print_child.max = 0
def child_selector(children, ftype):
for ch in children:
if type(ch) is ftype:
return ch
return None
if __name__ in "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load("main.qml")
print_child(engine.rootObjects()[0])
el = Highlighter(
child_selector(engine.rootObjects()[0].findChild(QObject, "text_view").children(), QTextDocument)
)
engine.quit.connect(app.quit)
sys.exit(app.exec_())
Print child 产生以下输出:
type: <class 'PyQt5.QtGui.QWindow'>
name:
type: <class 'PyQt5.QtCore.QObject'>
name:
type: <class 'PyQt5.QtCore.QObject'>
name: text_view
type: <class 'PyQt5.QtGui.QTextDocument'>
name:
type: <class 'PyQt5.QtGui.QAbstractTextDocumentLayout'>
name:
type: <class 'PyQt5.QtCore.QObject'>
name:
type: <class 'PyQt5.QtGui.QTextFrame'>
name:
type: <class 'PyQt5.QtCore.QObject'>
name:
type: <class 'PyQt5.QtCore.QObject'>
name:
type: <class 'PyQt5.QtGui.QTextDocument'>
name:
type: <class 'PyQt5.QtGui.QAbstractTextDocumentLayout'>
name:
type: <class 'PyQt5.QtCore.QObject'>
name:
type: <class 'PyQt5.QtGui.QTextFrame'>
name:
type: <class 'PyQt5.QtCore.QObject'>
name:
问题:
我已经定义了 Window 里面有两个 TextEdits,而我看到我的 TextEdits 嵌入在额外的 QObject 中(这就是为什么我必须添加
child_selector(...)
并且不能直接使用 findChild 输出)为什么会这样并且可以做得更好吗?是否有一些功能与
findChild(...)
使用 id 而不是 objectName 相同?engine
在没有rootObject()
结果的情况下使用 findChildren(...)None
我不明白为什么?
解决方案
- 我已经定义了 Window 里面有两个 TextEdits,而我看到我的 TextEdits 嵌入在额外的 QObject 中(这就是为什么我必须添加 child_selector(...) 并且不能直接使用 findChild 输出)为什么会这样并且可以做得更好
是的,您可以使用 findChild,在下面的部分中,我将向您展示解决方案:
findChild 有选择器:类型和 objectName,在 textDocument 的情况下,所以作为第一个测试它可以按类型过滤,我将使用 findChildren,因为每个 TextEdit 都有一个 textDocument:
root = engine.rootObjects()[0]
docs = root.findChildren(QtGui.QTextDocument)
print(docs)
for doc in docs:
el = Highlighter(doc)
输出:
[<PyQt5.QtGui.QTextDocument object at 0x7f5703eb4af8>, <PyQt5.QtGui.QTextDocument object at 0x7f5703eb4b88>]
通过应用 objectname 的过滤器,我们可以过滤第一个 TextEdit 的 textDocument:
root = engine.rootObjects()[0]
text_view = root.findChild(QtCore.QObject, "text_view")
doc = text_view.findChild(QtGui.QTextDocument)
el = Highlighter(doc)
- 是否有一些与 findChild(...) 相同但使用 id 而不是 objectName 的功能?
id 仅在 QML 中有意义,因此您不能在 python 中使用它,同样 findChild() 和 findChildren() 仅使用对象名和类型。
id 是一个取决于范围的标识符,例如:
我的项目.qml
Item{
id: root
// others code
}
main.qml
Window{
MyItem{
id: it1
}
MyItem{
id: it2
}
}
如您所见,根 id 将仅标识 MyItem.qml 中的项目,在它之外它没有任何意义,从概念上讲它就像this
在 c++ 或self
python 中一样,因为这些属性相对于范围具有意义。
- 在没有 rootObject() 的引擎上使用 findChildren(...) 导致 None 我不明白为什么?
QmlEngine 是一个允许创建组件的类,它没有层次关系,因此 QML 中层次结构树的任何元素都不像引擎的父亲,因此它返回 None。
我将回答评论中提出的问题,因为答案值得。
QTextDocument 继承自 QObject,所以它有 objectName,所以 QTextDocument 是 TextEdit 的一个属性,它 objectName 可以以某种方式在 QML 中设置吗?
不幸的是,不可能将 QML 中的 objectName 放置到 QTextDocument 中,因为在 QML 中 QTextDocument 不可访问。如果我们查看文档:
textDocument是TextEdit的一个属性,它是一个QQuickTextDocument,并且QQuickTextDocument有一个方法调用textDocument(),它只返回一个QTextDocument但该方法不是 aQ_INVOKABLE
也不是 a SLOT
。
更接近您想要的是将 objectName 设置为textDocument,然后使用textDocument()方法获取QTextDocument (由于名称相似,可能会令人困惑),要设置 objectName,我们将使用 Component.OnCompleted 信号:
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello")
TextEdit {
id: text_view
width: parent.width
height: parent.height * 0.8
color: "black"
text: qsTr("long class")
font.pixelSize: 12
anchors.margins: 5
Component.onCompleted: text_view.textDocument.objectName = "textDocument1"
}
TextEdit {
id: text_input
anchors.top: text_view.bottom
width: parent.width
height: parent.height - text_view.height
color: "#ef1d1d"
text: qsTr("")
font.capitalization: Font.MixedCase
font.pixelSize: 12
Component.onCompleted: text_input.textDocument.objectName = "textDocument2"
}
}
*.py
# ...
if __name__ in "__main__":
app = QtGui.QGuiApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
engine.load("main.qml")
if not engine.rootObjects():
sys.exit(-1)
root = engine.rootObjects()[0]
q_text_document1 = root.findChild(QtQuick.QQuickTextDocument, "textDocument1")
h1 = Highlighter(q_text_document1.textDocument())
q_text_document2 = root.findChild(QtQuick.QQuickTextDocument, "textDocument2")
h2 = Highlighter(q_text_document2.textDocument())
engine.quit.connect(app.quit)
sys.exit(app.exec_())
虽然我个人不喜欢通过 objectName 将 QML 中创建的元素公开给 python/C++,因为 QML 处理它的生命周期而不是 python/C++。在这种情况下,我更喜欢将类导出为新项目,并创建一个插槽来传递以下属性QQuickTextDocument
:
主文件
#!/bin/python3
import sys
from PyQt5 import QtCore, QtGui, QtQml, QtQuick
class Highlighter(QtGui.QSyntaxHighlighter):
def __init__(self, parent=None):
super(Highlighter, self).__init__(parent)
keywordFormat = QtGui.QTextCharFormat()
keywordFormat.setForeground(QtCore.Qt.darkBlue)
keywordFormat.setFontWeight(QtGui.QFont.Bold)
keywordPatterns = ["\\bchar\\b", "\\bclass\\b", "\\bconst\\b",
"\\bdouble\\b", "\\benum\\b", "\\bexplicit\\b", "\\bfriend\\b",
"\\binline\\b", "\\bint\\b", "\\blong\\b", "\\bnamespace\\b",
"\\boperator\\b", "\\bprivate\\b", "\\bprotected\\b",
"\\bpublic\\b", "\\bshort\\b", "\\bsignals\\b", "\\bsigned\\b",
"\\bslots\\b", "\\bstatic\\b", "\\bstruct\\b",
"\\btemplate\\b", "\\btypedef\\b", "\\btypename\\b",
"\\bunion\\b", "\\bunsigned\\b", "\\bvirtual\\b", "\\bvoid\\b",
"\\bvolatile\\b"]
self.highlightingRules = [(QtCore.QRegExp(pattern), keywordFormat)
for pattern in keywordPatterns]
classFormat = QtGui.QTextCharFormat()
classFormat.setFontWeight(QtGui.QFont.Bold)
classFormat.setForeground(QtCore.Qt.darkMagenta)
self.highlightingRules.append((QtCore.QRegExp("\\bQ[A-Za-z]+\\b"),
classFormat))
singleLineCommentFormat = QtGui.QTextCharFormat()
singleLineCommentFormat.setForeground(QtCore.Qt.red)
self.highlightingRules.append((QtCore.QRegExp("//[^\n]*"),
singleLineCommentFormat))
self.multiLineCommentFormat = QtGui.QTextCharFormat()
self.multiLineCommentFormat.setForeground(QtCore.Qt.red)
quotationFormat = QtGui.QTextCharFormat()
quotationFormat.setForeground(QtCore.Qt.darkGreen)
self.highlightingRules.append((QtCore.QRegExp("\".*\""), quotationFormat))
functionFormat = QtGui.QTextCharFormat()
functionFormat.setFontItalic(True)
functionFormat.setForeground(QtCore.Qt.blue)
self.highlightingRules.append((QtCore.QRegExp("\\b[A-Za-z0-9_]+(?=\\()"), functionFormat))
self.commentStartExpression = QtCore.QRegExp("/\\*")
self.commentEndExpression = QtCore.QRegExp("\\*/")
def highlightBlock(self, text):
for pattern, format in self.highlightingRules:
expression = QtCore.QRegExp(pattern)
index = expression.indexIn(text)
while index >= 0:
length = expression.matchedLength()
self.setFormat(index, length, format)
index = expression.indexIn(text, index + length)
self.setCurrentBlockState(0)
startIndex = 0
if self.previousBlockState() != 1:
startIndex = self.commentStartExpression.indexIn(text)
while startIndex >= 0:
endIndex = self.commentEndExpression.indexIn(text, startIndex)
if endIndex == -1:
self.setCurrentBlockState(1)
commentLength = len(text) - startIndex
else:
commentLength = endIndex - startIndex + self.commentEndExpression.matchedLength()
self.setFormat(startIndex, commentLength, self.multiLineCommentFormat)
startIndex = self.commentStartExpression.indexIn(text, startIndex + commentLength)
@QtCore.pyqtSlot(QtQuick.QQuickTextDocument)
def setQQuickTextDocument(self, q):
if isinstance(q, QtQuick.QQuickTextDocument):
self.setDocument(q.textDocument())
if __name__ in "__main__":
app = QtGui.QGuiApplication(sys.argv)
QtQml.qmlRegisterType(Highlighter, "Foo", 1, 0, "Highlighter")
engine = QtQml.QQmlApplicationEngine()
engine.load("main.qml")
if not engine.rootObjects():
sys.exit(-1)
root = engine.rootObjects()[0]
engine.quit.connect(app.quit)
sys.exit(app.exec_())
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import Foo 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello")
TextEdit {
id: text_view
width: parent.width
height: parent.height * 0.8
color: "black"
text: qsTr("long class")
font.pixelSize: 12
anchors.margins: 5
}
Highlighter{
id: h1
Component.onCompleted: h1.setQQuickTextDocument(text_view.textDocument)
}
TextEdit {
id: text_input
anchors.top: text_view.bottom
width: parent.width
height: parent.height - text_view.height
color: "#ef1d1d"
text: qsTr("")
font.capitalization: Font.MixedCase
font.pixelSize: 12
}
Highlighter{
id: h2
Component.onCompleted: h2.setQQuickTextDocument(text_input.textDocument)
}
}
使用最后一种方法,对象的生命周期由 QML 处理,使用初始方法 QML 可以消除某些项目,python/C++ 不会知道它,因为没有人通知它。
推荐阅读
- css - 从 SVG 文件中删除不必要的状态行
- apache-kafka - 我用的是卡夫卡吗?
- angular - 寻找 Angular e2e tslint 规则集
- java - 无法为从 logstash JDBC 输入插件创建的索引创建弹性搜索的映射
- excel - 如何将单元格的地址提供给 excel vba 中的 vlookup 函数?
- django - Django-filters:单个查询字符串中的多个 ID
- java - Jsoup选择不同的Div类型并依次处理
- android - 右侧带有图标的材质按钮
- java - 在 JPA CriteriaBuilder 中带有 WHERE ANY 运算符的整数 [] 列
- sql - 使用连接获取关联记录