首页 > 解决方案 > PyQt 启动时加载函数(qml 加载器)

问题描述

您好我有以下问题:

这是我的工作代码

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
}




}

标签: pythonpyqtqmlpyqt5python-3.6

解决方案


随着时间的推移,从 Python 或 C++ 实例化在 QML 中创建的对象既不好也不可维护。

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

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

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

主文件

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_())

main.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,您将会遇到问题


推荐阅读