Mercurial > lbo > hg > stex
view client/client.py @ 3:38725950fe15
Make graphs moving.
There are some weird issues with matplotlib rendering eating up all memory.
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Thu, 28 Feb 2019 20:01:03 +0100 |
parents | 61952d5a8718 |
children | 7a722bb179ff |
line wrap: on
line source
#!/usr/bin/env python3 import arguments import os import os.path as path import sys import zmq import PyQt5.QtWidgets as wid import PyQt5.QtCore as core from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure class Creds: user = '' password = '' addr = '' class ClientConfigDialog(wid.QDialog): addr = None user = None password = None defaults = False def __init__(self, parent, defaults=False): super().__init__(parent) self.defaults = defaults def show_dialog(self): vbox = wid.QVBoxLayout(self) self.addr = wid.QLineEdit() self.addr.readOnly = False self.addr.setText(self.load_default('addr')) self.user = wid.QLineEdit() self.user.readOnly = False self.user.setText(self.load_default('user')) self.password = wid.QLineEdit() self.password.readOnly = False self.password.setText(self.load_default('password')) self.password.setEchoMode(wid.QLineEdit.Password) if self.defaults and self.addr.text() and self.user.text() and self.password.text(): self.finished.emit(1) self.accepted.emit() return hbox_addr = wid.QHBoxLayout() hbox_addr.addWidget(wid.QLabel('Address of server: ')) hbox_addr.addWidget(self.addr) hbox_user = wid.QHBoxLayout() hbox_user.addWidget(wid.QLabel('Username: ')) hbox_user.addWidget(self.user) hbox_password = wid.QHBoxLayout() hbox_password.addWidget(wid.QLabel('Password: ')) hbox_password.addWidget(self.password) ok = wid.QPushButton('OK') ok.clicked.connect(self.ok_clicked) ok.clicked.connect(super().done) vbox.addLayout(hbox_addr) vbox.addLayout(hbox_user) vbox.addLayout(hbox_password) vbox.addWidget(ok) self.setVisible(True) default_addr_file = '.config/stex/' def load_default(self, id): try: home = os.environ.get('HOME') if not home: home = path.join('/home/', os.environ.get('USER')) with open(path.join(home, self.default_addr_file, id), 'r') as f: return f.readline() except Exception as e: sys.stderr.write("Couldn't read file: {}\n".format(e)) return '' def save_default(self, id, val): try: home = os.environ.get('HOME') if not home: home = path.join('/home/', os.environ.get('USER')) da = path.join(home, self.default_addr_file, id) os.makedirs(path.dirname(da), exist_ok=True) f = open(da, 'w' if os.access(da, os.F_OK) else 'x') f.write(val) except Exception as e: sys.stderr.write("Couldn't write file: {}\n".format(e)) return def ok_clicked(self): self.save_default('addr', self.addr.text()) self.save_default('user', self.user.text()) self.save_default('password', self.password.text()) def creds(self): creds = Creds() creds.user = self.user.text() creds.password = self.password.text() creds.addr = self.addr.text() return creds class StockGraph(FigureCanvas): fig = None axes = None sym = '' MAX_LEN = 500 XAXIS = [i for i in range(0, MAX_LEN)] history = [0 for i in range(0, MAX_LEN)] def __init__(self, sym, dim): self.sym = sym self.fig = Figure(figsize=(dim or (200,100)), dpi=90) self.axes = self.fig.add_subplot(111) super().__init__(self.fig) super().setSizePolicy(wid.QSizePolicy.Expanding, wid.QSizePolicy.Expanding) super().updateGeometry() def update_stock(self, value): self.history.append(value/100) self.history[0:] = self.history[1:] self.plot() def plot(self): self.axes.clear() self.axes.grid(True) self.axes.set_xlim(0, self.MAX_LEN) self.axes.set_title(self.sym) self.axes.plot(self.XAXIS, self.history) self.draw_idle() print('DEBUG: Would have drawn') class StockWidget(wid.QWidget): graph = None def __init__(self, graph): super().__init__() self.graph = graph graph.show() #self.update(5000) mainvbox = wid.QVBoxLayout(self) mainvbox.addWidget(self.graph) mainvbox.addLayout(self.init_buttonbox()) self.show() def init_buttonbox(self): buy = wid.QPushButton(' BUY ') sell = wid.QPushButton(' SELL ') self.current_state = wid.QLineEdit() self.current_state.setReadOnly(True) self.current_state.setAlignment(core.Qt.AlignCenter) hbox = wid.QHBoxLayout() hbox.addWidget(buy) hbox.addWidget(sell) hbox.addWidget(self.current_state) return hbox def update(self, val): print('DEBUG: Update {} with {}'.format(self.graph.sym, val)) self.graph.update_stock(val) self.mainvbox.adjustSize() self.current_state.setText('{} pc / {} ø/pc / {} ø'.format('?', val/100, val/100)) class ClientSocket: zctx = None sock = None callback = None socknot = None def __init__(self, creds, callback): """callback is a function taking received data dicts.""" self.callback = callback self.zctx = zmq.Context() self.sock = self.zctx.socket(zmq.SUB) self.sock.setsockopt(zmq.IPV6, 1) self.sock.subscribe('') self.sock.connect('tcp://{}'.format(creds.addr)) self.sock.setsockopt(zmq.RCVTIMEO, 0) fd = self.sock.getsockopt(zmq.FD) self.socknot = core.QSocketNotifier(fd, core.QSocketNotifier.Read) self.socknot.activated.connect(self.on_activated) def on_activated(self, _sock): try: while True: msg = self.sock.recv_json() print('DEBUG: received something') if '_stockdata' not in msg: return self.callback(msg) except Exception: return class Client(arguments.BaseArguments, wid.QWidget): _doc = """ Usage: stex [options] Options --defaults Use cached defaults if available. """ creds = Creds() def __init__(self): super(wid.QWidget, self).__init__() super(arguments.BaseArguments, self).__init__(doc=self._doc) ccd = ClientConfigDialog(self, defaults=self.defaults) ccd.accepted.connect(lambda: self.set_creds(ccd.creds())) ccd.accepted.connect(self.start_wait_window) ccd.show_dialog() def set_creds(self, creds): self.creds = creds def start_wait_window(self): self.sock = ClientSocket(self.creds, self.on_new_data) print('DEBUG: Started wait') stock_widgets = {} def on_new_data(self, stockdata): print('DEBUG: ' + str(stockdata)) for sym, val in sorted(stockdata.items()): if sym in self.stock_widgets: self.stock_widgets[sym].update(val) elif sym != '_stockdata': sw = StockWidget(StockGraph(sym, None)) self.stock_widgets[sym] = sw self.add_stock_widget(sw) main_window_active = False mainvbox = None hboxes = [] widgets_per_hbox = 2 def add_stock_widget(self, sw): if not self.main_window_active: self.start_main_window() if len(self.hboxes) == 0 or self.hboxes[-1].count() >= self.widgets_per_hbox: hbox = wid.QHBoxLayout() sw.show() hbox.addWidget(sw) self.hboxes.append(hbox) self.mainvbox.addLayout(hbox) else: self.hboxes[-1].addWidget(sw) return def start_main_window(self): print('DEBUG: started main window') self.mainvbox = wid.QVBoxLayout(self) self.main_window_active = True self.show() def main(): app = wid.QApplication(sys.argv) client = Client() sys.exit(app.exec_()) if __name__ == '__main__': main()