def register_qt_recv_signal(self, rxMsgSignal, recvMatch=None): """The client is a Qt app, and wants to receive the messages specified in recvMatch via the rxMsgSignal """ self.rxMsgSignal = rxMsgSignal self.recvMatch = recvMatch if self.mavfile: if isinstance(self.mavfile, mavutil.mavmmaplog): self.thread = QThread() self.recvWorker = UqtMavFileRecvWorker(self.mavfile, self.rxMsgSignal, self.recvMatch) self.recvWorker.moveToThread(self.thread) self.recvWorker.finished.connect(self.thread.quit) self.thread.started.connect(self.recvWorker.worker) self.thread.start() elif type(self.mavfile) in [ mavutil.mavserial ]: fd = self.get_file_descriptor() if fd >= 0: self.notifier = QSocketNotifier(fd, QSocketNotifier.Read) self.notifier.activated.connect(self.can_read) elif type(self.mavfile) in [ mavutil.mavudp, mavutil.mavtcp, mavutil.mavtcpin, mavutil.mavmcast, mavutil.mavlogfile, mavutil.mavlogfile ]: fd = self.get_file_descriptor() if fd: self.notifier = QSocketNotifier(fd.fileno(), QSocketNotifier.Read) self.notifier.activated.connect(self.can_read) elif isinstance(self.mavfile, mavutil.mavchildexec): pass
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 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)
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
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
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 utils.is_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(typing.cast(sip.voidptr, read_fd), QSocketNotifier.Read, self) self._notifier.activated.connect( # type: ignore[attr-defined] self.handle_signal_wakeup) self._orig_wakeup_fd = signal.set_wakeup_fd(write_fd) # pylint: enable=import-error,no-member,useless-suppression else: self._timer.start(1000) self._timer.timeout.connect(lambda: None) self._activated = True
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
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 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 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)
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)
def __init__(self, reactor, watcher, type): QSocketNotifier.__init__(self, watcher.fileno(), type) self.reactor = reactor self.watcher = watcher self.fn = None if type == QSocketNotifier.Read: self.fn = self.read elif type == QSocketNotifier.Write: self.fn = self.write QObject.connect(self, SIGNAL("activated(int)"), self.fn)
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 __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)
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 _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 connect(self, sock): """ Wraps the socket with the current object. :param sock: the socket """ 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 __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 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 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()
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 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 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 __init__(self): super(QObject, self).__init__() conn_parent, conn_child = socketpair() # TODO: figure out which of the two sockets should be set to # inheritable and which should be passed to the child if hasattr(conn_child, 'set_inheritable'): conn_child.set_inheritable(True) # Use a local variable for child so that we can talk to the child in # on_finalize without needing a reference to self child = mp.Process(target=_converter_process_func, args=(conn_parent, conn_child)) child.daemon = True child.start() self.child = child conn_child.close() self.conn = conn_parent self.busy = False self.notificationPending = False self.conversionNotifier = QSocketNotifier(self.conn.fileno(), QSocketNotifier.Read) self.conversionNotifier.activated.connect(self._conversionNotifierActivated) def on_finalize(conn): sendObject(conn_parent, {'command':'quit'}) conn_parent.close() child.join() weakref.finalize(self, on_finalize, conn_parent)
def __init__(self): super().__init__() self.inotify = INotify() self.qnotifier = QSocketNotifier(self.inotify.fd, QSocketNotifier.Read) self.qnotifier.activated.connect(self._on_activated) self.wd = None
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)
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 __init__(self, parent: Optional[QObject] = None) -> None: super().__init__(parent) self.inotify = INotify() self.qnotifier = QSocketNotifier(self.inotify.fd, QSocketNotifier.Read) self.qnotifier.activated.connect(self._on_activated) self.wd = None
def __init__(self): conn_parent, conn_child = mp.Pipe() # Use a local variable for child so that we can talk to the child in # on_finalize without needing a reference to self child = mp.Process(target=_converter_process_func, args=(conn_parent, conn_child)) child.daemon = True child.start() self.child = child conn_child.close() self.conn = conn_parent self.busy = False self.conversionNotifier = QSocketNotifier(self.conn.fileno(), QSocketNotifier.Read) # assign the activated signal of the notifier to a conversionDone # member to get a more meaningful signal name for others to connect to self.conversionDone = self.conversionNotifier.activated def on_finalize(conn): conn_parent.send({'command': 'quit'}) conn_parent.close() child.join() weakref.finalize(self, on_finalize, conn_parent)
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 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 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 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 __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)
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 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(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 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 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)
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)
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)