Edited at

Qt DesignerとPyQt5でGUIなMQTTサブスクライバーを作る時のメモ


はじめに

職場の建物が新築されたので,各階の分電盤に電力計を設置し,1か所でモニタリングできるシステムを構築しようと考えた。

電力計は@arms22 さんの記事を参考に製作した。

システムの全体図

system-overview-shrink.png


開発環境の構築

Windows10上に,先人の知恵を参考に環境構築。

paho-mqttはpipでインストール


MQTTブローカーの準備

稼働中のサーバにmosquittoをインストールしても良かったのだが,Raspberry Piを使うことにした。

普通にRaspberry Piのセットアップ後,apt-getでpython3,mosquittoをインストール。

pipでPyQt5,paho-mqttをインストール。

Raspberry PiのIPアドレスは静的なものを振っておく。


UIのデザイン

Qt Designerを起動

QtDesigner_newform.png

新しいフォームでは,「Main Window」を選ぶ。(Dialogでもいいが,その場合UIを表示するコードが少し変わる)

適当にUIをデザインする。

この例ではLCD Numberウィジェットを2つ配置してみた。この際,各ウィジェットのobjectNameは分かりやすいものに変えておくと吉。

QtDesigner_designform.png

適当な名前(test_ui.ui)を付けて保存する。


pyuic5で.uiを.pyに変換

pyuic5のインストールパスは自分の場合,PyQt5をインストールした際--userオプションを付けないとインストールできなかったので,C:\Users\[username]\AppData\Roaming\Python\Python36\Scriptsに入っていた。

次のコマンドで.uiファイルを.pyに変換する。

> pyuic5 test_ui.ui -o test_ui.py


変換した.pyを表示するコードを書く


test.py

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.Qt import *
from test_ui import Ui_MainWindow

class Test(QMainWindow):
def __init__(self):
super(Test, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)

if __name__ == '__main__':
app = QApplication(sys.argv)
window = Test()
window.show()
sys.exit(app.exec())


次のコマンドでデザインしたUIが表示されるはず。

> python test.py


MQTTのコードを埋め込む


test.py

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.Qt import *
from test_ui import Ui_MainWindow
import paho.mqtt.client as mqtt

class Test(QMainWindow):
def __init__(self, mqtt_client):
super(Test, self).__init__()
self._mqtt_client = mqtt_client
self._mqtt_client.on_connect = self.on_connect
self._mqtt_client.on_message = self.on_message

self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# トピックと表示するウィジェットを対応させる辞書
self.lcd = {
"test/A": self.ui.lcdTestA,
"test/B": self.ui.lcdTestB,
}

def on_message(self, client, userdata, msg):
print("Topic: " + str(msg.topic))
print("Message: " + str(msg.payload))
# 受信したtopicにより対応するwidgetにpayloadを表示する処理
for i in self.lcd.items():
if str(i[0]) == str(msg.topic):
i[1].display(format("%.1f" % float(msg.payload)))

def on_connect(self, client, userdata, flags, rc):
print("connected with result code: " + str(rc))
client.subscribe("#") # 全てのトピックを受信

if __name__ == '__main__':
client = mqtt.Client()
app = QApplication(sys.argv)
window = Test(client)
window.show()

client.connect('localhost', 1883)
client.loop_start()
try:
sys.exit(app.exec_())
finally:
client.loop_stop()


次のコマンドでUIを表示させる。

python test.py

別のシェルからpublishしてみる。

% mosquitto_pub -h localhost -t test/A -m 100

% mosquitto_pub -h localhost -t test/B -m 3.14

次のように表示されるはず。

QtDesigner_run.png


実際に作ったもの

WattMeterViewer.png

表示が0なのは,電力計を設置してないところ。


参考にしたページ