PyQt 启动时加载函数(qml 加载器)

Posted

技术标签:

【中文标题】PyQt 启动时加载函数(qml 加载器)【英文标题】:PyQt load function on startup (qml loader) 【发布时间】:2018-05-01 14:51:54 【问题描述】:

您好,我有以下问题:

这是我的工作代码

import sys
from PyQt5.QtCore import QObject, QUrl, Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
import os
import vlc
from time import sleep

# define VLC instance
instance = vlc.Instance()

# Define VLC player
instance = vlc.Instance('--input-repeat=-1', '--fullscreen')

player = instance.media_player_new()


list_test = []
list_name = []


def prepare_url(url):

media = instance.media_new(url)
    player.set_media(media)


if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
app = QApplication(sys.argv)

engine = QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("main", engine)
engine.load('SimpleQML.qml')

win = engine.rootObjects()[0]
win.show()
button = win.findChild(QObject, "playBtn")


def myFunction():
    print("A fine piece of text")



button.clicked.connect(myFunction)  # works on click
myFunction() #works with out clicking

sys.exit(app.exec_())

现在我想通过执行以下代码来扩展它:

import sys
from PyQt5.QtCore import QObject, QUrl, Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
import os
import vlc
from time import sleep

# define VLC instance
instance = vlc.Instance()

# Define VLC player
instance = vlc.Instance('--input-repeat=-1', '--fullscreen')

player = instance.media_player_new()


list_test = []
list_name = []


def prepare_url(url):

media = instance.media_new(url)

player.set_media(media)


if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
app = QApplication(sys.argv)

engine = QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("main", engine)
engine.load('SimpleQML.qml')

win = engine.rootObjects()[0]
win.show()
button = win.findChild(QObject, "playBtn")
comboBox = win.findChild(QObject, "comboBox")

def myFunction():
    print("das")

def list_fill():
    with open("config/stations.txt") as f:
        content = f.readlines()
        content = [x.strip() for x in content]
        list_t = [item.split("|")[0] for item in content if item]
        list_n = [item.split("|")[1] for item in content if item]
        del list_test[:]
        del list_name[:]
        comboBox.clear()
        for x in list_t:
            list_test.append(x)
        for x in list_n:
            list_name.append(x)

        addItems(list_name)

button.clicked.connect(myFunction)  # works too
myFunction()
list_fill()  #calling this crashes program
sys.exit(app.exec_())

最后这是错误

    das
Traceback (most recent call last):
 File "/home/flea/Desktop/quick qt/main.py", line 65, in <module>
  list_fill()
 File "/home/flea/Desktop/quick qt/main.py", line 55, in list_fill
 comboBox.clear()
AttributeError: 'QObject' object has no attribute 'clear'

我尝试使用硬编码列表来做 ti,但列表不是问题,由于某种原因,我的组合框无法被 python 识别。我不确定这里有什么问题。 我可以加载我的按钮并向它添加一个点击事件(这有效),但我无法将列表添加到我的组合框。

这是我的 Qml

 import QtQuick 2.10
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.3



ApplicationWindow 
id: applicationWindow
Material.theme: Material.Light
title: qsTr("Test Invoke")

width: 600
height: 500

Row 
    id: row
    width: 200
    height: 400
    anchors.left: parent.left
    anchors.leftMargin: 5
    anchors.top: parent.top
    anchors.topMargin: 5

    Button
        id: playBtn


        objectName: "playBtn"
        text : "Play"
        checked: false
        padding: 6
        rightPadding: 8
        font.wordSpacing: 0
        font.pointSize: 10
        font.family: "Times New Roman"
        topPadding: 4
        highlighted: true
        Material.accent: Material.Red



    

    Button 
        id: stopBtn
        objectName: "stopBtn"
        text: qsTr("Stop")
        anchors.left: playBtn.right
        anchors.leftMargin: 5
    

    Button 
        id: stfBtn
        text: qsTr("Save")
        objectName: "stfBtn"
        anchors.left: stopBtn.right
        anchors.leftMargin: 5
    

    Button 
        id: minimize
        objectName: "minBtn"
        text: qsTr("Min")
        anchors.left: stfBtn.right
        anchors.leftMargin: 5
    


Column 
    id: column
    x: 135
    y: 100
    width: 200
    height: 400

    TextField 
        objectName: "nameText"
        id: nameText
        width: 300
        text: qsTr("")
    

    TextField 
        objectName: "urlText"
        id: urlText
        width: 300
        text: qsTr("")
    

    ComboBox 
        objectName: "comboBox"
        id: comboBox
        width: 200
    


Slider 
    id: slide
    objectName: "slider"
    x: 160
    y: 311
    value: 0.5






【问题讨论】:

ComboBox 没有明确的功能。 QComboBox 与 ComboBox 不同 什么是 addItems? 【参考方案1】:

从 Python 或 C++ 实例化在 QML 中创建的对象既不好也不可维护。

适当的做法是在 Python 或 C++ 中创建一个对象并将其发送到 QML,然后创建允许与 QML 交互的 qproperties 和槽。

在你的情况下,我猜 list_fill 试图将数据添加到 ComboBox,但 ComboBox 没有明确的方法,所以如果你想清理它,只需传递一个空列表,或者在你的情况下传递它新列表。

另一方面,调用show()并不优雅,最好将ApplicationWindow的visible属性设置为true。

ma​​in.py

import sys
import os

from PyQt5.QtCore import QObject, QUrl, Qt, pyqtSlot, pyqtSignal, pyqtProperty
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine

import vlc

# define VLC instance
instance = vlc.Instance()

# Define VLC player
instance = vlc.Instance('--input-repeat=-1', '--fullscreen')

player = instance.media_player_new()


list_test = []
list_name = []

def prepare_url(url):
    media = instance.media_new(url)
    player.set_media(media)

class Manager(QObject):
    stationsChanged = pyqtSignal()
    currentStationChanged = pyqtSignal()
    def __init__(self):
        QObject.__init__(self)
        self.m_stations = []
        self.m_currentStation = ""
        self.currentStationChanged.connect(self.on_currentStationChanged)

    @pyqtProperty(str, notify=currentStationChanged)
    def currentStation(self):
        return self.m_currentStation

    @currentStation.setter
    def currentStation(self, val):
        if self.m_currentStation == val:
            return
        self.m_currentStation = val
        self.currentStationChanged.emit()

    @pyqtProperty(list, notify=stationsChanged)
    def stations(self):
        return self.m_stations

    @stations.setter
    def stations(self, val):
        if self.m_stations == val:
            return
        self.m_stations = val[:]
        self.stationsChanged.emit()

    @pyqtSlot()
    def play(self):
        print("play", self.currentStation)

    @pyqtSlot()
    def stop(self):
        print("stop")

    @pyqtSlot()
    def on_currentStationChanged(self):
        print(self.currentStation)

    def list_fill(self):
        l = []
        with open("config/stations.txt") as f:
            content = f.readlines()
            content = [x.strip() for x in content]
            list_t = [item.split("|")[0] for item in content if item]
            list_n = [item.split("|")[1] for item in content if item]
            l += list_t + list_n
        self.stations = l



if __name__ == "__main__":
    os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
    app = QApplication(sys.argv)

    engine = QQmlApplicationEngine()
    manager = Manager()
    ctx = engine.rootContext()
    ctx.setContextProperty("Manager", manager)
    engine.load('main.qml')
    if not engine.rootObjects():
        sys.exit(-1)
    manager.list_fill()
    sys.exit(app.exec_())

ma​​in.qml

import QtQuick 2.10
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.3

ApplicationWindow 
    id: applicationWindow
    Material.theme: Material.Light
    title: qsTr("Test Invoke")
    visible: true

    width: 600
    height: 500

    Row 
        id: row
        width: 200
        height: 400
        anchors.left: parent.left
        anchors.leftMargin: 5
        anchors.top: parent.top
        anchors.topMargin: 5

        Button
            id: playBtn
            text : "Play"
            checked: false
            padding: 6
            rightPadding: 8
            font.wordSpacing: 0
            font.pointSize: 10
            font.family: "Times New Roman"
            topPadding: 4
            highlighted: true
            Material.accent: Material.Red
            onClicked: Manager.play()
        

        Button 
            id: stopBtn
            text: qsTr("Stop")
            anchors.left: playBtn.right
            anchors.leftMargin: 5
            onClicked: Manager.stop()
        

        Button 
            id: stfBtn
            text: qsTr("Save")
            objectName: "stfBtn"
            anchors.left: stopBtn.right
            anchors.leftMargin: 5
        

        Button 
            id: minimize
            objectName: "minBtn"
            text: qsTr("Min")
            anchors.left: stfBtn.right
            anchors.leftMargin: 5
        
    

    Column 
        id: column
        x: 135
        y: 100
        width: 200
        height: 400

        TextField 
            id: nameText
            width: 300
            text: qsTr("")
        

        TextField 
            id: urlText
            width: 300
            text: qsTr("")
        

        ComboBox 
            id: comboBox
            width: 200
            model: Manager.stations
            onCurrentTextChanged: Manager.currentStation = currentText
        
    

    Slider 
        id: slider
        x: 160
        y: 311
        value: 0.5
    

这种实现的优点是您可以独立修改设计和逻辑部分。如果您通过setContextProperty 传递一个对象,它将在所有 .qml 文件中可见。如果你有很多 .qml 文件,你会遇到问题

【讨论】:

非常感谢您的回答,但这让我想到了主要问题。如果我在 python 文件中没有引用我的组合框,我如何获得它的值。为了清楚起见:在同一个示例中,我将如何打印出组合框选定的值,而不是硬编码字符串。我不想提出一个新问题,因为我认为这可以在这个问题中得到澄清。一般来说,如何访问 UI 元素所持有的值。 @ImRaphael 好的,我明白了,我将根据该要求更新我的答案。 @ImRaphael 现在 currentStation 具有所选项目。例如,如果您选择一个新项目并按下播放,则所选项目将在 python 中打印。 @ImRaphael 我还添加了一个新插槽,每次您选择新项目时都会收到通知。 太棒了,非常感谢。我现在将尝试使用其他东西,看看它们是如何协同工作的。干杯

以上是关于PyQt 启动时加载函数(qml 加载器)的主要内容,如果未能解决你的问题,请参考以下文章

三:JVM(重点)

什么类加载器的双亲委托模型?

Java内存管理-掌握虚拟机类加载器

使用 PyQt 将基于 qml 的图表集成到现有的 ui

重新加载 PyQt5 应用程序而不先重新启动它

qml加载开机白屏易死机的解决方案