Я только начал играть с PySide2 и QML для предстоящего проекта, и я сразу же наткнулся на проблему: как мне подключить сигнал, излучаемый классом python (который наследуется от QObject), к слоту в файле .qml?Например: у меня есть QThread (класс python), который генерирует пару координат xy каждые 50 миллисекунд.Я хочу добавить сгенерированную пару в LineSeries, определенную в файле QML, для создания осциллографического графика.
Вопрос, вероятно, очень простой и тупой, но мне действительно нужна помощь.
С уважением
Ландо
РЕДАКТИРОВАТЬ 4:
Я нашел решение, но мне оно не очень нравится.Можете ли вы предложить мне (если он существует) более элегантный способ сделать это?
Код для Python:
class Manager(QObject):
dataReady = Signal(float,float)
def __init__(self):
QObject.__init__(self)
self._currX = 0
self._currY = 0
self._delay = 0.5
self._multiplier = 1.0
self._power = 1.0
self._xIncrement = 1.0
self._starter = False
self._threader = None
@Property(bool)
def starter(self):
return self._starter
@starter.setter
def setStarter(self, val):
print("New val: {0}, oldVal: {1}".format(val,self._starter))
if self._starter == val:
return
self._starter = val
if val:
self.start()
else:
self.stop()
@Property(float)
def multiplier(self):
return self._multiplier
@multiplier.setter
def setMultiplier(self, val):
if self._multiplier == val:
return
print(val)
self._multiplier = val
@Property(int)
def power(self):
return self._power
@power.setter
def setPower(self, val):
if self._power == val:
return
print(val)
self._power = val
@Property(float)
def delay(self):
return self._delay
@delay.setter
def setDelay(self, val):
if self._delay == val:
return
print(val)
self._delay = val
@Property(float)
def xIncrement(self):
return self._xIncrement
@xIncrement.setter
def setXIncrement(self, val):
if self._xIncrement == val:
return
print(val)
self._xIncrement = val
def generatePoint(self):
self._currX += self._xIncrement
self._currY = self._multiplier*(self._currX**self._power)
return self._currX,self._currY
def stop(self):
self._goOn = False
if self._threader is not None:
while self._threader.isRunning():
sleep(0.1)
def start(self):
self._goOn = True
self._threader = Threader(core=self.core)
self._threader.start()
def core(self):
while self._goOn:
x,y = self.generatePoint()
print([x,y])
self.dataReady.emit(x,y)
sleep(self._delay)
class Threader(QThread):
def __init__(self,core,parent=None):
QThread.__init__(self,parent)
self._core = core
self._goOn = False
def run(self):
self._core()
if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
app = QApplication(sys.argv)
manager = Manager()
engine = QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("Manager", manager)
engine.load('main.qml')
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
Код для QML:
ApplicationWindow {
id: mainWindow
width:640
height: 480
title: qsTr("Simple ui")
visible: true
locale:locale
property var controlsColor: Material.DeepPurple
property var controlsAccent: Material.BlueGrey
property real x: 0.0
property int controlsElevation: 6
property int paneElevation: 4
function drawPoint(theX,theY){
console.log(theX);
mainLine.append(theX,theY)
if (theX >= testXAxis.max){
testXAxis.max = theX;
}
if (theY >= testYAxis.max){
testYAxis.max = theY;
}
if (theY <= testYAxis.min){
testYAxis.min = theY;
}
}
function clearLine(){
mainLine.clear();
mainLine.append(0,0);
}
Pane{
id: mainPanel
anchors.fill: parent
//Material.theme: Material.Dark
RowLayout{
id: mainRowLO
anchors.fill: parent
spacing: 15
//Chart pane
Pane{
id: chartPane
Material.elevation: paneElevation
//Material.background: Material.Grey
Layout.fillHeight: true
Layout.fillWidth: true
Layout.minimumHeight: 200
Layout.minimumWidth: 400
ChartView {
id: testChart
title: "Line"
anchors.fill: parent
antialiasing: true
LineSeries {
id: mainLine
name: "LineSeries"
axisX: ValueAxis{
id: testXAxis
min: 0.0
max: 2.0
}
axisY: ValueAxis{
id: testYAxis
min: 0.0
max: 2.0
}
XYPoint { x: 0; y: 0 }
}
}
}
Pane{
id: controlsPane
Material.elevation: paneElevation
//Material.background: Material.Grey
Layout.fillHeight: true
Layout.fillWidth: true
Layout.minimumHeight: 200
Layout.minimumWidth: 200
Layout.maximumWidth: 200
ColumnLayout{
id: controlsColumnLO
anchors.fill: parent
spacing: 40
Label{
id: powerLabel
text: "Exponent"
Layout.topMargin: 40
Layout.leftMargin: 10
Layout.rightMargin: 10
}
SpinBox{
id: powerNum
from: 0
value: 1
to: 5
stepSize: 1
width: 80
validator: DoubleValidator {
bottom: Math.min(powerNum.from, powerNum.to)
top: Math.max(powerNum.from, powerNum.to)
}
Material.foreground: controlsColor
Material.accent: controlsAccent
Material.elevation: controlsElevation
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
editable: true
onValueChanged: function(){
Manager.power = value;
}
}
Label{
id: multiplierLabel
text: "Multiplier"
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
}
Slider{
id: multiplierSlider
from: -50
value: 1
to: 50
Material.foreground: controlsColor
Material.accent: controlsAccent
Material.elevation: controlsElevation
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.fillWidth: true
onValueChanged: function(){
Manager.multiplier = value;
}
}
Label{
id: multValueLabel
text: String(multiplierSlider.value)
Layout.leftMargin: 10
Layout.rightMargin: 10
}
Label{
id: delayLable
text: "Delay[s]"
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
}
Slider{
id: delaySlider
from: 0.05
value: 0.1
to: 1
stepSize: 0.01
Material.foreground: controlsColor
Material.accent: controlsAccent
Material.elevation: controlsElevation
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.fillWidth: true
onValueChanged: function(){
Manager.delay = value;
}
}
Label{
id: incrementLable
text: "Increment"
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
}
Slider{
id: incrementSlider
from: 1.0
value: 1.0
to: 5.0
stepSize: 0.01
Material.foreground: controlsColor
Material.accent: controlsAccent
Material.elevation: controlsElevation
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.fillWidth: true
onValueChanged: function(){
Manager.xIncrement = value;
}
}
Item {
// spacer item
id: controlsSpacer
Layout.fillWidth: true
Layout.fillHeight: true
Pane { anchors.fill: parent }//; Material.background: Material.Light; Material.elevation: 4 } // to visualize the spacer
}
Button{
id: startPointBtn
text: "START"
Material.foreground: controlsColor
Material.accent: controlsAccent
Material.elevation: controlsElevation
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
onClicked: function(){
console.log(text);
console.log(text=="START")
if(text=="START"){
Manager.starter = true;
Manager.dataReady.connect(drawPoint);
clearLine();
text = "STOP";
}
else{
Manager.starter = false;
text = "START";
Manager.dataReady.disconnect(drawPoint);
}
}
}
}
}
}
}
}