Projekt

Obecné

Profil

Stáhnout (8.19 KB) Statistiky
| Větev: | Tag: | Revize:
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
    """
23
    Worker representing thread
24
    """
25
    connected = pyqtSignal()
26
    disconnected = pyqtSignal()
27
    error = pyqtSignal(Exception)
28
    newMessage = pyqtSignal(Message)
29
    closeTopic = pyqtSignal(str)
30
    subscriber: Subscriber = None
31
    params: SubscriberParams
32

    
33
    def __init__(self, params: SubscriberParams) -> None:
34
        """
35
        Constructor
36
        """
37
        super().__init__()
38
        self.params = params
39

    
40
    def start(self):
41
        """
42
        Start worker
43
        """
44
        self.subscriber = MQTTSubscriber(self, self.params)
45
        self.subscriber.start()
46

    
47
    def stop(self):
48
        """
49
        Stop worker
50
        """
51
        self.subscriber.stop()
52

    
53
    def onConnected(self):
54
        """
55
        Emit connection signal
56
        """
57
        self.connected.emit()
58

    
59
    def onDisconnected(self):
60
        """
61
        Emit disconnection signal
62
        """
63
        self.disconnected.emit()
64

    
65
    def onError(self):
66
        pass
67

    
68
    def onMessage(self, message: Message):
69
        """
70
        Emit message signal
71
        :param message: message
72
        """
73
        self.newMessage.emit(message)
74

    
75
    def onCloseTopic(self, topic: str):
76
        """
77
        Emit close topic signal
78
        :param topic: topic
79
        """
80
        print("Close topic")
81
        self.closeTopic.emit(topic)
82

    
83

    
84
class MainView(QMainWindow):
85
    """
86
    Main window of application.
87
    Displays all parts of the application.
88
    """
89
    worker: Worker = None
90
    workerThread: QThread = None
91

    
92
    def __init__(self):
93
        """
94
        Constructor - displays all parts of the application.
95
        """
96
        super(MainView, self).__init__()
97

    
98
        self.chartsNum = 0
99
        self.dataDict = {}
100
        self.canvasDict = {}
101
        self.figureDict = {}
102
        self.widgetDict = {}
103
        self.widgetList = []
104

    
105
        self.setMinimumSize(QSize(1200, 800))
106
        self.setWindowTitle("MQTT client")
107

    
108
        logger = self._createLoggerView()
109
        layout = QVBoxLayout()
110
        layout.addWidget(logger.widget)
111

    
112
        widget = QWidget()
113
        widget.setLayout(layout)
114
        self.setCentralWidget(widget)
115
        self._createMenuBar()
116

    
117
        scrollArea = QScrollArea(self)
118
        scrollArea.setWidgetResizable(True)
119
        scrollContent = QWidget()
120
        self.grid = QGridLayout(scrollContent)
121
        scrollArea.setWidget(scrollContent)
122
        layout.addWidget(scrollArea)
123

    
124
        self.init_subscriber()
125

    
126
    def _createLoggerView(self):
127
        """
128
        Create logger view
129
        """
130
        logger = LoggerView(self)
131
        formatter = logging.Formatter('%(asctime)s %(message)s', '%H:%M')
132
        logger.setFormatter(formatter)
133
        logger.setLevel(logging.INFO)
134
        logging.getLogger('').addHandler(logger)
135
        return logger
136

    
137
    def _createMenuBar(self):
138
        """
139
        Creates menu bar
140
        """
141
        menuBar = QMenuBar(self)
142
        settingsAction = QAction("&Settings", self)
143
        settingsAction.triggered.connect(self.settings)
144
        menuBar.addAction(settingsAction)
145
        self.setMenuBar(menuBar)
146

    
147
    def plot(self, message: Message):
148
        """
149
        Plots new charts or updates old ones
150
        :param message: message
151
        """
152
        if message.topic in self.dataDict:
153
            # topic already exists
154
            self.dataDict[message.topic].append(message.value)
155

    
156
            figure = self.figureDict[message.topic]
157
            figure.clear()
158

    
159
            figure = plt.figure(figure.number)
160
            figure.suptitle(message.topic)
161
            plt.plot(self.dataDict[message.topic])
162

    
163
            self.canvasDict[message.topic].draw()
164
        else:
165
            # new topic
166
            self.dataDict[message.topic] = [message.value]
167

    
168
            figure = plt.figure(figsize=[500, 500])
169
            canvas = FigureCanvas(figure)
170
            self.layout = QHBoxLayout()
171

    
172
            plt.plot(self.dataDict[message.topic])
173
            figure.suptitle(message.topic)
174

    
175
            self.canvasDict[message.topic] = canvas
176
            self.figureDict[message.topic] = figure
177

    
178
            widget = QWidget()
179
            self.widgetDict[message.topic] = widget
180
            self.widgetList.append(widget)
181
            widget.setLayout(self.layout)
182
            button = QPushButton(':')
183
            button.setFixedSize(QSize(40, 40))
184
            self.layout.addWidget(canvas)
185
            self.layout.addWidget(button)
186
            self.layout.setAlignment(button, QtCore.Qt.AlignTop)
187
            widget.setMinimumSize(QSize(500, 500))
188

    
189
            self.grid.addWidget(widget, int(self.chartsNum / 2), self.chartsNum % 2)
190

    
191
            self.chartsNum += 1
192

    
193
    def deletePlot(self, topic: str):
194
        """
195
        Deletes plot
196
        :param topic: topic
197
        """
198
        widget = self.widgetDict[topic]
199
        self.widgetList.remove(widget)
200
        widget.setParent(None)
201

    
202
        del self.widgetDict[topic]
203
        del self.canvasDict[topic]
204
        del self.figureDict[topic]
205
        del self.dataDict[topic]
206

    
207
        self.reorganizePlots()
208

    
209
    def reorganizePlots(self):
210
        """
211
        Reorganize plots
212
        """
213
        count = 0
214
        for widget in self.widgetList:
215
            self.grid.addWidget(widget, int(count / 2), count % 2)
216
            count += 1
217

    
218
        self.chartsNum -= 1
219

    
220
    def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
221
        self.worker.stop()
222

    
223
    def settings(self):
224
        """
225
        Opens settings dialog
226
        """
227
        dialog = SettingsDialog()
228
        if dialog.exec_():
229
            self.reconnect()
230

    
231
    def disconnect(self):
232
        """
233
        Disconnect
234
        """
235
        self.worker.stop()
236
        self.workerThread.quit()
237
        self.workerThread.wait()
238

    
239
    def reconnect(self):
240
        """
241
        Reconnect
242
        """
243
        self.disconnect()
244
        self.worker.params = self.getConfigParams()
245
        self.workerThread.start()
246

    
247
    def init_subscriber(self):
248
        """
249
        Initialization of subscriber
250
        """
251
        self.workerThread = QThread()
252
        self.worker = Worker(self.getConfigParams())
253
        self.worker.moveToThread(self.workerThread)
254
        self.workerThread.started.connect(self.worker.start)
255
        self.worker.newMessage.connect(
256
            lambda message: self.plot(message)
257
        )
258
        self.worker.closeTopic.connect(
259
            lambda topic: self.deletePlot(topic)
260
        )
261
        self.worker.window = self
262
        self.workerThread.start()
263

    
264
    def getConfigParams(self) -> SubscriberParams:
265
        """
266
        Returns config parameters
267
        :return: config parameters
268
        """
269
        settings = get_settings()
270

    
271
        connection = ConnectionParams(
272
            settings.value("connection_host", DEFAULT_HOST, str),
273
            settings.value("connection_port", DEFAULT_PORT, int),
274
            settings.value("connection_keepalive", DEFAULT_KEEPALIVE, int)
275
        )
276

    
277
        params = SubscriberParams(
278
            settings.value("topics_items", DEFAULT_TOPICS),
279
            settings.value("topics_timeout", DEFAULT_TIMEOUT, int),
280
            connection,
281
            settings.value("connection_anonymous", DEFAULT_ANONYMOUS, bool),
282
            settings.value("connection_username", DEFAULT_USERNAME, str),
283
            settings.value("connection_password", DEFAULT_USERNAME, str),
284
        )
285

    
286
        return params
(3-3/4)