class QTZOCPNode(QWidget): def __init__(self): super(QTZOCPNode, self).__init__() self.qle = QTextEdit(self) self.qle.move(1, 1) self.qle.resize(640, 480) self.init_zocp() self.show() def init_zocp(self): self.z = ZOCP("QT UI TEST") self.z.register_float("myFloat", 2.3, 'rw', 0, 5.0, 0.1) self.notifier = QSocketNotifier(self.z.inbox.getsockopt(zmq.FD), QSocketNotifier.Read) self.notifier.setEnabled(True) self.notifier.activated.connect(self.zocp_event) self.z.on_modified = self.on_modified self.z.start() def zocp_event(self): print("ZOCP EVENT START") self.z.run_once(0) print("ZOCP EVENT END") def on_modified(self, peer, name, data, *args, **kwargs): t = self.qle.toPlainText() t = "{0}\n{1}".format(data, t) self.qle.setPlainText(t) def closeEvent(self, *args): print(args) self.z.stop() del self.z
class Signal(QObject): signal = pyqtSignal(int) fds = {} def __init__(self, signum, parent): super(Signal, self).__init__(parent) self.signum = signum self.sn = None self.fd = [None, None] if self.setupHandler() < 0: return self.sn = QSocketNotifier(self.fd[1].fileno(), QSocketNotifier.Read, parent) self.sn.activated.connect(self.handleSignal) def __del__(self): #signal.signal( self.signum, signal.SIG_DFL) if self.signum in Signal.fds: Signal.fds.pop(self.signum) if self.fd[0] is not None: self.fd[0].close() if self.fd[1] is not None: self.fd[1].close() #super(Signal,self).__del__() @staticmethod def create(signum, parent): syslog.syslog(syslog.LOG_DEBUG, "DEBUG creating Signal instance for %d" % signum) if signum in Signal.fds: if Signal.fds[signum].sn: Signal.fds[signum].sn.deleteLater() del (Signal.fds[signum]) return Signal(signum, parent) def handleSignal(self): syslog.syslog(syslog.LOG_DEBUG, "DEBUG handling signal %d" % self.signum) self.sn.setEnabled(False) self.fd[1].recv(1) self.signal.emit(self.signum) self.sn.setEnabled(True) def setupHandler(self): syslog.syslog(syslog.LOG_DEBUG, "DEBUG setting up handler for signal %d" % self.signum) self.fd = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) if not self.fd: return -1 Signal.fds[self.signum] = self signal.signal(self.signum, self.handler) signal.siginterrupt(self.signum, False) return 0 @staticmethod def handler(signum, frame): syslog.syslog(syslog.LOG_DEBUG, "DEBUG handling signal %d" % signum) Signal.fds[signum].fd[0].send(bytes([1]))
class ServerSocket(QObject): """ This class is acts a bridge between a server socket and the Qt event loop. See the ClientSocket class for a more detailed explanation. """ def __init__(self, logger, parent=None): QObject.__init__(self, parent) self._logger = logger self._socket = None self._connected = False self._accept_notifier = None @property def connected(self): """Is the underlying socket connected?""" return self._connected def connect(self, sock): """Sets the underlying socket to utilize.""" self._accept_notifier = QSocketNotifier( sock.fileno(), QSocketNotifier.Read, self ) self._accept_notifier.activated.connect(self._notify_accept) self._accept_notifier.setEnabled(True) self._socket = sock self._connected = True def disconnect(self, err=None): """Terminates the current connection.""" if not self._socket: return if err: self._logger.warning("Connection lost") self._logger.exception(err) self._accept_notifier.setEnabled(False) try: self._socket.close() except socket.error: pass self._socket = None self._connected = False def _notify_accept(self): """Callback called when a client is connecting.""" while True: try: sock, address = self._socket.accept() except socket.error as e: if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK): break self.disconnect(e) break self._accept(sock) def _accept(self, socket): """Handles the client who newly connected.""" raise NotImplementedError("accept() is not implemented")
class FileListStream(QObject): """FileListStream represents a stream of filenames read from stdin or from other sources that is visualized in the FileView. """ sig_file_added = pyqtSignal(FileInfo) sig_end_of_stream = pyqtSignal() sig_error = pyqtSignal() @staticmethod def from_location(app, linesep, location): if location.get_path() in ["/stdin", "stdin"]: tee_fd, stream_id = app.stream_manager.get_stdin() else: raise Exception("FileListStream: unknown location: %s", location) return FileListStream(app.vfs, tee_fd, linesep) @property def sig_finished(self): return self.sig_end_of_stream def __init__(self, vfs: 'VirtualFilesystem', fp: IO, linesep: str = "\n") -> None: super().__init__() self.vfs = vfs self.fp = fp self.linesep = linesep self.readliner = None self.socket_notifier: Optional[QSocketNotifier] = None def close(self): self.fp.close() def start(self): self.readliner = non_blocking_readline(self.fp, self.linesep) self.socket_notifier = QSocketNotifier(self.fp.fileno(), QSocketNotifier.Read) self.socket_notifier.activated.connect(self._on_activated) def _on_activated(self, fd: int) -> None: while True: try: filename: str = next(self.readliner) except StopIteration: self.socket_notifier.setEnabled(False) self.socket_notifier = None self.sig_end_of_stream.emit() return else: if filename is not None: location = Location.from_path(filename) self.sig_file_added.emit(self.vfs.get_fileinfo(location)) else: return
class QWZMQListener(QWidget): def __init__(self, **kwargs): QWidget.__init__(self, parent=None) #logger.debug('In QWZMQListener.__init__') self.timeout = kwargs.get('timeout', 1000) _is_normal = kwargs.get('is_normal', True) _on_poll = kwargs.get('on_poll', self.on_zmq_poll) _host = kwargs.get('host', 'localhost') _platform = kwargs.get('platform', 6) _topicfilter = kwargs.get('topicfilter', b'') # b'10001' _uri = 'tcp://%s:%d' % (_host, front_pub_port(_platform)) # 'tcp://localhost:30016' if _is_normal : self.init_connect_zmq(_on_poll, _uri, _topicfilter) def init_connect_zmq(self, on_poll, uri, topicfilter): logger.debug('QWZMQListener.init_connect_zmq uri=%s' % uri) self.zmq_context = zmq.Context(1) self.zmq_socket = self.zmq_context.socket(zmq.SUB) self.zmq_socket.connect(uri) self.zmq_socket.setsockopt(zmq.SUBSCRIBE, topicfilter) self.zmq_notifier = QSocketNotifier(self.zmq_socket.getsockopt(zmq.FD), QSocketNotifier.Read, self) self.zmq_notifier.activated.connect(on_poll) def on_zmq_poll(self): """Needs to be re-implemented to do real work with messages from zmq. """ self.zmq_notifier.setEnabled(False) flags = self.zmq_socket.getsockopt(zmq.EVENTS) flag = 'UNKNOWN' msg = '' if flags & zmq.POLLIN : flag = 'POLLIN' msg = self.zmq_socket.recv_multipart() self.setWindowTitle(str(msg)) elif flags & zmq.POLLOUT : flag = 'POLLOUT' elif flags & zmq.POLLERR : flag = 'POLLERR' else : pass print("Flag zmq.%s in %d msg: %s" % (flag, flags, msg)) self.zmq_notifier.setEnabled(True) self.kick_zmq() # WITHOUT THIS LINE IT WOULD NOT CALL on_read_msg AGAIN! def kick_zmq(self): """ WITHOUT THIS LINE IT WOULD NOT CALL on_read_msg AGAIN! """ _flags = self.zmq_socket.getsockopt(zmq.EVENTS) def closeEvent(self, e) : logger.debug('%s.closeEvent' % self._name) QWidget.closeEvent(self, e)
class XMMSConnector(QObject): def __init__(self, xmms): QObject.__init__(self) fd = xmms.get_fd() self.xmms = xmms self.xmms.set_need_out_fun(self.checkWrite) self.rSock = QSocketNotifier(fd, QSocketNotifier.Read, self) self.rSock.activated.connect(self.handleRead) self.rSock.setEnabled(True) self.wSock = QSocketNotifier(fd, QSocketNotifier.Write, self) self.wSock.activated.connect(self.handleWrite) self.wSock.setEnabled(False) def checkWrite(self, i): if self.xmms.want_ioout(): self.toggleWrite(True) else: self.toggleWrite(False) def toggleRead(self, bool_val): self.rSock.setEnabled(bool_val) def toggleWrite(self, bool_val): self.wSock.setEnabled(bool_val) def handleRead(self, i): self.xmms.ioin() def handleWrite(self, i): self.xmms.ioout()
def register_io(self, fd, callback, mode, *args, **kwargs): handler = six.next(self._handlers) notifier = QSocketNotifier(self.fd_number(fd), self.constants[mode]) with self._mutex: self._io_handlers[handler] = notifier def _io_cb(*_): if not self._safe_callback(callback, fd, *args, **kwargs): self.unregister_io(handler) with self._mutex: # we need to store the closure callback to avoid the garbage collector # from collecting the closure self._io_handlers[handler] = (notifier, _io_cb) notifier.setEnabled(True) notifier.activated.connect(_io_cb)
def register_io(self, fd, callback, mode, *args, **kwargs): handler = six.next(self._handlers) notifier = QSocketNotifier(self.fd_number(fd), self.constants[mode]) with self._mutex: self._io_handlers[handler] = notifier def _io_cb(*_): if not self._safe_callback(callback, fd, *args, **kwargs): self.unregister_io(handler) with self._mutex: # we need to store the closure callback to avoid the garbage collector # from collecting the closure self._io_handlers[handler] = (notifier, _io_cb) notifier.setEnabled(True) notifier.activated.connect(_io_cb)
class _QtFIFOReader(QObject): """A FIFO reader based on a QSocketNotifier.""" got_line = pyqtSignal(str) def __init__(self, filepath, parent=None): super().__init__(parent) self._filepath = filepath # We open as R/W so we never get EOF and have to reopen the pipe. # See http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ # We also use os.open and os.fdopen rather than built-in open so we # can add O_NONBLOCK. fd = os.open(filepath, os.O_RDWR | os.O_NONBLOCK) # pylint: disable=no-member self.fifo = os.fdopen(fd, 'r') self._notifier = QSocketNotifier(fd, QSocketNotifier.Read, self) self._notifier.activated.connect(self.read_line) @pyqtSlot() def read_line(self): """(Try to) read a line from the FIFO.""" log.procs.debug("QSocketNotifier triggered!") self._notifier.setEnabled(False) for line in self.fifo: self.got_line.emit(line.rstrip('\r\n')) self._notifier.setEnabled(True) def cleanup(self): """Clean up so the FIFO can be closed.""" self._notifier.setEnabled(False)
class _QtFIFOReader(QObject): """A FIFO reader based on a QSocketNotifier.""" got_line = pyqtSignal(str) def __init__(self, filepath, parent=None): super().__init__(parent) self._filepath = filepath # We open as R/W so we never get EOF and have to reopen the pipe. # See http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ # We also use os.open and os.fdopen rather than built-in open so we # can add O_NONBLOCK. # pylint: disable=no-member,useless-suppression fd = os.open(filepath, os.O_RDWR | os.O_NONBLOCK) self.fifo = os.fdopen(fd, 'r') self._notifier = QSocketNotifier(fd, QSocketNotifier.Read, self) self._notifier.activated.connect(self.read_line) @pyqtSlot() def read_line(self): """(Try to) read a line from the FIFO.""" log.procs.debug("QSocketNotifier triggered!") self._notifier.setEnabled(False) for line in self.fifo: self.got_line.emit(line.rstrip('\r\n')) self._notifier.setEnabled(True) def cleanup(self): """Clean up so the FIFO can be closed.""" self._notifier.setEnabled(False)
class QtListener: def __init__(self, evtype, fileno, cb, tb=None, mark_as_closed=False): self.evtype, self.fileno, self.cb = evtype, fileno, cb self.tb, self.mark_as_closed = tb, mark_as_closed self.notifier = QSocketNotifier(fileno, self.eventType(evtype)) self.notifier.activated.connect(cb) self.spent = False def __del__(self): self.notifier.setEnabled(False) self.notifier = None def eventType(self, evtype): assert evtype in (BaseHub.READ, BaseHub.WRITE) if evtype == BaseHub.READ: return QSocketNotifier.Read elif evtype == BaseHub.WRITE: return QSocketNotifier.Write def defang(self): self.notifier.activated.disconnected(self.cb) if self.mark_as_closed is not None: self.mark_as_closed() self.spent = True
class QTZOCPNode(QWidget): def __init__(self): super(QTZOCPNode, self).__init__() self.qle = QTextEdit(self) self.qle.move(1, 1) self.qle.resize(640,480) self.init_zocp() self.show() def init_zocp(self): self.z = ZOCP("QT UI TEST") self.z.register_float("myFloat", 2.3, 'rw', 0, 5.0, 0.1) self.notifier = QSocketNotifier( self.z.inbox.getsockopt(zmq.FD), QSocketNotifier.Read ) self.notifier.setEnabled(True) self.notifier.activated.connect(self.zocp_event) self.z.on_modified = self.on_modified self.z.start() def zocp_event(self): print("ZOCP EVENT START") self.z.run_once(0) print("ZOCP EVENT END") def on_modified(self, peer, name, data, *args, **kwargs): t = self.qle.toPlainText() t = "{0}\n{1}".format(data, t) self.qle.setPlainText(t) def closeEvent(self, *args): print(args) self.z.stop() del self.z
class _QtFIFOReader(QObject): """A FIFO reader based on a QSocketNotifier. Attributes: _filepath: The path to the opened FIFO. _fifo: The Python file object for the FIFO. _notifier: The QSocketNotifier used. Signals: got_line: Emitted when a whole line arrived. """ got_line = pyqtSignal(str) def __init__(self, filepath, parent=None): super().__init__(parent) self._filepath = filepath # We open as R/W so we never get EOF and have to reopen the pipe. # See http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ # We also use os.open and os.fdopen rather than built-in open so we # can add O_NONBLOCK. # pylint: disable=no-member,useless-suppression fd = os.open(filepath, os.O_RDWR | os.O_NONBLOCK) # pylint: enable=no-member,useless-suppression self._fifo = os.fdopen(fd, 'r') self._notifier = QSocketNotifier(typing.cast(sip.voidptr, fd), QSocketNotifier.Read, self) self._notifier.activated.connect( # type: ignore[attr-defined] self.read_line) @pyqtSlot() def read_line(self): """(Try to) read a line from the FIFO.""" log.procs.debug("QSocketNotifier triggered!") try: self._notifier.setEnabled(False) try: for line in self._fifo: self.got_line.emit(line.rstrip('\r\n')) self._notifier.setEnabled(True) except UnicodeDecodeError as e: log.misc.error("Invalid unicode in userscript output: {}" .format(e)) except RuntimeError as e: # For unknown reasons, read_line can still get called after the # QSocketNotifier was already deleted... log.procs.debug("While reading userscript output: {}".format(e)) def cleanup(self): """Clean up so the FIFO can be closed.""" self._notifier.setEnabled(False) for line in self._fifo: self.got_line.emit(line.rstrip('\r\n')) self._fifo.close()
class _QtFIFOReader(QObject): """A FIFO reader based on a QSocketNotifier. Attributes: _filepath: The path to the opened FIFO. _fifo: The Python file object for the FIFO. _notifier: The QSocketNotifier used. Signals: got_line: Emitted when a whole line arrived. """ got_line = pyqtSignal(str) def __init__(self, filepath, parent=None): super().__init__(parent) self._filepath = filepath # We open as R/W so we never get EOF and have to reopen the pipe. # See http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ # We also use os.open and os.fdopen rather than built-in open so we # can add O_NONBLOCK. # pylint: disable=no-member,useless-suppression fd = os.open(filepath, os.O_RDWR | os.O_NONBLOCK) # pylint: enable=no-member,useless-suppression self._fifo = os.fdopen(fd, 'r') self._notifier = QSocketNotifier(fd, QSocketNotifier.Read, self) self._notifier.activated.connect(self.read_line) @pyqtSlot() def read_line(self): """(Try to) read a line from the FIFO.""" log.procs.debug("QSocketNotifier triggered!") try: self._notifier.setEnabled(False) try: for line in self._fifo: self.got_line.emit(line.rstrip('\r\n')) self._notifier.setEnabled(True) except UnicodeDecodeError as e: log.misc.error("Invalid unicode in userscript output: {}" .format(e)) except RuntimeError as e: # For unknown reasons, read_line can still get called after the # QSocketNotifier was already deleted... log.procs.debug("While reading userscript output: {}".format(e)) def cleanup(self): """Clean up so the FIFO can be closed.""" self._notifier.setEnabled(False) for line in self._fifo: self.got_line.emit(line.rstrip('\r\n')) self._fifo.close()
class ClientSocket(QObject): """ A class wrapping a Python socket and integrated into the Qt event loop. """ def __init__(self, logger, parent=None): """ Initializes the client socket. :param logger: the logger to user :param parent: the parent object """ QObject.__init__(self, parent) self._logger = logger self._socket = None self._read_buffer = b'' self._read_notifier = None self._write_buffer = b'' self._write_notifier = None self._connected = False self._outgoing = collections.deque() self._incoming = collections.deque() self._container = None @staticmethod def _chunkify(bs, n=65535): """ Creates chunks of a specified size from a bytes string. :param bs: the bytes :param n: the size of a chunk :return: generator of chunks """ for i in range(0, len(bs), n): yield bs[i:i + n] @property def connected(self): """ Returns if the socket is connected. :return: is connected? """ return self._connected def connect(self, sock): """ Wraps the socket with the current object. :param sock: the socket """ self._read_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._notify_read) self._read_notifier.setEnabled(True) self._write_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Write, self) self._write_notifier.activated.connect(self._notify_write) self._write_notifier.setEnabled(False) self._socket = sock self._connected = True def disconnect(self, err=None): """ Terminates the current connection. :param err: the reason or None """ if not self._socket: return if err: self._logger.warning("Connection lost") self._logger.exception(err) self._read_notifier.setEnabled(False) self._write_notifier.setEnabled(False) try: self._socket.close() except socket.error: pass self._socket = None self._connected = False def _notify_read(self): """ Callback called when some data is ready to be read on the socket. """ while True: try: data = self._socket.recv(4096) except socket.error as e: if e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK): self.disconnect(e) break if not data: break self._incoming.append(data) if self._incoming: QCoreApplication.instance().postEvent(self, PacketEvent()) def _notify_write(self): """ Callback called when some data is ready to written on the socket. """ while True: if not self._write_buffer: if not self._outgoing: break data = self._outgoing.popleft() if not data: continue self._write_buffer = data try: count = self._socket.send(self._write_buffer) except socket.error as e: if e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK): self.disconnect(e) break self._write_buffer = self._write_buffer[count:] if not self._write_buffer: self._write_notifier.setEnabled(False) def event(self, event): """ Callback called when a Qt event is fired. :param event: the event :return: was the event handled? """ if isinstance(event, PacketEvent): self._dispatch() event.accept() return True else: event.ignore() return False def _dispatch(self): """ Callback called when a packet event is fired. """ while self._incoming: data = self._incoming.popleft() self._read_raw(data) def _read_raw(self, data): """ Reads some raw from the underlying socket. :param data: the raw bytes """ self._read_buffer += data while b'\n' in self._read_buffer and not self._container: lines = self._read_buffer.split(b'\n') self._read_buffer = b'\n'.join(lines[1:]) self._read_line(lines[0]) if self._container: # Append raw data to content already received if self._container.downback: # trigger download callback self._container.downback(len(self._read_buffer), len(self._container)) if len(self._read_buffer) >= len(self._container): content = self._read_buffer[:len(self._container)] self._read_buffer = self._read_buffer[len(content):] self._container.content = content self._handle_packet(self._container) self._container = None def _write_raw(self, data): """ Writes some raw bytes to the underlying socket. :param data: the raw bytes """ if not self._socket: return self._outgoing.append(data) if not self._write_notifier.isEnabled(): self._write_notifier.setEnabled(True) def _read_line(self, line): """ Reads a line from the underlying socket. :param line: the line """ # Try to parse the line as a packet try: dct = json.loads(line.decode('utf-8')) packet = Packet.parse_packet(dct) except Exception as e: self._logger.warning("Invalid packet received: %s" % line) self._logger.exception(e) return # Wait for raw data if it is a container if isinstance(packet, Container): self._container = packet return # do not go any further self._handle_packet(packet) def _write_line(self, line): """ Writes a line to the underlying socket. :param line: the line """ self._write_raw(line.encode('utf-8') + b'\n') def _handle_packet(self, packet): """ Handle an incoming packet (used for replies). :param packet: the packet """ self._logger.debug("Received packet: %s" % packet) # Notify for replies if isinstance(packet, Reply): packet.trigger_callback() # Otherwise forward to the subclass elif not self.recv_packet(packet): self._logger.warning("Unhandled packet received: %s" % packet) def send_packet(self, packet): """ Sends a packet the other party. :param packet: the packet :return: a packet deferred if a reply is expected """ if not self._connected: self._logger.warning("Sending packet while disconnected") return None # Try to build then sent the line try: line = json.dumps(packet.build_packet()) self._write_line(line) except Exception as e: self._logger.warning("Invalid packet being sent: %s" % packet) self._logger.exception(e) self._logger.debug("Sending packet: %s" % packet) # Write raw data for containers if isinstance(packet, Container): data = packet.content count, total = 0, len(data) for chunk in self._chunkify(data): self._write_raw(chunk) count += len(chunk) if packet.upback: # trigger upload callback packet.upback(count, total) # Queries return a packet deferred if isinstance(packet, Query): d = PacketDeferred() packet.register_callback(d) return d return None def recv_packet(self, packet): """ Receives a packet from the other party. :param packet: the packet :return: has the packet been handled? """ raise NotImplementedError("recv_packet() not implemented")
class ServerSocket(QObject): """ A class wrapping a server socket and integrated into the Qt event loop. """ def __init__(self, logger, parent=None): """ Initialize the server socket. :param logger: the logger to use :param parent: the parent object """ QObject.__init__(self, parent) self._logger = logger self._socket = None self._connected = False self._accept_notifier = None @property def connected(self): """ Returns if the socket is connected. :return: is connected? """ return self._connected def connect(self, sock): """ Wraps the socket with the current object. :param sock: the socket """ sock.settimeout(0) self._accept_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Read, self) self._accept_notifier.activated.connect(self._notify_accept) self._accept_notifier.setEnabled(True) self._socket = sock self._connected = True def disconnect(self, err=None): """ Terminates the current connection. :param err: the reason or None """ if not self._socket: return if err: self._logger.warning("Connection lost") self._logger.exception(err) self._accept_notifier.setEnabled(False) try: self._socket.close() except socket.error: pass self._socket = None self._connected = False def _notify_accept(self): """ Callback called when a client is connecting. """ while True: try: sock, address = self._socket.accept() except socket.error as e: if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK): break self.disconnect(e) break sock.setblocking(False) self._accept(sock) def _accept(self, socket): """ Handles the client who newly connected. :param socket: the socket """ raise NotImplementedError('accept() is not implemented')
class ApplicationWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() # zmq self._zmq_context = zmq.Context() self._zmq_sock = self._zmq_context.socket(zmq.SUB) self._zmq_sock.connect("tcp://localhost:5555") self._zmq_sock.setsockopt(zmq.SUBSCRIBE, b'weights') self._zmq_sock.setsockopt(zmq.SUBSCRIBE, b'biases') #self._zmq_sock.setsockopt(zmq.SUBSCRIBE, b"bm_chat") self.read_noti = QSocketNotifier(self._zmq_sock.getsockopt(zmq.FD), QSocketNotifier.Read, self) self.read_noti.activated.connect(self.on_read_msg) self._zmq_sock2 = self._zmq_context.socket(zmq.SUB) self._zmq_sock2.connect("tcp://localhost:5557") self._zmq_sock2.setsockopt(zmq.SUBSCRIBE, b'progress') self.read_noti2 = QSocketNotifier(self._zmq_sock2.getsockopt(zmq.FD), QSocketNotifier.Read, self) self.read_noti2.activated.connect(self.on_read_msg2) self._main = QtWidgets.QWidget() self.setCentralWidget(self._main) vlayout = QtWidgets.QVBoxLayout(self._main) vlayout.setObjectName("verticalLayout") self.layout = QtWidgets.QHBoxLayout() self.layout.setObjectName("horizontalLayoutWithHeatMaps") self.heat_map_canvas = [] #self.addToolBar(NavigationToolbar(static_canvas, self)) #self.layout.addWidget(cost_canvas) #self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(cost_canvas, self)) layout2 = QtWidgets.QHBoxLayout() layout2.setObjectName("horizontalLayoutWithErrorGraphAndControls") layout3 = QtWidgets.QHBoxLayout() layout3.setObjectName("horizontalLayoutWithErrorGraph") #static_canvas = FigureCanvas(Figure(figsize=(5, 3))) cost_canvas = FigureCanvas(Figure(figsize=(5, 3))) eval_canvas = FigureCanvas(Figure(figsize=(5, 3))) layout3.addWidget(cost_canvas) layout3.addWidget(eval_canvas) self.learninRate = QDoubleSpinBox() self.learninRate.setValue(0.05) self.learninRate.setSingleStep(0.005) self.learninRate.setDecimals(3) self.l1 = QDoubleSpinBox() self.l1.setValue(0.05) self.l1.setSingleStep(0.005) self.l1.setDecimals(3) self.l2 = QDoubleSpinBox() self.l2.setValue(0.05) self.l2.setSingleStep(0.005) self.l2.setDecimals(3) self.sendButton = QPushButton() self.sendButton.setText("Send") self.sendButton.clicked.connect(self.sendHiperParameters) layout4 = QtWidgets.QVBoxLayout() layout4.setObjectName("vertiacalWithControls") layout4.addWidget(QLabel("Learning Rate")) layout4.addWidget(self.learninRate) layout4.addWidget(QLabel("L1")) layout4.addWidget(self.l1) layout4.addWidget(QLabel("L2")) layout4.addWidget(self.l2) layout4.addWidget(self.sendButton) layout2.addLayout(layout3) layout2.addLayout(layout4) vlayout.addLayout(self.layout) vlayout.addLayout(layout2) # seaborn !!! # sns.swarmplot(x="species", y="petal_length", data=iris, ax=self._seaborn_heatmap_ax) # seaborn !!! # self._static_ax = static_canvas.figure.subplots() # t = np.linspace(0, 10, 501) # self._static_ax.plot(t, np.tan(t), ".") self._cost_ax = cost_canvas.figure.subplots() self._eval_ax = eval_canvas.figure.subplots() # self._timer = cost_canvas.new_timer( # 100, [(self._update_canvas, (), {})]) # self._timer.start() self.cost = np.array([]) self.eval = np.array([]) QtCore.QTimer.singleShot(1000, self.OnLoad) def sendHiperParameters(self): print(self.learninRate.value(), self.l1.value(), self.l2.value()) def OnLoad(self): startRunCar() def updateProgress(self, cost, eval): self.cost = np.append(self.cost, [cost]) self.eval = np.append(self.eval, [eval]) self._cost_ax.clear() t = np.linspace(0, 10, 101) # Shift the sinusoid as a function of time. self._cost_ax.plot(self.cost) self._cost_ax.figure.canvas.draw() self._eval_ax.clear() t = np.linspace(0, 10, 101) # Shift the sinusoid as a function of time. self._eval_ax.plot(self.eval) self._eval_ax.figure.canvas.draw() def update_heatmaps(self, weights, biases): if (len(self.heat_map_canvas) == 0): self._seaborn_heatmap_ax = [] for i in range(len(weights) + 1): figsize = (5, 3) if i < len(weights) else (5, 1) self.heat_map_canvas.append( FigureCanvas(Figure(figsize=figsize))) self.layout.addWidget(self.heat_map_canvas[-1]) self._seaborn_heatmap_ax.append( self.heat_map_canvas[-1].figure.subplots()) print('update weights') weights = np.abs(weights) vmin = 0 vmax = max([np.amax(x) for x in weights]) for (w, i) in zip(weights, range(len(weights))): a, b = w.shape self._seaborn_heatmap_ax[i].clear() if (i < len(weights) - 1): sns.heatmap(w, ax=self._seaborn_heatmap_ax[i], vmin=vmin, vmax=vmax, cbar=False) else: # Обновление еще и colormap self._seaborn_heatmap_ax[-1].clear() sns.heatmap(w, ax=self._seaborn_heatmap_ax[i], cbar_ax=self._seaborn_heatmap_ax[-1], vmin=vmin, vmax=vmax, cbar=True) self._seaborn_heatmap_ax[-1].figure.canvas.draw() self._seaborn_heatmap_ax[i].figure.canvas.draw() def on_read_msg(self): self.read_noti.setEnabled(False) if self._zmq_sock.getsockopt(zmq.EVENTS) & zmq.POLLIN: while self._zmq_sock.getsockopt(zmq.EVENTS) & zmq.POLLIN: topic = self._zmq_sock.recv_string() data = self._zmq_sock.recv_pyobj() print(topic) weights, biases = data self.update_heatmaps(weights, biases) elif self._zmq_sock.getsockopt(zmq.EVENTS) & zmq.POLLOUT: print("[Socket] zmq.POLLOUT") elif self._zmq_sock.getsockopt(zmq.EVENTS) & zmq.POLLERR: print("[Socket] zmq.POLLERR") self.read_noti.setEnabled(True) def on_read_msg2(self): self.read_noti2.setEnabled(False) if self._zmq_sock2.getsockopt(zmq.EVENTS) & zmq.POLLIN: while self._zmq_sock2.getsockopt(zmq.EVENTS) & zmq.POLLIN: topic = self._zmq_sock2.recv_string() data = self._zmq_sock2.recv_pyobj() print(topic) cost, eval = data self.updateProgress(cost, eval) elif self._zmq_sock2.getsockopt(zmq.EVENTS) & zmq.POLLOUT: print("[Socket] zmq.POLLOUT") elif self._zmq_sock2.getsockopt(zmq.EVENTS) & zmq.POLLERR: print("[Socket] zmq.POLLERR") self.read_noti2.setEnabled(True)
class ClientsDiscovery(QObject): """ This class is used by the server to discover client on the local network. It uses an UDP socket broadcasting the server hostname and port on the port 31013. A client will reply back with a simple message. """ def __init__(self, logger, parent=None): super(ClientsDiscovery, self).__init__(parent) self._logger = logger self._info = None self._socket = None self._read_notifier = None self._started = False # Timer signaling that it's time to broadcast self._timer = QTimer() self._timer.setInterval(10000) self._timer.timeout.connect(self._send_request) def start(self, host, port, ssl): """Start the discovery process and broadcast the given information.""" self._logger.debug("Starting clients discovery") self._info = "%s %d %s" % (host, port, ssl) # Create a datagram socket capable of broadcasting self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self._socket.settimeout(0) # No timeout self._socket.setblocking(0) # No blocking self._read_notifier = QSocketNotifier(self._socket.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._notify_read) self._read_notifier.setEnabled(True) self._started = True self._timer.start() self._send_request() def stop(self): """Stop the discovery process.""" self._logger.debug("Stopping clients discovery") self._read_notifier.setEnabled(False) if self._socket: try: self._socket.close() except socket.error: pass self._socket = None self._started = False self._timer.stop() def _send_request(self): """This function sends to discovery request packets.""" # This is very verbose as it triggers every 1 sec so only enable when debugging #self._logger.trace("Sending discovery request") request = DISCOVERY_REQUEST + " " + self._info request = request.encode("utf-8") while len(request): try: self._socket.setblocking(0) sent = self._socket.sendto(request, 0, ("<broadcast>", 31013)) request = request[sent:] except socket.error as e: self._logger.warning( "Couldn't send discovery request: {}".format(e)) # Force return, otherwise the while loop will halt IDA # This is a temporary fix, and it's gonna yield the above # warning every every n seconds.. return def _notify_read(self): """This function is called when a discovery reply is received.""" response, address = self._socket.recvfrom(4096) response = response.decode("utf-8") if response == DISCOVERY_REPLY: self._logger.trace("Received discovery reply from %s:%d" % address)
class ServersDiscovery(QObject): """ This class is used by the client to discover servers on the local network. It uses an UDP socket listening on port 31013 to received the request broadcasted by the server. Discovery server will be shown in the UI. """ def __init__(self, logger, parent=None): super(ServersDiscovery, self).__init__(parent) self._logger = logger self._servers = [] self._socket = None self._read_notifier = None self._started = False @property def servers(self): return self._servers def start(self): """Start the discovery process and listen for discovery requests.""" self._logger.debug("Starting servers discovery") # Create a datagram socket bound on port 31013 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if platform.system() == "Darwin": self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) self._socket.bind(("", 31013)) self._socket.settimeout(0) self._socket.setblocking(0) self._read_notifier = QSocketNotifier(self._socket.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._notify_read) self._read_notifier.setEnabled(True) self._started = True def stop(self): """Stop the discovery process.""" self._logger.debug("Stopping servers discovery") self._read_notifier.setEnabled(False) try: self._socket.close() except socket.errno: pass self._socket = None self._started = False def _notify_read(self): """This function is called when a discovery request is received.""" request, address = self._socket.recvfrom(4096) request = request.decode("utf-8") if request.startswith(DISCOVERY_REQUEST): self._logger.trace("Received discovery request from %s:%d" % address) # Get the server information _, host, port, ssl = request.split() server = {"host": host, "port": int(port), "no_ssl": ssl != "True"} # Remove the old value self._servers = [(s, t) for (s, t) in self._servers if s != server] # Append the new value self._servers.append((server, time.time())) self._logger.trace("Sending discovery reply to %s:%d" % address) # Reply to the discovery request reply = DISCOVERY_REPLY reply = reply.encode("utf-8") try: self._socket.sendto(reply, address) except socket.error: self._logger.warning("Couldn't send discovery reply")
class Application(QApplication): """Main application instance. Attributes: _args: ArgumentParser instance. _shutting_down: True if we're currently shutting down. _quit_status: The current quitting status. _crashdlg: The crash dialog currently open. _crashlogfile: A file handler to the fatal crash logfile. _event_filter: The EventFilter for the application. _signal_notifier: A QSocketNotifier used for signals on Unix. _signal_timer: A QTimer used to poll for signals on Windows. geometry: The geometry of the last closed main window. """ def __init__(self, args): """Constructor. Args: Argument namespace from argparse. """ # pylint: disable=too-many-statements self._quit_status = { 'crash': True, 'tabs': False, 'main': False, } self.geometry = None self._shutting_down = False self._crashdlg = None self._crashlogfile = None qt_args = qtutils.get_args(args) log.init.debug("Qt arguments: {}, based on {}".format(qt_args, args)) super().__init__(qt_args) sys.excepthook = self._exception_hook self._args = args objreg.register('args', args) objreg.register('app', self) if self._args.version: print(version.version()) print() print() print(qutebrowser.__copyright__) print() print(version.GPL_BOILERPLATE.strip()) sys.exit(0) try: sent = ipc.send_to_running_instance(self._args.command) if sent: sys.exit(0) log.init.debug("Starting IPC server...") ipc.init() except ipc.IPCError as e: text = ('{}\n\nMaybe another instance is running but ' 'frozen?'.format(e)) msgbox = QMessageBox(QMessageBox.Critical, "Error while " "connecting to running instance!", text) msgbox.exec_() # We didn't really initialize much so far, so we just quit hard. sys.exit(1) log.init.debug("Starting init...") self.setQuitOnLastWindowClosed(False) self.setOrganizationName("qutebrowser") self.setApplicationName("qutebrowser") self.setApplicationVersion(qutebrowser.__version__) self._init_icon() utils.actute_warning() try: self._init_modules() except (OSError, UnicodeDecodeError) as e: msgbox = QMessageBox( QMessageBox.Critical, "Error while initializing!", "Error while initializing: {}".format(e)) msgbox.exec_() sys.exit(1) QTimer.singleShot(0, self._process_args) log.init.debug("Initializing eventfilter...") self._event_filter = modeman.EventFilter(self) self.installEventFilter(self._event_filter) log.init.debug("Connecting signals...") self._connect_signals() log.init.debug("Setting up signal handlers...") self._setup_signals() QDesktopServices.setUrlHandler('http', self.open_desktopservices_url) QDesktopServices.setUrlHandler('https', self.open_desktopservices_url) QDesktopServices.setUrlHandler('qute', self.open_desktopservices_url) log.init.debug("Init done!") if self._crashdlg is not None: self._crashdlg.raise_() def __repr__(self): return utils.get_repr(self) def _init_modules(self): """Initialize all 'modules' which need to be initialized.""" # pylint: disable=too-many-statements log.init.debug("Initializing save manager...") save_manager = savemanager.SaveManager(self) objreg.register('save-manager', save_manager) save_manager.add_saveable('window-geometry', self._save_geometry) save_manager.add_saveable('version', self._save_version) log.init.debug("Initializing network...") networkmanager.init() log.init.debug("Initializing readline-bridge...") readline_bridge = readline.ReadlineBridge() objreg.register('readline-bridge', readline_bridge) log.init.debug("Initializing directories...") standarddir.init(self._args) log.init.debug("Initializing config...") config.init() save_manager.init_autosave() log.init.debug("Initializing web history...") history.init() log.init.debug("Initializing crashlog...") self._handle_segfault() log.init.debug("Initializing sessions...") session_manager = sessions.SessionManager(self) objreg.register('session-manager', session_manager) log.init.debug("Initializing js-bridge...") js_bridge = qutescheme.JSBridge(self) objreg.register('js-bridge', js_bridge) log.init.debug("Initializing websettings...") websettings.init() log.init.debug("Initializing adblock...") host_blocker = adblock.HostBlocker() host_blocker.read_hosts() objreg.register('host-blocker', host_blocker) log.init.debug("Initializing quickmarks...") quickmark_manager = quickmarks.QuickmarkManager() objreg.register('quickmark-manager', quickmark_manager) log.init.debug("Initializing proxy...") proxy.init() log.init.debug("Initializing cookies...") cookie_jar = cookies.CookieJar(self) objreg.register('cookie-jar', cookie_jar) log.init.debug("Initializing cache...") diskcache = cache.DiskCache(self) objreg.register('cache', diskcache) log.init.debug("Initializing completions...") completionmodels.init() def _init_icon(self): """Initialize the icon of qutebrowser.""" icon = QIcon() for size in (16, 24, 32, 48, 64, 96, 128, 256, 512): filename = ':/icons/qutebrowser-{}x{}.png'.format(size, size) pixmap = QPixmap(filename) qtutils.ensure_not_null(pixmap) icon.addPixmap(pixmap) qtutils.ensure_not_null(icon) self.setWindowIcon(icon) def _handle_segfault(self): """Handle a segfault from a previous run.""" logname = os.path.join(standarddir.data(), 'crash.log') try: # First check if an old logfile exists. if os.path.exists(logname): with open(logname, 'r', encoding='ascii') as f: data = f.read() os.remove(logname) self._init_crashlogfile() if data: # Crashlog exists and has data in it, so something crashed # previously. self._crashdlg = crashdialog.get_fatal_crash_dialog( self._args.debug, data) self._crashdlg.show() else: # There's no log file, so we can use this to display crashes to # the user on the next start. self._init_crashlogfile() except OSError: log.init.exception("Error while handling crash log file!") self._init_crashlogfile() def _init_crashlogfile(self): """Start a new logfile and redirect faulthandler to it.""" logname = os.path.join(standarddir.data(), 'crash.log') try: self._crashlogfile = open(logname, 'w', encoding='ascii') except OSError: log.init.exception("Error while opening crash log file!") else: earlyinit.init_faulthandler(self._crashlogfile) def _process_args(self): """Open startpage etc. and process commandline args.""" config_obj = objreg.get('config') for sect, opt, val in self._args.temp_settings: try: config_obj.set('temp', sect, opt, val) except (configexc.Error, configparser.Error) as e: message.error('current', "set: {} - {}".format( e.__class__.__name__, e)) if not self._args.override_restore: self._load_session(self._args.session) session_manager = objreg.get('session-manager') if not session_manager.did_load: log.init.debug("Initializing main window...") window = mainwindow.MainWindow() if not self._args.nowindow: window.show() self.setActiveWindow(window) self.process_pos_args(self._args.command) self._open_startpage() self._open_quickstart() def _load_session(self, name): """Load the default session. Args: name: The name of the session to load, or None to read state file. """ state_config = objreg.get('state-config') if name is None: try: name = state_config['general']['session'] except KeyError: # No session given as argument and none in the session file -> # start without loading a session return session_manager = objreg.get('session-manager') try: session_manager.load(name) except sessions.SessionNotFoundError: message.error('current', "Session {} not found!".format(name)) except sessions.SessionError as e: message.error('current', "Failed to load session {}: {}".format( name, e)) try: del state_config['general']['session'] except KeyError: pass # If this was a _restart session, delete it. if name == '_restart': session_manager.delete('_restart') def _get_window(self, via_ipc, force_window=False, force_tab=False): """Helper function for process_pos_args to get a window id. Args: via_ipc: Whether the request was made via IPC. force_window: Whether to force opening in a window. force_tab: Whether to force opening in a tab. """ if force_window and force_tab: raise ValueError("force_window and force_tab are mutually " "exclusive!") if not via_ipc: # Initial main window return 0 window_to_raise = None open_target = config.get('general', 'new-instance-open-target') if (open_target == 'window' or force_window) and not force_tab: window = mainwindow.MainWindow() window.show() win_id = window.win_id window_to_raise = window else: try: window = objreg.last_window() except objreg.NoWindow: # There is no window left, so we open a new one window = mainwindow.MainWindow() window.show() win_id = window.win_id window_to_raise = window win_id = window.win_id if open_target != 'tab-silent': window_to_raise = window if window_to_raise is not None: window_to_raise.setWindowState(window.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) window_to_raise.raise_() window_to_raise.activateWindow() self.alert(window_to_raise) return win_id def process_pos_args(self, args, via_ipc=False, cwd=None): """Process positional commandline args. URLs to open have no prefix, commands to execute begin with a colon. Args: args: A list of arguments to process. via_ipc: Whether the arguments were transmitted over IPC. cwd: The cwd to use for fuzzy_url. """ if via_ipc and not args: win_id = self._get_window(via_ipc, force_window=True) self._open_startpage(win_id) return win_id = None for cmd in args: if cmd.startswith(':'): if win_id is None: win_id = self._get_window(via_ipc, force_tab=True) log.init.debug("Startup cmd {}".format(cmd)) commandrunner = runners.CommandRunner(win_id) commandrunner.run_safely_init(cmd[1:]) elif not cmd: log.init.debug("Empty argument") win_id = self._get_window(via_ipc, force_window=True) else: win_id = self._get_window(via_ipc) tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) log.init.debug("Startup URL {}".format(cmd)) try: url = urlutils.fuzzy_url(cmd, cwd, relative=True) except urlutils.FuzzyUrlError as e: message.error(0, "Error in startup argument '{}': " "{}".format(cmd, e)) else: tabbed_browser.tabopen(url, background=False) def _open_startpage(self, win_id=None): """Open startpage. The startpage is never opened if the given windows are not empty. Args: win_id: If None, open startpage in all empty windows. If set, open the startpage in the given window. """ if win_id is not None: window_ids = [win_id] else: window_ids = objreg.window_registry for cur_win_id in window_ids: tabbed_browser = objreg.get('tabbed-browser', scope='window', window=cur_win_id) if tabbed_browser.count() == 0: log.init.debug("Opening startpage") for urlstr in config.get('general', 'startpage'): try: url = urlutils.fuzzy_url(urlstr, do_search=False) except urlutils.FuzzyUrlError as e: message.error(0, "Error when opening startpage: " "{}".format(e)) tabbed_browser.tabopen(QUrl('about:blank')) else: tabbed_browser.tabopen(url) def _open_quickstart(self): """Open quickstart if it's the first start.""" state_config = objreg.get('state-config') try: quickstart_done = state_config['general']['quickstart-done'] == '1' except KeyError: quickstart_done = False if not quickstart_done: tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') tabbed_browser.tabopen( QUrl('http://www.qutebrowser.org/quickstart.html')) state_config['general']['quickstart-done'] = '1' def _setup_signals(self): """Set up signal handlers. On Windows this uses a QTimer to periodically hand control over to Python so it can handle signals. On Unix, it uses a QSocketNotifier with os.set_wakeup_fd to get notified. """ signal.signal(signal.SIGINT, self.interrupt) signal.signal(signal.SIGTERM, self.interrupt) if os.name == 'posix' and hasattr(signal, 'set_wakeup_fd'): import fcntl read_fd, write_fd = os.pipe() for fd in (read_fd, write_fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) self._signal_notifier = QSocketNotifier( read_fd, QSocketNotifier.Read, self) self._signal_notifier.activated.connect(self._handle_signal_wakeup) signal.set_wakeup_fd(write_fd) else: self._signal_timer = usertypes.Timer(self, 'python_hacks') self._signal_timer.start(1000) self._signal_timer.timeout.connect(lambda: None) @pyqtSlot() def _handle_signal_wakeup(self): """Handle a newly arrived signal. This gets called via self._signal_notifier when there's a signal. Python will get control here, so the signal will get handled. """ log.destroy.debug("Handling signal wakeup!") self._signal_notifier.setEnabled(False) read_fd = self._signal_notifier.socket() try: os.read(read_fd, 1) except OSError: log.destroy.exception("Failed to read wakeup fd.") self._signal_notifier.setEnabled(True) def _connect_signals(self): """Connect all signals to their slots.""" config_obj = objreg.get('config') self.lastWindowClosed.connect(self.on_last_window_closed) config_obj.style_changed.connect(style.get_stylesheet.cache_clear) self.focusChanged.connect(self.on_focus_changed) self.focusChanged.connect(message.on_focus_changed) def _get_widgets(self): """Get a string list of all widgets.""" widgets = self.allWidgets() widgets.sort(key=repr) return [repr(w) for w in widgets] def _get_pyqt_objects(self, lines, obj, depth=0): """Recursive method for get_all_objects to get Qt objects.""" for kid in obj.findChildren(QObject): lines.append(' ' * depth + repr(kid)) self._get_pyqt_objects(lines, kid, depth + 1) def get_all_objects(self): """Get all children of an object recursively as a string.""" output = [''] widget_lines = self._get_widgets() widget_lines = [' ' + e for e in widget_lines] widget_lines.insert(0, "Qt widgets - {} objects".format( len(widget_lines))) output += widget_lines pyqt_lines = [] self._get_pyqt_objects(pyqt_lines, self) pyqt_lines = [' ' + e for e in pyqt_lines] pyqt_lines.insert(0, 'Qt objects - {} objects:'.format( len(pyqt_lines))) output += pyqt_lines output += [''] output += objreg.dump_objects() return '\n'.join(output) def _recover_pages(self, forgiving=False): """Try to recover all open pages. Called from _exception_hook, so as forgiving as possible. Args: forgiving: Whether to ignore exceptions. Return: A list containing a list for each window, which in turn contain the opened URLs. """ pages = [] for win_id in objreg.window_registry: win_pages = [] tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) for tab in tabbed_browser.widgets(): try: urlstr = tab.cur_url.toString( QUrl.RemovePassword | QUrl.FullyEncoded) if urlstr: win_pages.append(urlstr) except Exception: if forgiving: log.destroy.exception("Error while recovering tab") else: raise pages.append(win_pages) return pages def _save_geometry(self): """Save the window geometry to the state config.""" if self.geometry is not None: state_config = objreg.get('state-config') geom = base64.b64encode(self.geometry).decode('ASCII') state_config['geometry']['mainwindow'] = geom def _save_version(self): """Save the current version to the state config.""" state_config = objreg.get('state-config') state_config['general']['version'] = qutebrowser.__version__ def _destroy_crashlogfile(self): """Clean up the crash log file and delete it.""" if self._crashlogfile is None: return # We use sys.__stderr__ instead of sys.stderr here so this will still # work when sys.stderr got replaced, e.g. by "Python Tools for Visual # Studio". if sys.__stderr__ is not None: faulthandler.enable(sys.__stderr__) else: faulthandler.disable() try: self._crashlogfile.close() os.remove(self._crashlogfile.name) except OSError: log.destroy.exception("Could not remove crash log!") def _exception_hook(self, exctype, excvalue, tb): # noqa """Handle uncaught python exceptions. It'll try very hard to write all open tabs to a file, and then exit gracefully. """ exc = (exctype, excvalue, tb) if not self._quit_status['crash']: log.misc.error("ARGH, there was an exception while the crash " "dialog is already shown:", exc_info=exc) return log.misc.error("Uncaught exception", exc_info=exc) is_ignored_exception = (exctype is bdb.BdbQuit or not issubclass(exctype, Exception)) if self._args.pdb_postmortem: pdb.post_mortem(tb) if (is_ignored_exception or self._args.no_crash_dialog or self._args.pdb_postmortem): # pdb exit, KeyboardInterrupt, ... status = 0 if is_ignored_exception else 2 try: self.shutdown(status) return except Exception: log.init.exception("Error while shutting down") self.quit() return self._quit_status['crash'] = False try: pages = self._recover_pages(forgiving=True) except Exception: log.destroy.exception("Error while recovering pages") pages = [] try: cmd_history = objreg.get('command-history')[-5:] except Exception: log.destroy.exception("Error while getting history: {}") cmd_history = [] try: objects = self.get_all_objects() except Exception: log.destroy.exception("Error while getting objects") objects = "" try: objreg.get('ipc-server').ignored = True except Exception: log.destroy.exception("Error while ignoring ipc") try: self.lastWindowClosed.disconnect(self.on_last_window_closed) except TypeError: log.destroy.exception("Error while preventing shutdown") QApplication.closeAllWindows() self._crashdlg = crashdialog.ExceptionCrashDialog( self._args.debug, pages, cmd_history, exc, objects) ret = self._crashdlg.exec_() if ret == QDialog.Accepted: # restore self._do_restart(pages) # We might risk a segfault here, but that's better than continuing to # run in some undefined state, so we only do the most needed shutdown # here. qInstallMessageHandler(None) self._destroy_crashlogfile() sys.exit(1) def _get_restart_args(self, pages=(), session=None): """Get the current working directory and args to relaunch qutebrowser. Args: pages: The pages to re-open. session: The session to load, or None. Return: An (args, cwd) tuple. args: The commandline as a list of strings. cwd: The current working directory as a string. """ if os.path.basename(sys.argv[0]) == 'qutebrowser': # Launched via launcher script args = [sys.argv[0]] cwd = None elif hasattr(sys, 'frozen'): args = [sys.executable] cwd = os.path.abspath(os.path.dirname(sys.executable)) else: args = [sys.executable, '-m', 'qutebrowser'] cwd = os.path.join(os.path.abspath(os.path.dirname( qutebrowser.__file__)), '..') if not os.path.isdir(cwd): # Probably running from an python egg. Let's fallback to # cwd=None and see if that works out. # See https://github.com/The-Compiler/qutebrowser/issues/323 cwd = None # Add all open pages so they get reopened. page_args = [] for win in pages: page_args.extend(win) page_args.append('') # Serialize the argparse namespace into json and pass that to the new # process via --json-args. # We do this as there's no way to "unparse" the namespace while # ignoring some arguments. argdict = vars(self._args) argdict['session'] = None argdict['url'] = [] argdict['command'] = page_args[:-1] argdict['json_args'] = None # Ensure the given session (or none at all) gets opened. if session is None: argdict['session'] = None argdict['override_restore'] = True else: argdict['session'] = session argdict['override_restore'] = False # Dump the data data = json.dumps(argdict) args += ['--json-args', data] log.destroy.debug("args: {}".format(args)) log.destroy.debug("cwd: {}".format(cwd)) return args, cwd @cmdutils.register(instance='app') def restart(self): """Restart qutebrowser while keeping existing tabs open.""" ok = self._do_restart(session='_restart') if ok: self.shutdown() def _do_restart(self, pages=(), session=None): """Inner logic to restart qutebrowser. The "better" way to restart is to pass a session (_restart usually) as that'll save the complete state. However we don't do that (and pass a list of pages instead) when we restart because of an exception, as that's a lot simpler and we don't want to risk anything going wrong. Args: pages: A list of URLs to open. session: The session to load, or None. Return: True if the restart succeeded, False otherwise. """ log.destroy.debug("sys.executable: {}".format(sys.executable)) log.destroy.debug("sys.path: {}".format(sys.path)) log.destroy.debug("sys.argv: {}".format(sys.argv)) log.destroy.debug("frozen: {}".format(hasattr(sys, 'frozen'))) # Save the session if one is given. if session is not None: session_manager = objreg.get('session-manager') session_manager.save(session) # Open a new process and immediately shutdown the existing one try: args, cwd = self._get_restart_args(pages, session) if cwd is None: subprocess.Popen(args) else: subprocess.Popen(args, cwd=cwd) except OSError: log.destroy.exception("Failed to restart") return False else: return True @cmdutils.register(instance='app', maxsplit=0, debug=True) def debug_pyeval(self, s): """Evaluate a python string and display the results as a web page. // We have this here rather in utils.debug so the context of eval makes more sense and because we don't want to import much stuff in the utils. Args: s: The string to evaluate. """ try: r = eval(s) out = repr(r) except Exception: out = traceback.format_exc() qutescheme.pyeval_output = out tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') tabbed_browser.openurl(QUrl('qute:pyeval'), newtab=True) @cmdutils.register(instance='app') def report(self): """Report a bug in qutebrowser.""" pages = self._recover_pages() cmd_history = objreg.get('command-history')[-5:] objects = self.get_all_objects() self._crashdlg = crashdialog.ReportDialog(pages, cmd_history, objects) self._crashdlg.show() def interrupt(self, signum, _frame): """Handler for signals to gracefully shutdown (SIGINT/SIGTERM). This calls self.shutdown and remaps the signal to call self.interrupt_forcefully the next time. """ log.destroy.info("SIGINT/SIGTERM received, shutting down!") log.destroy.info("Do the same again to forcefully quit.") signal.signal(signal.SIGINT, self.interrupt_forcefully) signal.signal(signal.SIGTERM, self.interrupt_forcefully) # If we call shutdown directly here, we get a segfault. QTimer.singleShot(0, functools.partial(self.shutdown, 128 + signum)) def interrupt_forcefully(self, signum, _frame): """Interrupt forcefully on the second SIGINT/SIGTERM request. This skips our shutdown routine and calls QApplication:exit instead. It then remaps the signals to call self.interrupt_really_forcefully the next time. """ log.destroy.info("Forceful quit requested, goodbye cruel world!") log.destroy.info("Do the same again to quit with even more force.") signal.signal(signal.SIGINT, self.interrupt_really_forcefully) signal.signal(signal.SIGTERM, self.interrupt_really_forcefully) # This *should* work without a QTimer, but because of the trouble in # self.interrupt we're better safe than sorry. QTimer.singleShot(0, functools.partial(self.exit, 128 + signum)) def interrupt_really_forcefully(self, signum, _frame): """Interrupt with even more force on the third SIGINT/SIGTERM request. This doesn't run *any* Qt cleanup and simply exits via Python. It will most likely lead to a segfault. """ log.destroy.info("WHY ARE YOU DOING THIS TO ME? :(") sys.exit(128 + signum) @cmdutils.register(instance='app', name='wq', completion=[usertypes.Completion.sessions]) def save_and_quit(self, name='default'): """Save open pages and quit. Args: name: The name of the session. """ self.shutdown(session=name) @pyqtSlot() def on_last_window_closed(self): """Slot which gets invoked when the last window was closed.""" self.shutdown(last_window=True) @cmdutils.register(instance='app', name=['quit', 'q'], ignore_args=True) def shutdown(self, status=0, session=None, last_window=False): """Quit qutebrowser. Args: status: The status code to exit with. session: A session name if saving should be forced. last_window: If the shutdown was triggered due to the last window closing. """ if self._shutting_down: return self._shutting_down = True log.destroy.debug("Shutting down with status {}, session {}..." .format(status, session)) session_manager = objreg.get('session-manager') if session is not None: session_manager.save(session, last_window=last_window, load_next_time=True) elif config.get('general', 'save-session'): session_manager.save('default', last_window=last_window, load_next_time=True) deferrer = False for win_id in objreg.window_registry: prompter = objreg.get('prompter', None, scope='window', window=win_id) if prompter is not None and prompter.shutdown(): deferrer = True if deferrer: # If shutdown was called while we were asking a question, we're in # a still sub-eventloop (which gets quit now) and not in the main # one. # This means we need to defer the real shutdown to when we're back # in the real main event loop, or we'll get a segfault. log.destroy.debug("Deferring real shutdown because question was " "active.") QTimer.singleShot(0, functools.partial(self._shutdown, status)) else: # If we have no questions to shut down, we are already in the real # event loop, so we can shut down immediately. self._shutdown(status) def _shutdown(self, status): # noqa """Second stage of shutdown.""" log.destroy.debug("Stage 2 of shutting down...") # Remove eventfilter try: log.destroy.debug("Removing eventfilter...") self.removeEventFilter(self._event_filter) except AttributeError: pass # Close all windows QApplication.closeAllWindows() # Shut down IPC try: objreg.get('ipc-server').shutdown() except KeyError: pass # Save everything try: save_manager = objreg.get('save-manager') except KeyError: log.destroy.debug("Save manager not initialized yet, so not " "saving anything.") else: for key in save_manager.saveables: try: save_manager.save(key, is_exit=True) except OSError as e: msgbox = QMessageBox( QMessageBox.Critical, "Error while saving!", "Error while saving {}: {}".format(key, e)) msgbox.exec_() # Re-enable faulthandler to stdout, then remove crash log log.destroy.debug("Deactiving crash log...") self._destroy_crashlogfile() # If we don't kill our custom handler here we might get segfaults log.destroy.debug("Deactiving message handler...") qInstallMessageHandler(None) # Now we can hopefully quit without segfaults log.destroy.debug("Deferring QApplication::exit...") # We use a singleshot timer to exit here to minimize the likelihood of # segfaults. QTimer.singleShot(0, functools.partial(self.exit, status)) def on_focus_changed(self, _old, new): """Register currently focused main window in the object registry.""" if new is None: window = None else: window = new.window() if window is None or not isinstance(window, mainwindow.MainWindow): try: objreg.delete('last-focused-main-window') except KeyError: pass else: objreg.register('last-focused-main-window', window, update=True) @pyqtSlot(QUrl) def open_desktopservices_url(self, url): """Handler to open an URL via QDesktopServices.""" win_id = self._get_window(via_ipc=True, force_window=False) tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) tabbed_browser.tabopen(url) def exit(self, status): """Extend QApplication::exit to log the event.""" log.destroy.debug("Now calling QApplication::exit.") if self._args.debug_exit: if hunter is None: print("Not logging late shutdown because hunter could not be " "imported!", file=sys.stderr) else: print("Now logging late shutdown.", file=sys.stderr) hunter.trace() super().exit(status)
class TwistedSocketNotifier(QObject): """Connection between an fd event and reader/writer callbacks.""" activated = pyqtSignal(int) def __init__(self, parent, reactor, watcher, socketType): QObject.__init__(self, parent) self.reactor = reactor self.watcher = watcher fd = watcher.fileno() self.notifier = QSocketNotifier(fd, socketType, parent) self.notifier.setEnabled(True) if socketType == QSocketNotifier.Read: self.fn = self.read else: self.fn = self.write self.notifier.activated.connect(self.fn) def shutdown(self): self.notifier.setEnabled(False) self.notifier.activated.disconnect(self.fn) self.fn = self.watcher = None self.notifier.deleteLater() self.deleteLater() def read(self, fd): if not self.watcher: return w = self.watcher # doRead can cause self.shutdown to be called so keep # a reference to self.watcher def _read(): # Don't call me again, until the data has been read self.notifier.setEnabled(False) why = None try: why = w.doRead() inRead = True except: inRead = False log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, inRead) elif self.watcher: self.notifier.setEnabled(True) # Re enable notification following sucessfull read self.reactor._iterate(fromqt=True) log.callWithLogger(w, _read) def write(self, sock): if not self.watcher: return w = self.watcher def _write(): why = None self.notifier.setEnabled(False) try: why = w.doWrite() except: log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, False) elif self.watcher: self.notifier.setEnabled(True) self.reactor._iterate(fromqt=True) log.callWithLogger(w, _write)
class TunableDashboard(Ui_BaseDashboard): """GUI dashboard with asynchronous updates via IPC or network. TunableDashboard inherits from Ui_BaseDashboard, the bare-bones GUI window described by the QT Designer file 'dashboard_gui.ui'. The base class defines the various display widgets, but doesn't provide any way to update their content after the window has launched. TunableDashboard improves on this by accepting the input parameter 'sock', a caller-provided socket over which the caller may send commands to update dashboard display fields. """ def setup(self, db_widget, sock, width=960, ffmly='Fantasque Sans Mono', fscale=1.0): """Initialization routine for GUI dashboard widget: Call the base-class initialization routine, then establish a callback to receive messages over the socket. """ self.db_widget, self.sock = db_widget, sock self.setupUi(db_widget, width=width, ffmly=ffmly, fscale=fscale) """ tabulate distinct fonts in widget for subsequent use in rescaling, etc. """ self.font_set = set() def find_fonts(w): if hasattr(w, 'font'): self.font_set.add(w.font()) for child in w.children(): find_fonts(child) find_fonts(self.db_widget) log('Found {} fonts:'.format(len(self.font_set))) for font in self.font_set: log(' {}'.format(font.toString())) """ arrange for read_input to be called whenever data is available on the socket""" self.sock.setblocking(False) self.notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Read, db_widget) self.notifier.setEnabled(True) self.notifier.activated.connect(self.read_input) def read_input(self, fd): """ Callback invoked when there is input to read from the socket.""" try: input = str(self.sock.recv(BUFSIZE), 'utf-8') if len(input) == 0: condition = 'received EOF on socket' elif self.process_input(input) == False: condition = "received 'terminate' command" else: return except: condition = get_exception_info(msg='socket error', warning=True) log('{}; closing dashboard'.format(condition)) self.sock.close() self.notifier.setEnabled(False) self.db_widget.close() def process_input(self, input): """ Process a batch of input received over the socket. Args: input (str): input received over the socket Returns: False: a 'terminate' command was received; the socket and window should be shut down. True: No 'terminate' commant received; carry on as before. """ for (ln, n) in [(l, l.find(' ')) for l in input.split('\n') if l]: key, val = (ln.lower(), '') if n < 0 else (ln[:n].lower(), ln[n + 1:]) if key == 'terminate': return False elif key == 'title': self.db_widget.setWindowTitle(val) elif key == 'width': self.rescale(width_fraction=float(val)) elif key == 'font_scale': for f in self.font_set: f.setPointSize(int(float(val) * f.pointSize())) elif key == 'clear': for key in DASHBOARD_ELEMENTS: self.update_item(key, '', redraw=False) elif key in DASHBOARD_ELEMENTS: self.update_item(key, val, redraw=False) else: log('ignoring unrecognized dashboard update: ' + ln) self.db_widget.update() return True def update_item(self, item, value, redraw=True): """ Set GUI dashboard element 'item' to value 'value.' """ msg, w = None, self.get_widget_by_name(item) if isinstance(w, QLCDNumber): w.display(value) elif isinstance(w, QLabel): w.setText(value) elif isinstance(w, QProgressBar): tok = value.split() if len(tok) == 1 and tok[0].isdigit(): w.setProperty('value', int(tok[0])) elif len(tok) == 3 and tok[0] == 'range' and tok[1].isdigit( ) and tok[2].isdigit(): w.setRange(int(tok[1]), int(tok[2])) w.setProperty('value', int(tok[1])) elif len(tok) > 1: msg = 'failed to process progress-bar update: {} {}'.format( item, value) else: msg = 'failed to process update for unknown dashboard element {}={}'.format( item, value) if msg: log('warning: {}'.format(msg)) elif redraw: self.db_widget.update() def get_widget_by_name(self, name): if name.lower() == 'progress': return self.progress for w in self.db_widget.children(): if w.objectName().strip(' :').lower() == name.lower(): return w return None
class ClientSocket(QObject): """ A class wrapping a Python socket and integrated into the Qt event loop. """ MAX_READ_SIZE = 4096 MAX_WRITE_SIZE = 65535 def __init__(self, logger, parent=None): """ Initializes the client socket. :param logger: the logger to user """ QObject.__init__(self, parent) self._logger = logger self._socket = None self._server = parent and isinstance(parent, ServerSocket) self._read_buffer = bytearray() self._read_notifier = None self._read_packet = None self._write_buffer = bytearray() self._write_notifier = None self._write_packet = None self._connected = False self._outgoing = collections.deque() self._incoming = collections.deque() @property def connected(self): """ Returns if the socket is connected. :return: is connected? """ return self._connected def connect(self, sock): """ Wraps the socket with the current object. :param sock: the socket """ self._read_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._notify_read) self._read_notifier.setEnabled(True) self._write_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Write, self) self._write_notifier.activated.connect(self._notify_write) self._write_notifier.setEnabled(False) self._socket = sock self._connected = True def disconnect(self, err=None): """ Terminates the current connection. :param err: the reason or None """ if not self._socket: return if err: self._logger.warning("Connection lost") self._logger.exception(err) self._read_notifier.setEnabled(False) self._write_notifier.setEnabled(False) try: self._socket.close() except socket.error: pass self._socket = None self._connected = False def set_keep_alive(self, cnt, intvl, idle): """ Set the TCP keep-alive of the underlying socket. It activates after `idle` seconds of idleness, then sends a keep-alive ping once every `intvl` seconds, and closes the connection after `cnt` failed ping. """ # Taken from https://github.com/markokr/skytools/ TCP_KEEPCNT = getattr(socket, 'TCP_KEEPCNT', None) TCP_KEEPINTVL = getattr(socket, 'TCP_KEEPINTVL', None) TCP_KEEPIDLE = getattr(socket, 'TCP_KEEPIDLE', None) TCP_KEEPALIVE = getattr(socket, 'TCP_KEEPALIVE', None) SIO_KEEPALIVE_VALS = getattr(socket, 'SIO_KEEPALIVE_VALS', None) if TCP_KEEPIDLE is None and TCP_KEEPALIVE is None \ and sys.platform == 'darwin': TCP_KEEPALIVE = 0x10 self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) if TCP_KEEPCNT is not None: self._socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPCNT, cnt) if TCP_KEEPINTVL is not None: self._socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPINTVL, intvl) if TCP_KEEPIDLE is not None: self._socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, idle) elif TCP_KEEPALIVE is not None: self._socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPALIVE, idle) elif SIO_KEEPALIVE_VALS is not None: self._socket.ioctl(SIO_KEEPALIVE_VALS, (1, idle * 1000, intvl * 1000)) def _notify_read(self): """ Callback called when some data is ready to be read on the socket. """ # Read as much data as is available while True: try: data = self._socket.recv(ClientSocket.MAX_READ_SIZE) if not data: self.disconnect() break except socket.error as e: if e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK) \ and not isinstance(e, ssl.SSLWantReadError) \ and not isinstance(e, ssl.SSLWantWriteError): self.disconnect(e) break # No more data available self._read_buffer.extend(data) while True: if self._read_packet is None: if b'\n' in self._read_buffer: pos = self._read_buffer.index(b'\n') line = self._read_buffer[:pos] self._read_buffer = self._read_buffer[pos + 1:] # Try to parse the line as a packet try: dct = json.loads(line.decode('utf-8')) self._read_packet = Packet.parse_packet( dct, self._server) except Exception as e: msg = "Invalid packet received: %s" % line self._logger.warning(msg) self._logger.exception(e) continue else: break # Not enough data for a packet else: if isinstance(self._read_packet, Container): avail = len(self._read_buffer) total = self._read_packet.size # Trigger the downback if self._read_packet.downback: self._read_packet.downback(min(avail, total), total) # Read the container's content if avail >= total: self._read_packet.content = self._read_buffer[:total] self._read_buffer = self._read_buffer[total:] else: break # Not enough data for a packet self._incoming.append(self._read_packet) self._read_packet = None if self._incoming: QCoreApplication.instance().postEvent(self, PacketEvent()) def _notify_write(self): """ Callback called when some data is ready to written on the socket. """ while True: if not self._write_buffer: if not self._outgoing: break # No more packets to send self._write_packet = self._outgoing.popleft() try: line = json.dumps(self._write_packet.build_packet()) line = line.encode('utf-8') + b'\n' except Exception as e: msg = "Invalid packet being sent: %s" % self._write_packet self._logger.warning(msg) self._logger.exception(e) continue # Write the container's content self._write_buffer.extend(bytearray(line)) if isinstance(self._write_packet, Container): data = self._write_packet.content self._write_buffer.extend(bytearray(data)) self._write_packet.size += len(line) # Send as many bytes as possible try: count = min(len(self._write_buffer), ClientSocket.MAX_WRITE_SIZE) sent = self._socket.send(self._write_buffer[:count]) self._write_buffer = self._write_buffer[sent:] except socket.error as e: if e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK) \ and not isinstance(e, ssl.SSLWantReadError) \ and not isinstance(e, ssl.SSLWantWriteError): self.disconnect(e) break # Can't write anything # Trigger the upback if isinstance(self._write_packet, Container) \ and self._write_packet.upback: self._write_packet.size -= count total = len(self._write_packet.content) sent = max(total - self._write_packet.size, 0) self._write_packet.upback(sent, total) break if not self._write_buffer: self._write_notifier.setEnabled(False) def event(self, event): """ Callback called when a Qt event is fired. :param event: the event :return: was the event handled? """ if isinstance(event, PacketEvent): self._dispatch() event.accept() return True else: event.ignore() return False def _dispatch(self): """ Callback called when a packet event is fired. """ while self._incoming: packet = self._incoming.popleft() self._logger.debug("Received packet: %s" % packet) # Notify for replies if isinstance(packet, Reply): packet.trigger_callback() # Otherwise forward to the subclass elif not self.recv_packet(packet): self._logger.warning("Unhandled packet received: %s" % packet) def send_packet(self, packet): """ Sends a packet the other party. :param packet: the packet :return: a packet deferred if a reply is expected """ if not self._connected: self._logger.warning("Sending packet while disconnected") return None self._logger.debug("Sending packet: %s" % packet) # Enqueue the packet self._outgoing.append(packet) if not self._write_notifier.isEnabled(): self._write_notifier.setEnabled(True) # Queries return a packet deferred if isinstance(packet, Query): d = PacketDeferred() packet.register_callback(d) return d return None def recv_packet(self, packet): """ Receives a packet from the other party. :param packet: the packet :return: has the packet been handled? """ raise NotImplementedError("recv_packet() not implemented")
class SignalHandler(QObject): """Handler responsible for handling OS signals (SIGINT, SIGTERM, etc.). Attributes: _app: The QApplication instance. _quitter: The Quitter instance. _activated: Whether activate() was called. _notifier: A QSocketNotifier used for signals on Unix. _timer: A QTimer used to poll for signals on Windows. _orig_handlers: A {signal: handler} dict of original signal handlers. _orig_wakeup_fd: The original wakeup filedescriptor. """ def __init__(self, *, app, quitter, parent=None): super().__init__(parent) self._app = app self._quitter = quitter self._notifier = None self._timer = usertypes.Timer(self, 'python_hacks') self._orig_handlers = {} self._activated = False self._orig_wakeup_fd = None def activate(self): """Set up signal handlers. On Windows this uses a QTimer to periodically hand control over to Python so it can handle signals. On Unix, it uses a QSocketNotifier with os.set_wakeup_fd to get notified. """ self._orig_handlers[signal.SIGINT] = signal.signal( signal.SIGINT, self.interrupt) self._orig_handlers[signal.SIGTERM] = signal.signal( signal.SIGTERM, self.interrupt) if os.name == 'posix' and hasattr(signal, 'set_wakeup_fd'): # pylint: disable=import-error,no-member import fcntl read_fd, write_fd = os.pipe() for fd in (read_fd, write_fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) self._notifier = QSocketNotifier(read_fd, QSocketNotifier.Read, self) self._notifier.activated.connect(self.handle_signal_wakeup) self._orig_wakeup_fd = signal.set_wakeup_fd(write_fd) else: self._timer.start(1000) self._timer.timeout.connect(lambda: None) self._activated = True def deactivate(self): """Deactivate all signal handlers.""" if not self._activated: return if self._notifier is not None: self._notifier.setEnabled(False) rfd = self._notifier.socket() wfd = signal.set_wakeup_fd(self._orig_wakeup_fd) os.close(rfd) os.close(wfd) for sig, handler in self._orig_handlers.items(): signal.signal(sig, handler) self._timer.stop() self._activated = False @pyqtSlot() def handle_signal_wakeup(self): """Handle a newly arrived signal. This gets called via self._notifier when there's a signal. Python will get control here, so the signal will get handled. """ log.destroy.debug("Handling signal wakeup!") self._notifier.setEnabled(False) read_fd = self._notifier.socket() try: os.read(read_fd, 1) except OSError: log.destroy.exception("Failed to read wakeup fd.") self._notifier.setEnabled(True) def _log_later(self, *lines): """Log the given text line-wise with a QTimer.""" for line in lines: QTimer.singleShot(0, functools.partial(log.destroy.info, line)) def interrupt(self, signum, _frame): """Handler for signals to gracefully shutdown (SIGINT/SIGTERM). This calls shutdown and remaps the signal to call interrupt_forcefully the next time. """ signal.signal(signal.SIGINT, self.interrupt_forcefully) signal.signal(signal.SIGTERM, self.interrupt_forcefully) # Signals can arrive anywhere, so we do this in the main thread self._log_later("SIGINT/SIGTERM received, shutting down!", "Do the same again to forcefully quit.") QTimer.singleShot( 0, functools.partial(self._quitter.shutdown, 128 + signum)) def interrupt_forcefully(self, signum, _frame): """Interrupt forcefully on the second SIGINT/SIGTERM request. This skips our shutdown routine and calls QApplication:exit instead. It then remaps the signals to call self.interrupt_really_forcefully the next time. """ signal.signal(signal.SIGINT, self.interrupt_really_forcefully) signal.signal(signal.SIGTERM, self.interrupt_really_forcefully) # Signals can arrive anywhere, so we do this in the main thread self._log_later("Forceful quit requested, goodbye cruel world!", "Do the same again to quit with even more force.") QTimer.singleShot(0, functools.partial(self._app.exit, 128 + signum)) def interrupt_really_forcefully(self, signum, _frame): """Interrupt with even more force on the third SIGINT/SIGTERM request. This doesn't run *any* Qt cleanup and simply exits via Python. It will most likely lead to a segfault. """ print("WHY ARE YOU DOING THIS TO ME? :(") sys.exit(128 + signum)
class TwistedSocketNotifier(QObject): """ Connection between an fd event and reader/writer callbacks. """ activated = pyqtSignal(int) def __init__(self, parent, reactor, watcher, socketType): QObject.__init__(self, parent) self.reactor = reactor self.watcher = watcher fd = watcher.fileno() self.notifier = QSocketNotifier(fd, socketType, parent) self.notifier.setEnabled(True) if socketType == QSocketNotifier.Read: self.fn = self.read else: self.fn = self.write self.notifier.activated.connect(self.fn) def shutdown(self): self.notifier.setEnabled(False) self.notifier.activated.disconnect(self.fn) self.fn = self.watcher = None self.notifier.deleteLater() self.deleteLater() def read(self, fd): if not self.watcher: return w = self.watcher # doRead can cause self.shutdown to be called so keep # a reference to self.watcher def _read(): #Don't call me again, until the data has been read self.notifier.setEnabled(False) why = None try: why = w.doRead() inRead = True except: inRead = False log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, inRead) elif self.watcher: self.notifier.setEnabled(True) # Re enable notification following sucessfull read self.reactor._iterate(fromqt=True) log.callWithLogger(w, _read) def write(self, sock): if not self.watcher: return w = self.watcher def _write(): why = None self.notifier.setEnabled(False) try: why = w.doWrite() except: log.err() why = sys.exc_info()[1] if why: self.reactor._disconnectSelectable(w, why, False) elif self.watcher: self.notifier.setEnabled(True) self.reactor._iterate(fromqt=True) log.callWithLogger(w, _write)
class ServersDiscovery(QObject): def __init__(self, logger, parent=None): super(ServersDiscovery, self).__init__(parent) self._logger = logger self._active = [] self._servers = [] self._socket = None self._read_notifier = None self._started = False self._timer = QTimer() self._timer.setInterval(10000) self._timer.timeout.connect(self._trim_replies) @property def servers(self): return self._servers @property def started(self): return self._started def start(self): self._logger.debug("Starting servers discovery....") self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(("", 31013)) self._socket.settimeout(0) self._socket.setblocking(0) self._read_notifier = QSocketNotifier(self._socket.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._notify_read) self._read_notifier.setEnabled(True) self._started = True self._timer.start() def stop(self): self._logger.debug("Stopping servers discovery...") self._read_notifier.setEnabled(False) try: self._socket.close() except socket.errno: pass self._socket = None self._started = False self._timer.stop() def _notify_read(self): request, address = self._socket.recvfrom(4096) request = request.decode("utf-8") if request.startswith(DISCOVERY_REQUEST): self._logger.debug("Received discovery request from %s:%d" % address) _, host, port, ssl = request.split() server = {"host": host, "port": int(port), "no_ssl": ssl != "True"} if server not in self._active: self._active.append(server) if server not in self._servers: self._servers.append(server) self._logger.debug("Server discovered: %s" % server) self._logger.debug("Sending discovery reply to %s:%d..." % address) reply = DISCOVERY_REPLY reply = reply.encode("utf-8") self._socket.sendto(reply, address) def _trim_replies(self): self._logger.debug("Discovered %d servers: %s" % (len(self.servers), self.servers)) self._servers = self._active self._active = []
class ClientsDiscovery(QObject): def __init__(self, logger, parent=None): super(ClientsDiscovery, self).__init__(parent) self._logger = logger self._info = None self._socket = None self._read_notifier = None self._started = False self._timer = QTimer() self._timer.setInterval(10000) self._timer.timeout.connect(self._send_request) @property def started(self): return self._started def start(self, host, port, ssl): self._logger.debug("Starting clients discovery...") self._info = "%s %d %s" % (host, port, ssl) self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self._socket.settimeout(0) self._socket.setblocking(0) self._read_notifier = QSocketNotifier(self._socket.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._notify_read) self._read_notifier.setEnabled(True) self._started = True self._timer.start() self._send_request() def stop(self): self._logger.debug("Stopping clients discovery...") self._read_notifier.setEnabled(False) try: self._socket.close() except socket.error: pass self._socket = None self._started = False self._timer.stop() def _send_request(self): self._logger.debug("Sending discovery request...") request = DISCOVERY_REQUEST + " " + self._info request = request.encode("utf-8") while len(request): try: sent = self._socket.sendto(request, ("<broadcast>", 31013)) except socket.error: self._logger.warning("Couldn't send discovery request") request = request[sent:] def _notify_read(self): response, address = self._socket.recvfrom(4096) response = response.decode("utf-8") if response == DISCOVERY_REPLY: self._logger.debug("Received discovery reply from %s:%d" % address)
class WindowClass(QMainWindow, form_class): #class WindowClass(QMainWindow) : def __init__(self): super().__init__() # uic.loadUi('button.ui',self) pg.setConfigOptions(antialias=True) self._client = Client() socket = self._client.socket self._notifier = QSocketNotifier(socket.getsockopt(zmq.FD), QSocketNotifier.Read, self) self._notifier.activated.connect(self._socket_activity) self._counter = 0 self.setupUi(self) self._log('[UI] started') s_thrd = Thread(target=self.sub_thrd) s_thrd.start() self._log('[Thread] started') self.buff_size = 200 #버튼에 기능을 연결하는 코드 self.pBut.clicked.connect(self.button1Function) self.p = self.gview self.p.setRange(xRange=[0, self.buff_size], yRange=[0, 500]) self.p2 = self.gview_2 self.p2.setRange(xRange=[0, self.buff_size], yRange=[-250, 250]) # self.p.enableAutoRange(axis='x') # self.p2.enableAutoRange(axis='x') self.data = np.empty(self.buff_size) self.data2 = np.empty(self.buff_size) self.time = np.empty(self.buff_size) self.sizeArray = (np.random.random(500) * 20.).astype(int) self.ptr = 0 self.lastTime = time() self.nowTime = time() dt = self.nowTime - self.lastTime self.fps = -1.0 #1.0/dt self.timer = QtCore.QTimer() self.timer.timeout.connect(self._update) self.timer.start(0) #sub_msg = self._sub.recv() #self._log(sub_msg) def sub_thrd(self): _sub = Subscriber() print("thrd sub start") while True: msg = dict(zip('topic1', (x.decode() for x in _sub.recv()))) #id, msg = _sub.recv(); #msg = _sub.recv(); #print("[sub_thrd][%s] : %s" %(id, msg)) #print("[sub_thrd] : %s" %( msg)) for k, v in msg.items(): print(f'{k}: {v}') #self._log(msg) def _update(self): self.p.clear() self.p2.clear() if self.ptr > self.buff_size: self.p.enableAutoRange(axis='x') self.p2.enableAutoRange(axis='x') if self.randCheck.isChecked(): self.size = self.sizeArray else: self.size = self.sizeSpin.value() rtval = np.random.randint(0, 100) self.data[:-1] = self.data[1:] self.data[-1] = float(rtval) self.data2 = self.data + 50 self.time[:-1] = self.time[1:] self.time[-1] = self.ptr curve = pg.PlotCurveItem(x=self.time, y=self.data, pen='g', brush='b', size=self.size) curve2 = pg.PlotCurveItem(x=self.time, y=self.data2, pen='r', brush='b', size=self.size) self.p.addItem(curve) self.p2.addItem(curve2) self.ptr += 1 now = time() dt = now - self.lastTime self.lastTime = time() if self.fps < 0: self.fps = 1.0 / dt else: s = np.clip(dt * 3., 0, 1) self.fps = self.fps * (1 - s) + (1.0 / dt) * s self.p.setTitle('%0.2f fps' % self.fps) self.p.repaint() def _log(self, data): text = self.txtBrw.toPlainText() self.txtBrw.setPlainText(text + data + '\n') def _send_data(self): msg = "Test message #" + str(self._counter) self._client.dispatch(msg) self._log("[UI] sent: " + msg) self._counter += 1 def _socket_activity(self): self._notifier.setEnabled(False) flags = self._client.socket.getsockopt(zmq.EVENTS) self._log("[Socket] socket.getsockopt(zmq.EVENTS): " + repr(flags)) if flags & zmq.POLLIN: received = self._client.recv() self._log("[Socket] zmq.POLLIN") self._log("[Socket] received: " + repr(received)) elif flags & zmq.POLLOUT: self._log("[Socket] zmq.POLLOUT") elif flags & zmq.POLLERR: self._log("[Socket] FAILURE") self._notifier.setEnabled(True) flags = self._client.socket.getsockopt(zmq.EVENTS) self._log("[Socket] socket.getsockopt(zmq.EVENTS): " + repr(flags)) def button1Function(self): print("btn_1 Clicked") self.txtBrw.append("pBut clicked") self._send_data()
class SignalHandler(QObject): """Handler responsible for handling OS signals (SIGINT, SIGTERM, etc.). Attributes: _app: The QApplication instance. _quitter: The Quitter instance. _activated: Whether activate() was called. _notifier: A QSocketNotifier used for signals on Unix. _timer: A QTimer used to poll for signals on Windows. _orig_handlers: A {signal: handler} dict of original signal handlers. _orig_wakeup_fd: The original wakeup filedescriptor. """ def __init__(self, *, app, quitter, parent=None): super().__init__(parent) self._app = app self._quitter = quitter self._notifier = None self._timer = usertypes.Timer(self, 'python_hacks') self._orig_handlers = {} self._activated = False self._orig_wakeup_fd = None def activate(self): """Set up signal handlers. On Windows this uses a QTimer to periodically hand control over to Python so it can handle signals. On Unix, it uses a QSocketNotifier with os.set_wakeup_fd to get notified. """ self._orig_handlers[signal.SIGINT] = signal.signal( signal.SIGINT, self.interrupt) self._orig_handlers[signal.SIGTERM] = signal.signal( signal.SIGTERM, self.interrupt) if os.name == 'posix' and hasattr(signal, 'set_wakeup_fd'): # pylint: disable=import-error,no-member,useless-suppression import fcntl read_fd, write_fd = os.pipe() for fd in [read_fd, write_fd]: flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) self._notifier = QSocketNotifier(read_fd, QSocketNotifier.Read, self) self._notifier.activated.connect(self.handle_signal_wakeup) self._orig_wakeup_fd = signal.set_wakeup_fd(write_fd) else: self._timer.start(1000) self._timer.timeout.connect(lambda: None) self._activated = True def deactivate(self): """Deactivate all signal handlers.""" if not self._activated: return if self._notifier is not None: self._notifier.setEnabled(False) rfd = self._notifier.socket() wfd = signal.set_wakeup_fd(self._orig_wakeup_fd) os.close(rfd) os.close(wfd) for sig, handler in self._orig_handlers.items(): signal.signal(sig, handler) self._timer.stop() self._activated = False @pyqtSlot() def handle_signal_wakeup(self): """Handle a newly arrived signal. This gets called via self._notifier when there's a signal. Python will get control here, so the signal will get handled. """ log.destroy.debug("Handling signal wakeup!") self._notifier.setEnabled(False) read_fd = self._notifier.socket() try: os.read(read_fd, 1) except OSError: log.destroy.exception("Failed to read wakeup fd.") self._notifier.setEnabled(True) def _log_later(self, *lines): """Log the given text line-wise with a QTimer.""" for line in lines: QTimer.singleShot(0, functools.partial(log.destroy.info, line)) def interrupt(self, signum, _frame): """Handler for signals to gracefully shutdown (SIGINT/SIGTERM). This calls shutdown and remaps the signal to call interrupt_forcefully the next time. """ signal.signal(signal.SIGINT, self.interrupt_forcefully) signal.signal(signal.SIGTERM, self.interrupt_forcefully) # Signals can arrive anywhere, so we do this in the main thread self._log_later("SIGINT/SIGTERM received, shutting down!", "Do the same again to forcefully quit.") QTimer.singleShot(0, functools.partial( self._quitter.shutdown, 128 + signum)) def interrupt_forcefully(self, signum, _frame): """Interrupt forcefully on the second SIGINT/SIGTERM request. This skips our shutdown routine and calls QApplication:exit instead. It then remaps the signals to call self.interrupt_really_forcefully the next time. """ signal.signal(signal.SIGINT, self.interrupt_really_forcefully) signal.signal(signal.SIGTERM, self.interrupt_really_forcefully) # Signals can arrive anywhere, so we do this in the main thread self._log_later("Forceful quit requested, goodbye cruel world!", "Do the same again to quit with even more force.") QTimer.singleShot(0, functools.partial(self._app.exit, 128 + signum)) def interrupt_really_forcefully(self, signum, _frame): """Interrupt with even more force on the third SIGINT/SIGTERM request. This doesn't run *any* Qt cleanup and simply exits via Python. It will most likely lead to a segfault. """ print("WHY ARE YOU DOING THIS TO ME? :(") sys.exit(128 + signum)
class Eldasi(QWidget): def __init__(self): super(Eldasi, self).__init__() self.initUI() self.client_available = False def initUI(self): # widgets to enable/disable depending on connection status self.toggle_widgets = [] # Log widget self.log_text = QPlainTextEdit(self) # Quick connections layout connections_layout = QHBoxLayout() self.address_text = QLineEdit(self, text='tcp://127.0.0.1:5000') self.connect_button = QPushButton("Start server") connections_layout.addWidget(self.address_text) connections_layout.addWidget(self.connect_button) self.connect_button.clicked.connect(self.start_server) # Text send widgets send_button = QPushButton("Send") send_button.clicked.connect(lambda: self._send_data(self.data_text.text())) self.data_text = QLineEdit(self) send_layout = QHBoxLayout() send_layout.addWidget(send_button) send_layout.addWidget(self.data_text) self.toggle_widgets.extend([send_button, self.data_text]) # Quick ELSA commands widgets commands_layout = QHBoxLayout() commands_layout2 = QHBoxLayout() commands_layout3 = QHBoxLayout() start_button = QPushButton("Start") start_button.clicked.connect(lambda: self._send_data("init")) commands_layout.addWidget(start_button) stop_button = QPushButton("Stop") stop_button.clicked.connect(lambda: self._send_data("stop")) commands_layout.addWidget(stop_button) status_button = QPushButton("Status") status_button.clicked.connect(lambda: self._send_data("status")) commands_layout.addWidget(status_button) init_button = QPushButton("Scan_self") init_button.clicked.connect(lambda: self._send_data("startself")) commands_layout.addWidget(init_button) tune_button = QPushButton("GDAC/TDAC") tune_button.clicked.connect(lambda: self._send_data("tune")) commands_layout2.addWidget(tune_button) fix_button = QPushButton("fix") fix_button.clicked.connect(lambda: self._send_data("fix")) commands_layout2.addWidget(fix_button) threshold_button = QPushButton("Threshold") threshold_button.clicked.connect(lambda: self._send_data("threshold")) commands_layout2.addWidget(threshold_button) framerate_button = QPushButton("Framerate") framerate_button.clicked.connect(lambda: self._send_data("framerate")) commands_layout2.addWidget(framerate_button) external_button = QPushButton("Scan_ext") external_button.clicked.connect(lambda: self._send_data("startexternal")) commands_layout3.addWidget(external_button) poweron_button = QPushButton("Power on") poweron_button.clicked.connect(lambda: self._send_data("poweron")) commands_layout3.addWidget(poweron_button) poweroff_button = QPushButton("Power off") poweroff_button.clicked.connect(lambda: self._send_data("poweroff")) commands_layout3.addWidget(poweroff_button) exit_button = QPushButton("EXIT") exit_button.clicked.connect(lambda: self._send_data("exit")) commands_layout3.addWidget(exit_button) self.toggle_widgets.extend([start_button, stop_button, tune_button, init_button, fix_button, poweron_button, poweroff_button, status_button, external_button, threshold_button, framerate_button, exit_button]) # Combine widgets to layout layout = QGridLayout() layout.addWidget(self.log_text, 1, 1) layout.addLayout(connections_layout, 2, 1) layout.addLayout(commands_layout, 3, 1) layout.addLayout(commands_layout2, 4, 1) layout.addLayout(commands_layout3, 5, 1) layout.addLayout(send_layout, 6, 1) self.setLayout(layout) # Window settings self.setGeometry(200, 200, 480, 400) self.setWindowTitle('ELSA DAQ Simulation') self.show() self._switch_widgets() self._log('[UI] started') def start_server(self): self._server = Server() ret = self._server.start(self.address_text.text()) if ret is not True: self._log("[ZMQError]: " + repr(ret)) return socket = self._server.socket self._notifier = QSocketNotifier(socket.getsockopt(zmq.FD), QSocketNotifier.Read, self) self._notifier.activated.connect(self._socket_activity) self._notifier.setEnabled(True) self.connect_button.clicked.disconnect() self.connect_button.clicked.connect(self.stop_server) self.connect_button.setText('Stop server') self._log("[Socket] Start server") def stop_server(self): self._notifier.setEnabled(False) self._server.socket.close() self._notifier.activated.disconnect() self._switch_widgets(False) self.connect_button.clicked.disconnect() self.connect_button.clicked.connect(self.start_server) self.connect_button.setText('Start server') self._log("[Socket] Stop server") def _switch_widgets(self, status=None): if status is None: self._switch_widgets(status=not self.toggle_widgets[0].isEnabled()) else: for w in self.toggle_widgets: w.setEnabled(status) def _log(self, data): self.log_text.appendPlainText(data) def _send_data(self, data): if data: self._server.dispatch(data) self._log("[UI] sent: " + data) def _socket_activity(self): self._notifier.setEnabled(False) socket = self._server.socket if socket.getsockopt(zmq.EVENTS) & zmq.POLLIN: while socket.getsockopt(zmq.EVENTS) & zmq.POLLIN: received = self._server.recv() self._log("[Socket] received: " + repr(received)) # Called when client connects elif socket.getsockopt(zmq.EVENTS) & zmq.POLLOUT: self._log("[Socket] Client connected") self.client_available = True self._switch_widgets(True) else: self.client_available = False self._switch_widgets(False) self._notifier.setEnabled(True)
class VNCClient(QObject): started = pyqtSignal() finished = pyqtSignal() imageSizeChanged = pyqtSignal(QSize) imageChanged = pyqtSignal(int, int, int, int) passwordRequested = pyqtSignal(bool) textCut = pyqtSignal(unicode) def __init__(self, host, port, settings, parent=None): super(VNCClient, self).__init__(parent) self.thread = QThread() self.moveToThread(self.thread) self.host = host self.port = port self.settings = settings self.username = None self.password = None self.rfb_client = None self.socket_notifier = None self.thread.started.connect(self._SH_ThreadStarted) self.thread.finished.connect(self._SH_ThreadFinished) def _get_settings(self): return self.__dict__['settings'] def _set_settings(self, settings): old_settings = self.__dict__.get('settings', None) if settings == old_settings: return self.__dict__['settings'] = settings if self.thread.isRunning(): QApplication.postEvent(self, RFBConfigureClientEvent()) settings = property(_get_settings, _set_settings) del _get_settings, _set_settings @property def image(self): return self.rfb_client.image if self.rfb_client is not None else None def start(self): self.thread.start() def stop(self): self.thread.quit() def key_event(self, key, down): if self.thread.isRunning(): QApplication.postEvent(self, RFBKeyEvent(key, down)) def mouse_event(self, x, y, button_mask): if self.thread.isRunning(): QApplication.postEvent(self, RFBMouseEvent(x, y, button_mask)) def cut_text_event(self, text): if text and self.thread.isRunning(): QApplication.postEvent(self, RFBCutTextEvent(text)) def _SH_ThreadStarted(self): self.started.emit() notification_center = NotificationCenter() notification_center.post_notification('VNCClientWillStart', sender=self) self.rfb_client = RFBClient(parent=self) try: self.rfb_client.connect() except RFBClientError: self.thread.quit() else: self.socket_notifier = QSocketNotifier(self.rfb_client.socket, QSocketNotifier.Read, self) self.socket_notifier.activated.connect(self._SH_SocketNotifierActivated) notification_center.post_notification('VNCClientDidStart', sender=self) def _SH_ThreadFinished(self): self.finished.emit() notification_center = NotificationCenter() notification_center.post_notification('VNCClientWillEnd', sender=self) if self.socket_notifier is not None: self.socket_notifier.activated.disconnect(self._SH_SocketNotifierActivated) self.socket_notifier = None self.rfb_client = None notification_center.post_notification('VNCClientDidEnd', sender=self) def _SH_SocketNotifierActivated(self, sock): self.socket_notifier.setEnabled(False) try: self.rfb_client.handle_server_message() except RFBClientError: self.thread.quit() else: self.socket_notifier.setEnabled(True) def _SH_ConfigureRFBClient(self): if self.rfb_client is not None: self.rfb_client.configure() def customEvent(self, event): handler = getattr(self, '_EH_%s' % event.name, Null) handler(event) def _EH_RFBConfigureClientEvent(self, event): if self.rfb_client is not None: self.rfb_client.configure() def _EH_RFBKeyEvent(self, event): if self.rfb_client is not None: self.rfb_client.send_key_event(event.key, event.down) def _EH_RFBMouseEvent(self, event): if self.rfb_client is not None: self.rfb_client.send_pointer_event(event.x, event.y, event.button_mask) def _EH_RFBCutTextEvent(self, event): if self.rfb_client is not None: self.rfb_client.send_client_cut_text(event.text)
class QtSocket(QObject): """ This class is the glue between the communication from the server and the Qt event loop and by using the QSocketNotifier we can get notified when data is ready to be sent / received without openining an additional thread """ def __init__(self, gotten_packet_callback, parent=None): super(QtSocket, self).__init__(parent) self._socket = None self._connected = False self._gotten_packet_callback = gotten_packet_callback self._incoming = [] self._outgoing = [] @property def connected(self): return self._connected def initialize_socket(self, sock): self._recv_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Read, self) self._recv_notifier.activated.connect(self._handle_recv_ready) self._recv_notifier.setEnabled(True) self._send_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Write, self) self._send_notifier.activated.connect(self._handle_send_ready) self._send_notifier.setEnabled(True) self._socket = sock self._connected = True self._incoming = [] self._outgoing = [] def disconnect(self): if not self._socket: return self._recv_notifier.setEnabled(False) self._send_notifier.setEnabled(False) try: self._socket.close() except socket.error: pass self._socket = None self._connected = False self._incoming = [] self._outgoing = [] def _handle_recv_ready(self): if not self.connected: return try: pkt = RevetherPacket.parse_stream(ClientSocket(self._socket)) except construct.StreamError: self.disconnect() return self._incoming.append(pkt) if self._incoming: QCoreApplication.instance().postEvent(self, SocketEvent()) def send_packet(self, pkt, callback=None, err_callback=None): item = (pkt, callback, err_callback) self._outgoing.append(item) def _handle_send_ready(self): if not self.connected: return if not self._outgoing: return pkt, callback, err_callback = self._outgoing.pop(0) try: self._socket.send(pkt) if callback: callback() except socket.error: if err_callback: err_callback() return def event(self, event): """ Callback for the when a SocketEvent is happening """ if isinstance(event, SocketEvent): self._gotten_packet_callback(self._incoming) self._incoming = [] event.accept() return True else: event.ignore() return False
class ClientSocket(QObject): """ This class is acts a bridge between a client socket and the Qt event loop. By using a QSocketNotifier, we can be notified when some data is ready to be read or written on the socket, not requiring an extra thread. """ MAX_DATA_SIZE = 65535 def __init__(self, logger, parent=None): QObject.__init__(self, parent) self._logger = logger self._socket = None self._server = parent and isinstance(parent, ServerSocket) self._read_buffer = bytearray() self._read_notifier = None self._read_packet = None self._write_buffer = bytearray() self._write_notifier = None self._write_packet = None self._connected = False self._outgoing = collections.deque() self._incoming = collections.deque() @property def connected(self): """Is the underlying socket connected?""" return self._connected def wrap_socket(self, sock): """Sets the underlying socket to use.""" self._read_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._notify_read) self._read_notifier.setEnabled(True) self._write_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Write, self) self._write_notifier.activated.connect(self._notify_write) self._write_notifier.setEnabled(True) self._socket = sock def disconnect(self, err=None): """Terminates the current connection.""" if not self._socket: return self._logger.debug("Disconnected") if err: self._logger.exception(err) self._read_notifier.setEnabled(False) self._write_notifier.setEnabled(False) try: self._socket.shutdown(socket.SHUT_RDWR) self._socket.close() except socket.error: pass self._socket = None self._connected = False def set_keep_alive(self, cnt, intvl, idle): """ Set the TCP keep-alive of the underlying socket. It activates after idle seconds of idleness, sends a keep-alive ping once every intvl seconds, and disconnects after `cnt`failed pings. """ # Taken from https://github.com/markokr/skytools/ tcp_keepcnt = getattr(socket, "TCP_KEEPCNT", None) tcp_keepintvl = getattr(socket, "TCP_KEEPINTVL", None) tcp_keepidle = getattr(socket, "TCP_KEEPIDLE", None) tcp_keepalive = getattr(socket, "TCP_KEEPALIVE", None) sio_keeplive_vals = getattr(socket, "SIO_KEEPALIVE_VALS", None) if (tcp_keepidle is None and tcp_keepalive is None and sys.platform == "darwin"): tcp_keepalive = 0x10 self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) if tcp_keepcnt is not None: self._socket.setsockopt(socket.IPPROTO_TCP, tcp_keepcnt, cnt) if tcp_keepintvl is not None: self._socket.setsockopt(socket.IPPROTO_TCP, tcp_keepintvl, intvl) if tcp_keepidle is not None: self._socket.setsockopt(socket.IPPROTO_TCP, tcp_keepidle, idle) elif tcp_keepalive is not None: self._socket.setsockopt(socket.IPPROTO_TCP, tcp_keepalive, idle) elif sio_keeplive_vals is not None: self._socket.ioctl(sio_keeplive_vals, (1, idle * 1000, intvl * 1000)) def _check_socket(self): """Check if the connection has been established yet.""" # Ignore if you're already connected if self._connected: return True # Check if the connection was successful ret = self._socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) if ret != 0 and ret != errno.EINPROGRESS and ret != errno.EWOULDBLOCK: self.disconnect(socket.error(ret, os.strerror(ret))) return False else: # Do SSL handshake if needed if isinstance(self._socket, ssl.SSLSocket): try: self._socket.do_handshake() except socket.error as e: if not isinstance(e, ssl.SSLWantReadError) and not isinstance( e, ssl.SSLWantReadError): self.disconnect(e) return False self._connected = True self._logger.debug("Connected") return True def _notify_read(self): """Callback called when some data is ready to be read on the socket.""" if not self._check_socket(): return # Read as many bytes as possible try: data = self._socket.recv(ClientSocket.MAX_DATA_SIZE) if not data: self.disconnect() return except socket.error as e: if (e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK) and not isinstance(e, ssl.SSLWantReadError) and not isinstance(e, ssl.SSLWantWriteError)): self.disconnect(e) return # No more data available self._read_buffer.extend(data) # Split the received data on new lines (= packets) while True: if self._read_packet is None: if b"\n" in self._read_buffer: pos = self._read_buffer.index(b"\n") line = self._read_buffer[:pos] self._read_buffer = self._read_buffer[pos + 1: # noqa: E203 ] # Try to parse the line (= packet) try: dct = json.loads(line.decode("utf-8")) self._read_packet = Packet.parse_packet( dct, self._server) except Exception as e: msg = "Invalid packet received: %s" % line self._logger.warning(msg) self._logger.exception(e) continue else: break # Not enough data for a packet else: if isinstance(self._read_packet, Container): avail = len(self._read_buffer) total = self._read_packet.size # Trigger the downback if self._read_packet.downback: self._read_packet.downback(min(avail, total), total) # Read the container's content if avail >= total: self._read_packet.content = self._read_buffer[:total] self._read_buffer = self._read_buffer[total:] else: break # Not enough data for a packet self._incoming.append(self._read_packet) self._read_packet = None if self._incoming: QCoreApplication.instance().postEvent(self, PacketEvent()) def _notify_write(self): """Callback called when some data is ready to written on the socket.""" if not self._check_socket(): return if not self._write_buffer: if not self._outgoing: return # No more packets to send self._write_packet = self._outgoing.popleft() # Dump the packet as a line try: line = json.dumps(self._write_packet.build_packet()) line = line.encode("utf-8") + b"\n" except Exception as e: msg = "Invalid packet being sent: %s" % self._write_packet self._logger.warning(msg) self._logger.exception(e) return # Write the container's content self._write_buffer.extend(bytearray(line)) if isinstance(self._write_packet, Container): data = self._write_packet.content self._write_buffer.extend(bytearray(data)) self._write_packet.size += len(line) # Send as many bytes as possible try: count = min(len(self._write_buffer), ClientSocket.MAX_DATA_SIZE) sent = self._socket.send(self._write_buffer[:count]) self._write_buffer = self._write_buffer[sent:] except socket.error as e: if (e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK) and not isinstance(e, ssl.SSLWantReadError) and not isinstance(e, ssl.SSLWantWriteError)): self.disconnect(e) return # Can't write anything # Trigger the upback if (isinstance(self._write_packet, Container) and self._write_packet.upback): self._write_packet.size -= count total = len(self._write_packet.content) sent = max(total - self._write_packet.size, 0) self._write_packet.upback(sent, total) if not self._write_buffer and not self._outgoing: self._write_notifier.setEnabled(False) def event(self, event): """Callback called when a Qt event is fired.""" if isinstance(event, PacketEvent): self._dispatch() event.accept() return True else: event.ignore() return False def _dispatch(self): """Callback called when a PacketEvent is received.""" while self._incoming: packet = self._incoming.popleft() self._logger.debug("Received packet: %s" % packet) # Notify for replies if isinstance(packet, Reply): packet.trigger_callback() # Otherwise forward to the subclass elif not self.recv_packet(packet): self._logger.warning("Unhandled packet received: %s" % packet) def send_packet(self, packet): """Sends a packet the other party.""" if not self._connected: self._logger.warning("Sending packet while disconnected") return None self._logger.debug("Sending packet: %s" % packet) # Enqueue the packet self._outgoing.append(packet) if not self._write_notifier.isEnabled(): self._write_notifier.setEnabled(True) # Queries return a packet deferred if isinstance(packet, Query): d = PacketDeferred() packet.register_callback(d) return d return None def recv_packet(self, packet): """Receives a packet from the other party.""" raise NotImplementedError("recv_packet() not implemented")
class QWZMQListener(QWidget): def __init__(self, **kwargs): QWidget.__init__(self, parent=None) #logger.debug('In QWZMQListener.__init__') self.vold = -1 self.timeout = kwargs.get('timeout', 1000) # milliseconds _is_normal = kwargs.get('is_normal', True) _on_poll = kwargs.get('on_poll', self.on_zmq_poll) _host = kwargs.get('host', 'localhost') _platform = kwargs.get('platform', 6) _topicfilter = kwargs.get('topicfilter', b'') # b'10001' _uri = 'tcp://%s:%d' % (_host, front_pub_port(_platform) ) # 'tcp://localhost:30016' if _is_normal: self.init_connect_zmq(_on_poll, _uri, _topicfilter) def init_connect_zmq(self, on_poll, uri, topicfilter): logger.debug('QWZMQListener.init_connect_zmq uri=%s' % uri) self.zmq_context = zmq.Context(1) self.zmq_socket = self.zmq_context.socket(zmq.SUB) self.zmq_socket.connect(uri) self.zmq_socket.setsockopt(zmq.SUBSCRIBE, topicfilter) self.zmq_socket.setsockopt(zmq.RCVTIMEO, self.timeout) # milliseconds self.zmq_notifier = QSocketNotifier(self.zmq_socket.getsockopt(zmq.FD), QSocketNotifier.Read, self) self.zmq_notifier.activated.connect(on_poll) #print("QWZMQListener flags zmq.POLLIN:%d POLLOUT:%d POLLERR:%d" % (zmq.POLLIN, zmq.POLLOUT, zmq.POLLERR)) def on_zmq_poll(self): """Needs to be re-implemented to do real work with messages from zmq. """ self.zmq_notifier.setEnabled(False) flags = self.zmq_socket.getsockopt(zmq.EVENTS) flag = 'UNKNOWN' msg = '' #print("A") if flags & zmq.POLLIN: while self.zmq_socket.getsockopt(zmq.EVENTS) & zmq.POLLIN: flag = 'POLLIN' msg = self.zmq_socket.recv_multipart() self.process_zmq_message(msg) #print("L") elif flags & zmq.POLLOUT: flag = 'POLLOUT' elif flags & zmq.POLLERR: flag = 'POLLERR' else: pass print("QWZMQListener Flag zmq.%s in %d msg: %s" % (flag, flags, msg)) #print("B") self.zmq_notifier.setEnabled(True) #self.kick_zmq() # WITHOUT THIS LINE IT WOULD NOT CALL on_read_msg AGAIN! _flags = self.zmq_socket.getsockopt(zmq.EVENTS) if _flags & zmq.POLLIN: self.on_zmq_poll() #print("E") def process_zmq_message(self, msg): s = msg[1].decode('utf-8') v = int(s.split(' ', 1)[0]) #self.setWindowTitle(s) self.edi_text.append(s) self.edi_text.moveCursor( 23, 0) # QTextCursor.NextRow, QTextCursor.MoveAnchor if v != self.vold + 1: self.edi_text.append(' ===== got it!') self.vold = v if v % 50 == 0: self.edi_text.setText('') #scrollbar = self.edi_text.verticalScrollBar() #scrollbar.setValue(scrollbar.maximum()); def kick_zmq(self): """ WITHOUT THIS LINE IT WOULD NOT CALL on_read_msg AGAIN! """ _flags = self.zmq_socket.getsockopt(zmq.EVENTS) print("kick_zmq _flags:", _flags) if _flags == 1: print('GRRRRRRRRRRRRRRRRRRRRRRR!') def closeEvent(self, e): logger.debug('%s.closeEvent' % self._name) QWidget.closeEvent(self, e)
class ZmqClient(QObject): nucleo_firmware_version = pyqtSignal(str) message_received = pyqtSignal(object) heartbeat = pyqtSignal(int) start_of_match = pyqtSignal(int) match_state_change = pyqtSignal(int, int) comm_stats = pyqtSignal(object) propulsion_telemetry = pyqtSignal(object) propulsion_telemetry_ex = pyqtSignal(object) rplidar_plot = pyqtSignal(object) rplidar_robot_detection = pyqtSignal(object) astar_dbg_map = pyqtSignal(object) odometry_config = pyqtSignal(object) propulsion_controller_config = pyqtSignal(object) dynamixel_registers = pyqtSignal(int, int, object) fpga_registers = pyqtSignal(int, int) fpga_registers_crc = pyqtSignal(int, int, int) asserv_plot = pyqtSignal(int, int) sensors = pyqtSignal(int) gpio = pyqtSignal(int) debug_goldo = pyqtSignal(int) robot_end_load_config_status = pyqtSignal(bool) sequence_event = pyqtSignal(int, object) odrive_response = pyqtSignal(object) camera_image = pyqtSignal(object) def __init__(self, ip=None, parent=None): super(ZmqClient, self).__init__(None) self._context = zmq.Context() self._push_socket = self._context.socket(zmq.PUB) self._push_socket.connect('tcp://{}:3002'.format(ip)) self._socket_main_sub = self._context.socket(zmq.SUB) self._socket_main_sub.connect('tcp://{}:3801'.format(ip)) self._socket_main_sub.setsockopt(zmq.SUBSCRIBE, b'') self._socket_main_pub = self._context.socket(zmq.PUB) self._socket_main_pub.connect('tcp://{}:3802'.format(ip)) self._notifier_main = QSocketNotifier( self._socket_main_sub.getsockopt(zmq.FD), QSocketNotifier.Read, self) self._notifier_main.activated.connect(self._on_socket_main_sub) self._callbacks = [] self._goldo_log_fd = open("goldo_log.txt", "wt") def send_message(self, message_type, message_body): self._push_socket.send_multipart( [struct.pack('<H', message_type), message_body]) def publishTopic(self, topic, msg=None): if msg is None: msg = _sym_db.GetSymbol('google.protobuf.Empty')() self._socket_main_pub.send_multipart([ topic.encode('utf8'), msg.DESCRIPTOR.full_name.encode('utf8'), msg.SerializeToString() ]) def registerCallback(self, pattern: str, callback): pattern = (pattern.replace('*', r'([^/]+)').replace( '/#', r'(/[^/]+)*').replace('#/', r'([^/]+/)*')) self._callbacks.append((re.compile(f"^{pattern}$"), callback)) def _on_socket_main_sub(self): self._notifier_main.setEnabled(False) socket = self._socket_main_sub flags = socket.getsockopt(zmq.EVENTS) while flags & zmq.POLLIN: topic, full_name, payload = socket.recv_multipart() flags = socket.getsockopt(zmq.EVENTS) topic = topic.decode('utf8') full_name = full_name.decode('utf8') msg_class = _sym_db.GetSymbol(full_name) if msg_class is not None: msg = msg_class() msg.ParseFromString(payload) else: msg = None callback_matches = ((regexp.match(topic), callback) for regexp, callback in self._callbacks) for match, callback in callback_matches: if match: callback(*match.groups(), msg) if topic.startswith('nucleo'): pass #print(topic) if topic == 'nucleo/out/os/heartbeat': self.heartbeat.emit(msg.timestamp) if topic == 'nucleo/out/robot/config/load_status': self.robot_end_load_config_status.emit(msg.status == 0) if topic == 'nucleo/out/odrive/response': self.odrive_response.emit(msg) if topic == 'nucleo/out/odometry/config': self.odometry_config.emit(msg) if topic == 'nucleo/out/propulsion/telemetry': self.propulsion_telemetry.emit(msg) if topic == 'nucleo/out/propulsion/telemetry_ex': self.propulsion_telemetry_ex.emit(msg) if topic == 'nucleo/out/propulsion/config': self.propulsion_controller_config.emit(msg) if topic == 'nucleo/out/fpga/reg': self.fpga_registers.emit(msg.apb_address, msg.apb_value) if topic == 'rplidar/out/scan': self.rplidar_plot.emit(msg) if topic == 'nucleo/out/dbg_goldo': #print("nucleo/out/dbg_goldo : {:8x}".format(msg.value)) val = msg.value ts = val & 0xffff0000 ts = ts >> 16 pos = val & 0x0000ffff if pos & 0x00008000 == 0x00008000: pos -= 0x10000 self._goldo_log_fd.write("{} {}\n".format(ts, pos)) self._goldo_log_fd.flush() self.asserv_plot.emit(ts, pos) self._notifier_main.setEnabled(True) def _on_message_received(self, msg): return self.message_received.emit(msg) msg_type = struct.unpack('<H', msg[0:2])[0] if msg_type == message_types.GetNucleoFirmwareVersion: firm_ver = NucleoFirmwareVersion(msg[2:]) #print("firm_ver : " + firm_ver.s) self.nucleo_firmware_version.emit(firm_ver.s) if msg_type == message_types.SensorsChange: self.sensors.emit(struct.unpack('<I', msg[2:])[0]) if msg_type == message_types.GPIODebug: self.gpio.emit(struct.unpack('<I', msg[2:])[0]) if msg_type == message_types.SequenceEvent: event_id = struct.unpack('<B', msg[2:3])[0] self.sequence_event.emit(event_id, msg[3:]) print(event_id, msg[3:]) if msg_type == message_types.DebugGoldo: self.debug_goldo.emit(struct.unpack('<I', msg[2:])[0]) if msg_type == message_types.MatchStateChange: state, side = struct.unpack('<BB', msg[2:4]) self.match_state_change.emit(state, side) if msg_type == message_types.PropulsionTelemetryEx: telemetry = PropulsionTelemetryEx(msg[2:]) self.propulsion_telemetry_ex.emit(telemetry) if msg_type == message_types.RplidarPlot: my_plot = RplidarPlot(msg[2:]) self.rplidar_plot.emit(my_plot) if msg_type == message_types.RplidarRobotDetection: other_robot = RplidarRobotDetection(msg[2:]) self.rplidar_robot_detection.emit(other_robot) if msg_type == message_types.RobotStratDbgAstarMap: astar_dbg_map_bytes = msg[3:] self.astar_dbg_map.emit(astar_dbg_map_bytes) if msg_type == message_types.StartOfMatch: timestamp = struct.unpack('<I', msg[2:6])[0] self.start_of_match.emit(timestamp) if msg_type == message_types.CommStats: #self.comm_stats.emit(struct.unpack('<HHIIIII', msg[2:])) pass if msg_type == message_types.DbgDynamixelGetRegisters: id_, address = struct.unpack('<BB', msg[2:4]) self.dynamixel_registers.emit(id_, address, msg[4:]) if msg_type == message_types.NucleoLog: log = msg[2:].split(bytes([0]), 1)[0].decode("utf-8") print("" + log) if msg_type == 90: print(struct.unpack('<BB', msg[2:]))
class ZmqClient(QObject, ZmqCodecMixin): notifyScore = pyqtSignal() notifyHeartbeat = pyqtSignal() notifyConfigStatus = pyqtSignal() notifyMatchTimer = pyqtSignal() notifyMatchState = pyqtSignal() notifySide = pyqtSignal() notifyODrive = pyqtSignal() notifyTirette = pyqtSignal() notifyEmergencyStop = pyqtSignal() cameraFrameReceived = pyqtSignal(object) cameraDetectionsReceived = pyqtSignal(object) def __init__(self, parent=None): super().__init__(parent) self._context = zmq.Context() self._sub_socket = self._context.socket(zmq.SUB) self._sub_socket.bind('tcp://*:3901') self._sub_socket.setsockopt(zmq.SUBSCRIBE, b'') self._pub_socket = self._context.socket(zmq.PUB) self._pub_socket.bind('tcp://*:3902') self._notifier = QSocketNotifier(self._sub_socket.getsockopt(zmq.FD), QSocketNotifier.Read, self) self._notifier.activated.connect(self._on_sub_socket_event) self._config_status = 0 self._score = 0 self._heartbeat = 0 self._match_timer = 0 self._side = 0 self._match_state = 0 self._odrive_state = '00' self._odrive_error = False self._tirette = False self._emergency_stop = False @pyqtSlot() def configNucleo(self): msg = Int32Value(value=0) self.publishTopic('gui/out/commands/config_nucleo', msg) @pyqtSlot() def preMatch(self): msg = Int32Value(value=0) self.publishTopic('gui/out/commands/prematch', msg) @pyqtSlot() def odriveCalibration(self): msg = Int32Value(value=0) self.publishTopic('nucleo/in/propulsion/calibrate_odrive', msg) def publishTopic(self, topic, msg): self._pub_socket.send_multipart(self._encodeTopic(topic, msg)) def _on_sub_socket_event(self): self._notifier.setEnabled(False) flags = self._sub_socket.getsockopt(zmq.EVENTS) while flags & zmq.POLLIN: topic, msg = self._decodeTopic(self._sub_socket.recv_multipart()) self._on_message_received(topic, msg) flags = self._sub_socket.getsockopt(zmq.EVENTS) self._notifier.setEnabled(True) def _on_message_received(self, topic, msg): if topic == 'gui/in/heartbeat': self._heartbeat = msg.timestamp self.notifyHeartbeat.emit() if topic == 'gui/in/match_state': self._match_state = msg.value self.notifyMatchState.emit() if topic == 'gui/in/score': self._score = msg.value self.notifyScore.emit() if topic == 'gui/in/side': self._side = msg.value self.notifySide.emit() if topic == 'gui/in/match_timer': self._match_timer = msg.value self.notifyMatchTimer.emit() if topic == 'gui/in/camera/image': self.cameraFrameReceived.emit(msg) if topic == 'gui/in/camera/detections': self.cameraDetectionsReceived.emit(msg) if topic == 'gui/in/nucleo_config_status': self._config_status = msg.status + 1 self.notifyConfigStatus.emit() if topic == 'gui/in/nucleo_reset': self._config_status = 0 self.notifyConfigStatus.emit() if topic == 'gui/in/odrive_state': self._odrive_state = msg.value self.notifyODrive.emit() if topic == 'gui/in/odrive_error': self._odrive_error = msg.value self.notifyODrive.emit() if topic == 'gui/in/sensors/start_match': self._tirette = msg.value self.notifyTirette.emit() if topic == 'gui/in/sensors/emergency_stop': self._emergency_stop = msg.value self.notifyEmergencyStop.emit() @pyqtProperty(int, notify=notifyScore) def score(self): return self._score @pyqtProperty(int, notify=notifyHeartbeat) def heartbeat(self): return self._heartbeat @pyqtProperty(int, notify=notifyMatchState) def match_state(self): return self._match_state @pyqtProperty(int, notify=notifyConfigStatus) def config_status(self): return self._config_status @pyqtProperty(int, notify=notifyMatchTimer) def match_timer(self): return self._match_timer @pyqtProperty(str, notify=notifyODrive) def odrive_state(self): return self._odrive_state @pyqtProperty(bool, notify=notifyODrive) def odrive_error(self): return self._odrive_error @pyqtProperty(bool, notify=notifyTirette) def tirette(self): return self._tirette @pyqtProperty(bool, notify=notifyEmergencyStop) def emergency_stop(self): return self._emergency_stop def setSide(self, side): self._side = side self.notifySide.emit() msg = Int32Value(value=side) self.publishTopic('gui/out/side', msg) @pyqtProperty(int, fset=setSide, notify=notifySide) def side(self): return self._side