1
|
import logging
|
2
|
|
3
|
import matplotlib.pyplot as plt
|
4
|
from PyQt5 import QtCore
|
5
|
from PyQt5 import QtGui
|
6
|
from PyQt5.QtCore import QSize, QThread, QObject, pyqtSignal, QSettings
|
7
|
from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QScrollArea, QGridLayout
|
8
|
from PyQt5.QtWidgets import QMenuBar, QAction, QPushButton
|
9
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
10
|
|
11
|
from aswi2021vochomurka.model.Message import Message
|
12
|
from aswi2021vochomurka.service.mqtt.mqtt_subscriber import MQTTSubscriber
|
13
|
from aswi2021vochomurka.service.subscriber import Subscriber
|
14
|
from aswi2021vochomurka.service.subscriber_callback import SubscriberCallback
|
15
|
from aswi2021vochomurka.service.subscriber_params import SubscriberParams, ConnectionParams
|
16
|
from aswi2021vochomurka.view.logger_view import LoggerView
|
17
|
from aswi2021vochomurka.view.settings import SettingsDialog, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_KEEPALIVE, \
|
18
|
DEFAULT_ANONYMOUS, DEFAULT_USERNAME, DEFAULT_TIMEOUT, DEFAULT_TOPICS, get_settings
|
19
|
|
20
|
|
21
|
class Worker(QObject, SubscriberCallback):
|
22
|
connected = pyqtSignal()
|
23
|
disconnected = pyqtSignal()
|
24
|
error = pyqtSignal(Exception)
|
25
|
newMessage = pyqtSignal(Message)
|
26
|
subscriber: Subscriber = None
|
27
|
params: SubscriberParams
|
28
|
|
29
|
def __init__(self, params: SubscriberParams) -> None:
|
30
|
super().__init__()
|
31
|
self.params = params
|
32
|
|
33
|
def start(self):
|
34
|
self.subscriber = MQTTSubscriber(self, self.params)
|
35
|
self.subscriber.start()
|
36
|
|
37
|
def stop(self):
|
38
|
self.subscriber.stop()
|
39
|
|
40
|
def onConnected(self):
|
41
|
self.connected.emit()
|
42
|
|
43
|
def onDisconnected(self):
|
44
|
self.disconnected.emit()
|
45
|
|
46
|
def onError(self):
|
47
|
pass
|
48
|
|
49
|
def onMessage(self, message: Message):
|
50
|
self.newMessage.emit(message)
|
51
|
# self.window.plot(message)
|
52
|
|
53
|
def onCloseTopic(self, topic: str):
|
54
|
pass
|
55
|
|
56
|
|
57
|
class MainView(QMainWindow):
|
58
|
worker: Worker = None
|
59
|
workerThread: QThread = None
|
60
|
|
61
|
def __init__(self):
|
62
|
super(MainView, self).__init__()
|
63
|
|
64
|
self.chartsNum = 0
|
65
|
self.arrayData = []
|
66
|
|
67
|
self.dataIndex = 0
|
68
|
self.dataDict = {}
|
69
|
|
70
|
self.canvasDict = {}
|
71
|
self.figureDict = {}
|
72
|
|
73
|
# self.toolbar = NavigationToolbar(self.canvas, self)
|
74
|
|
75
|
# self.setMinimumSize(QSize(440, 240))
|
76
|
self.setMinimumSize(QSize(1200, 800))
|
77
|
self.setWindowTitle("MQTT client")
|
78
|
|
79
|
logger = self._createLoggerView()
|
80
|
layout = QVBoxLayout()
|
81
|
layout.addWidget(logger.widget)
|
82
|
# layout.addWidget(self.toolbar)
|
83
|
|
84
|
widget = QWidget()
|
85
|
widget.setLayout(layout)
|
86
|
self.setCentralWidget(widget)
|
87
|
self._createMenuBar()
|
88
|
|
89
|
scrollArea = QScrollArea(self)
|
90
|
scrollArea.setWidgetResizable(True)
|
91
|
scrollContent = QWidget()
|
92
|
self.grid = QGridLayout(scrollContent)
|
93
|
scrollArea.setWidget(scrollContent)
|
94
|
layout.addWidget(scrollArea)
|
95
|
|
96
|
self.init_subscriber()
|
97
|
|
98
|
def _createLoggerView(self):
|
99
|
logger = LoggerView(self)
|
100
|
formatter = logging.Formatter('%(asctime)s %(message)s', '%H:%M')
|
101
|
logger.setFormatter(formatter)
|
102
|
logger.setLevel(logging.INFO)
|
103
|
logging.getLogger('').addHandler(logger)
|
104
|
return logger
|
105
|
|
106
|
def _createMenuBar(self):
|
107
|
menuBar = QMenuBar(self)
|
108
|
settingsAction = QAction("&Settings", self)
|
109
|
settingsAction.triggered.connect(self.settings)
|
110
|
menuBar.addAction(settingsAction)
|
111
|
self.setMenuBar(menuBar)
|
112
|
|
113
|
def plot(self, message: Message):
|
114
|
if message.topic in self.dataDict:
|
115
|
self.dataDict[message.topic].append(message.value)
|
116
|
|
117
|
figure = self.figureDict[message.topic]
|
118
|
figure.clear()
|
119
|
|
120
|
plt.figure(figure.number)
|
121
|
plt.plot(self.dataDict[message.topic])
|
122
|
|
123
|
self.canvasDict[message.topic].draw()
|
124
|
else:
|
125
|
self.dataDict[message.topic] = [message.value]
|
126
|
|
127
|
figure = plt.figure(figsize=[500, 500])
|
128
|
canvas = FigureCanvas(figure)
|
129
|
layout = QHBoxLayout()
|
130
|
|
131
|
plt.plot(self.dataDict[message.topic])
|
132
|
|
133
|
self.canvasDict[message.topic] = canvas
|
134
|
self.figureDict[message.topic] = figure
|
135
|
|
136
|
widget = QWidget()
|
137
|
widget.setLayout(layout)
|
138
|
button = QPushButton(':')
|
139
|
button.setFixedSize(QSize(40, 40))
|
140
|
layout.addWidget(canvas)
|
141
|
layout.addWidget(button)
|
142
|
layout.setAlignment(button, QtCore.Qt.AlignTop)
|
143
|
widget.setMinimumSize(QSize(500, 500))
|
144
|
|
145
|
self.grid.addWidget(widget, int(self.chartsNum / 2), self.chartsNum % 2)
|
146
|
|
147
|
self.chartsNum += 1
|
148
|
|
149
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
150
|
self.worker.stop()
|
151
|
|
152
|
def settings(self):
|
153
|
dialog = SettingsDialog()
|
154
|
if dialog.exec_():
|
155
|
self.reconnect()
|
156
|
|
157
|
def disconnect(self):
|
158
|
self.worker.stop()
|
159
|
self.workerThread.quit()
|
160
|
self.workerThread.wait()
|
161
|
|
162
|
def reconnect(self):
|
163
|
self.disconnect()
|
164
|
self.worker.params = self.getConfigParams()
|
165
|
self.workerThread.start()
|
166
|
|
167
|
def init_subscriber(self):
|
168
|
self.workerThread = QThread()
|
169
|
self.worker = Worker(self.getConfigParams())
|
170
|
self.worker.moveToThread(self.workerThread)
|
171
|
self.workerThread.started.connect(self.worker.start)
|
172
|
self.worker.newMessage.connect(
|
173
|
lambda message: self.plot(message)
|
174
|
)
|
175
|
self.worker.window = self
|
176
|
self.workerThread.start()
|
177
|
|
178
|
def getConfigParams(self) -> SubscriberParams:
|
179
|
settings = get_settings()
|
180
|
|
181
|
connection = ConnectionParams(
|
182
|
settings.value("connection_host", DEFAULT_HOST, str),
|
183
|
settings.value("connection_port", DEFAULT_PORT, int),
|
184
|
settings.value("connection_keepalive", DEFAULT_KEEPALIVE, int)
|
185
|
)
|
186
|
|
187
|
params = SubscriberParams(
|
188
|
settings.value("topics_items", DEFAULT_TOPICS),
|
189
|
settings.value("topics_timeout", DEFAULT_TIMEOUT, int),
|
190
|
connection,
|
191
|
settings.value("connection_anonymous", DEFAULT_ANONYMOUS, bool),
|
192
|
settings.value("connection_username", DEFAULT_USERNAME, str),
|
193
|
settings.value("connection_password", DEFAULT_USERNAME, str),
|
194
|
)
|
195
|
|
196
|
return params
|