pyqt5儿童使用qml文件访问

时间:2018-11-18 19:32:54

标签: python qml pyqt5

我想运行qt QSyntaxHighlight示例,但是使用QML而不是python代码来构建窗口。虽然我仍然设法做到这一点,但是代码仍然很笨拙,我相信我对QML或API的理解是错误的。

我使用名为main.qml的TextEdits创建了简单的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: 

问题:

  • 我定义了内部带有两个TextEdits的Window,但是我看到我的TextEdits嵌入了其他QObject中(这就是为什么我必须添加child_selector(...)并且不能直接使用findChild输出的原因)是这样吗,还能做得更好吗?

  • 是否有一些与findChild(...)相同的功能,但使用id而不是objectName?

  • engine上使用rootObject()而不使用None的findChildren(...)我无法理解为什么?

1 个答案:

答案 0 :(得分:1)

-我定义了Window,其中有两个TextEdit,而我看到我的TextEdit被嵌入在其他QObject中(这就是为什么我必须添加child_selector(...)并且不能直接使用findChild输出的原因) )为什么会这样,并且可以做得更好

是的,您可以使用findChild,在下面的部分中,我向您展示解决方案:

findChild具有选择器:对于textDocument,它是类型和objectName,因此作为第一个测试可以按类型过滤,我将使用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>]

通过应用对象名称的过滤器,我们可以过滤第一个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是一个取决于范围的标识符,例如:

MyItem.qml

Item{
    id: root
    // others code
}

main.qml

Window{
    MyItem{
        id: it1
    }
    MyItem{
        id: it2
    }
}

如您所见,根ID只会识别MyItem.qml中的Item,在它的外面没有任何意义,从概念上讲,它类似于c ++中的this或python中的self,因为那些属性对于范围具有意义。


-在没有rootObject()的引擎上使用findChildren(...),结果为None我无法理解为什么?

QmlEngine是一个允许创建组件的类,它没有层次关系,因此,QML中层次树的元素没有引擎的父元素,因此它返回None。


我将回答评论中提出的问题,因为答案应有。

QTextDocument继承自QObject,因此它具有objectName,因此QTextDocument是TextEdit的属性,可以通过某种方式在QML中设置objectName吗?

不幸的是,不可能将QML中的objectName放置到QTextDocument,因为在QML中QTextDocument是不可访问的。如果我们查看文档:

textDocumentTextEdit的属性,它是QQuickTextDocument,并且QQuickTextDocument的方法调用textDocument()仅返回{{3} },但该方法既不是Q_INVOKABLE也不是SLOT

更接近您想要的是将objectName设置为QTextDocument,然后使用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 ++。在这种情况下,我更喜欢将类导出为新的Item,并创建一个插槽以传递QQuickTextDocument的属性:

main.py

#!/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 ++不会知道它,因为没有人通知它。