はじめに
普段ちょっとしたGUIを作るときにPyQtを使っているのですが、QMLの表示やプロパティの変更に手こずったので、その備忘録です。
画面作成にはQtDesignerを使っています。QtDesignerの使い方はこちらを参考に。
環境
- Windows 10
- Qt Designer 5.11.1
- Python 3.7.3
- PyQt5 5.13.1
作成したサンプル
+ボタンを押すとゲージの値が増え、-ボタンを押すと減るというものです。
ファイルの構成
- qml_gauge_sample_gui.py
- 実行ファイル
- MyGauge.qml
- QMLファイル
- my_gauge.py
- QQuickWidgetを継承し、ソースにMyGauge.qmlをセット
- qml_gauge_sample.ui
- Designerで作成したuiファイル
- qml_gauge_sample_ui.py
- uiファイルからpyuicコマンドで作成したPythonファイル
各ファイルの詳細
①
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Quick Extras module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls.Private 1.0
import QtQuick.Extras 1.4
import QtQuick.Extras.Private 1.0
Gauge {
minimumValue: 0
value: gauge.value
maximumValue: 100
tickmarkStepSize: 10
orientation: Qt.Vertical
}
QtQuick.ExtrasのGaugeを使用します。
valueの値をgauge.valueとして、プログラムから更新できるようにします。
②
import os
from PyQt5.QtCore import pyqtProperty
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import QObject
from PyQt5.QtCore import QUrl
from PyQt5.QtQuickWidgets import QQuickWidget
class GaugeProperty(QObject):
_value_changed_signal = pyqtSignal(int)
def __init__(self, parent=None):
super(GaugeProperty, self).__init__(parent)
self._value = 0
@pyqtProperty(int, notify=_value_changed_signal)
def value(self):
return self._value
@value.setter
def value(self, value):
if self._value == value:
return
self._value = value
self._value_changed_signal.emit(value)
class MyGauge(QQuickWidget):
def __init__(self, parent):
super(MyGauge, self).__init__(parent)
self._gauge = GaugeProperty(self)
self.setResizeMode(self.SizeRootObjectToView)
self.engine().rootContext().setContextProperty("gauge", self._gauge)
directory = os.path.dirname(os.path.abspath(__file__))
self.setSource(QUrl.fromLocalFile(os.path.join(directory,
"MyGauge.qml")))
def increment(self):
self._gauge.value += 1
def decrement(self):
self._gauge.value -= 1
QQuickWidgetを継承したMyGaugeクラスと、ゲージのvalueプロパティを管理するGaugePropertyクラスを作成します。
MyGaugeクラスは、画面作成時に格上げの対象先となります。
③
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GaugeSampleWidget</class>
<widget class="QWidget" name="GaugeSampleWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>94</width>
<height>335</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="MyGauge" name="myGauge" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>71</width>
<height>291</height>
</rect>
</property>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>300</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="_horizontalLayout">
<item>
<widget class="QPushButton" name="_plusButton">
<property name="font">
<font>
<pointsize>9</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="_minusButton">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>MyGauge</class>
<extends>QWidget</extends>
<header>my_gauge.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>_plusButton</sender>
<signal>pressed()</signal>
<receiver>GaugeSampleWidget</receiver>
<slot>increment_gauge_value()</slot>
<hints>
<hint type="sourcelabel">
<x>28</x>
<y>314</y>
</hint>
<hint type="destinationlabel">
<x>159</x>
<y>310</y>
</hint>
</hints>
</connection>
<connection>
<sender>_minusButton</sender>
<signal>pressed()</signal>
<receiver>GaugeSampleWidget</receiver>
<slot>decrement_gauge_value()</slot>
<hints>
<hint type="sourcelabel">
<x>68</x>
<y>313</y>
</hint>
<hint type="destinationlabel">
<x>69</x>
<y>394</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>increment_gauge_value()</slot>
<slot>decrement_gauge_value()</slot>
</slots>
</ui>
MyGaugeクラスに格上げされたQWidgetと、ゲージのインクリメント/デクリメントを行うボタンが二つ配置されています。
④
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'qml_gauge_sample.ui'
#
# Created by: PyQt5 UI code generator 5.13.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_GaugeSampleWidget(object):
def setupUi(self, GaugeSampleWidget):
GaugeSampleWidget.setObjectName("GaugeSampleWidget")
GaugeSampleWidget.resize(94, 335)
self.myGauge = MyGauge(GaugeSampleWidget)
self.myGauge.setGeometry(QtCore.QRect(10, 10, 71, 291))
self.myGauge.setObjectName("myGauge")
self.horizontalLayoutWidget = QtWidgets.QWidget(GaugeSampleWidget)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 300, 71, 31))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self._horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self._horizontalLayout.setContentsMargins(0, 0, 0, 0)
self._horizontalLayout.setObjectName("_horizontalLayout")
self._plusButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
font = QtGui.QFont()
font.setPointSize(9)
font.setBold(True)
font.setWeight(75)
self._plusButton.setFont(font)
self._plusButton.setObjectName("_plusButton")
self._horizontalLayout.addWidget(self._plusButton)
self._minusButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self._minusButton.setFont(font)
self._minusButton.setObjectName("_minusButton")
self._horizontalLayout.addWidget(self._minusButton)
self.retranslateUi(GaugeSampleWidget)
self._plusButton.pressed.connect(GaugeSampleWidget.increment_gauge_value)
self._minusButton.pressed.connect(GaugeSampleWidget.decrement_gauge_value)
QtCore.QMetaObject.connectSlotsByName(GaugeSampleWidget)
def retranslateUi(self, GaugeSampleWidget):
_translate = QtCore.QCoreApplication.translate
GaugeSampleWidget.setWindowTitle(_translate("GaugeSampleWidget", "Form"))
self._plusButton.setText(_translate("GaugeSampleWidget", "+"))
self._minusButton.setText(_translate("GaugeSampleWidget", "-"))
from my_gauge import MyGauge
$ pyuic5 qml_gauge_sample.ui > qml_gauge_sample_ui.py
で作成した、Pythonファイル。
⑤
import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
from qml_gauge_sample_ui import Ui_GaugeSampleWidget
class QmlGaugeSampleGui(QMainWindow, Ui_GaugeSampleWidget):
def __init__(self, parent=None):
super(QmlGaugeSampleGui, self).__init__(parent)
self.setupUi(self)
@pyqtSlot()
def increment_gauge_value(self):
self.myGauge.increment()
@pyqtSlot()
def decrement_gauge_value(self):
self.myGauge.decrement()
if __name__ == '__main__':
argvs = sys.argv
app = QApplication(argvs)
qml_gauge_sample_gui = QmlGaugeSampleGui()
qml_gauge_sample_gui.show()
sys.exit(app.exec_())
Designerで作成した、Ui_GaugeSampleWidgetを継承したQmlGaugeSampleGuiクラスを作ります。
QmlGaugeSampleGuiクラスにはincrement_gauge_value()関数とdecrement_gauge_value()関数を持っており、それぞれ+ボタン、-ボタンが押されると呼ばれるようにDesigner上でSignal/Slotの設定がされています。
あとは、
$ python qml_gauge_sample_gui.py
で実行すれば、ゲージQMLが埋め込まれた画面が表示されます。
参考URL
- https://codeday.me/jp/qa/20190428/726754.html
- https://stackoverflow.com/questions/49930114/how-to-set-values-in-qml-using-pyqt5
- https://doc.qt.io/qtforpython/PySide2/QtQuickWidgets/QQuickWidget.html#PySide2.QtQuickWidgets.PySide2.QtQuickWidgets.QQuickWidget.ResizeMode
- https://doc.qt.io/qt-5/qml-qtquick-extras-gauge.html