Пример #1
0
def init():
    """Start listening to incoming connections."""
    global _server
    if _server is not None:
        return

    server = QLocalServer(None)

    # find a free socket name to use
    for name in ids():
        if server.listen(name):
            break
    else:
        # all names failed, try to contact and remove stale file if that fails
        socket = QLocalSocket()
        for name in ids():
            socket.connectToServer(name)
            if not socket.waitForConnected(10000):
                QLocalServer.removeServer(name)
                if server.listen(name):
                    break
            else:
                socket.disconnectFromServer()
        else:
            # no ids left, don't listen
            return
    app.aboutToQuit.connect(server.close)
    server.newConnection.connect(slot_new_connection)
    os.environ["FRESCOBALDI_SOCKET"] = name
    _server = server
Пример #2
0
class SingleApplication(QObject):

    newInstance = pyqtSignal()
    urlPost = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.mServer = QLocalServer()
        self.mServer.newConnection.connect(self.newConnection)

    def listen(self, client):
        self.mServer.removeServer(client)
        self.mServer.listen(client)
        print(self.mServer.errorString())

    def hasPrevious(self, name, args):
        socket = QLocalSocket()
        socket.connectToServer(name, QLocalSocket.ReadWrite)
        if socket.waitForConnected():
            if len(args) > 1:
                socket.write(args[1])

            socket.flush()
            return True
        return False

    def newConnection(self):
        self.newInstance.emit()
        self.mSocket = self.mServer.nextPendingConnection()
        self.mSocket.readyRead.connect(self.readyRead)

    def readyRead(self):
        self.urlPost.emit(str(self.mSocket.readAll()))
        self.mSocket.close()
Пример #3
0
    def __init__(self, _id, *argv):

        super(QtSingleApplication, self).__init__(*argv)
        self._id = _id
        self._activationWindow = None
        self._activateOnMessage = False

        # Is there another instance running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected(-1)

        if self._isRunning:
            # Yes, there is.
            self._outStream = QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            # No, there isn't.
            # First we remove existing servers of that name that might not have been properly closed as the server died
            QLocalServer.removeServer(self._id)
            self._outSocket = None
            self._outStream = None
            self._inSocket = None
            self._inStream = None
            self._server = QLocalServer()
            self._server.listen(self._id)
            self._server.newConnection.connect(self._onNewConnection)
Пример #4
0
def init():
    """Start listening to incoming connections."""
    global _server
    if _server is not None:
        return
    
    server = QLocalServer(None)
    
    # find a free socket name to use
    for name in ids():
        if server.listen(name):
            break
    else:
        # all names failed, try to contact and remove stale file if that fails
        socket = QLocalSocket()
        for name in ids():
            socket.connectToServer(name)
            if not socket.waitForConnected(10000):
                QLocalServer.removeServer(name)
                if server.listen(name):
                    break
            else:
                socket.disconnectFromServer()
        else:
            # no ids left, don't listen
            return
    app.aboutToQuit.connect(server.close)
    server.newConnection.connect(slot_new_connection)
    os.environ["FRESCOBALDI_SOCKET"] = name
    _server = server
Пример #5
0
 def __init__(self, appid, *argv):
     super(SingleApplication, self).__init__(*argv)
     self._appid = appid
     self._activationWindow = None
     self._activateOnMessage = False
     self._outSocket = QLocalSocket()
     self._outSocket.connectToServer(self._appid)
     self._isRunning = self._outSocket.waitForConnected()
     self._outStream = None
     self._inSocket = None
     self._inStream = None
     self._server = None
     self.settings = QSettings(SingleApplication.getSettingsPath(),
                               QSettings.IniFormat)
     self.singleInstance = self.settings.value('singleInstance',
                                               'on',
                                               type=str) in {'on', 'true'}
     if self._isRunning and self.singleInstance:
         self._outStream = QTextStream(self._outSocket)
         for a in argv[0][1:]:
             a = os.path.join(os.getcwd(), a)
             if os.path.isfile(a):
                 self.sendMessage(a)
                 break
         sys.exit(0)
     else:
         error = self._outSocket.error()
         if error == QLocalSocket.ConnectionRefusedError:
             self.close()
             QLocalServer.removeServer(self._appid)
         self._outSocket = None
         self._server = QLocalServer()
         self._server.listen(self._appid)
         self._server.newConnection.connect(self._onNewConnection)
Пример #6
0
 def newLocalServer(self):
     self.localServer = QLocalServer(self)
     self.localServer.newConnection.connect(self.newLocalConnection)
     if not self.localServer.listen(self.serverName):
         if self.localServer.serverError(
         ) == QAbstractSocket.AddressInUseError:
             QLocalServer.removeServer(self.serverName)
             self.localServer.listen(self.serverName)
Пример #7
0
 def create(self, name=piony.G_SOCKET_NAME):
     QLocalServer.removeServer(name)
     self.server = QLocalServer()
     if not self.server.listen(name):
         print("Error: server -- unable to start: {}.".format(
             self.server.errorString()))
         self.quit.emit()
     self.server.newConnection.connect(self.notify)
Пример #8
0
 def create(self, name=piony.G_SOCKET_NAME):
     QLocalServer.removeServer(name)
     self.server = QLocalServer()
     if not self.server.listen(name):
         print("Error: server -- unable to start: {}."
               .format(self.server.errorString()))
         self.quit.emit()
     self.server.newConnection.connect(self.notify)
Пример #9
0
 def __init__(self, instance=None):
     QObject.__init__(self)
     sock_name = self.get_sock_name(instance)
     QLocalServer.removeServer(sock_name)
     self._server = QLocalServer()
     self._server.newConnection.connect(self._on_new_connection)
     if not self._server.listen(sock_name):
         logging.error("Can not start ipc: %s" % self._server.errorString())
     self._readers = {}
Пример #10
0
class InstanceHandler(QObject):
    """ Makes sure that only one instance of this application can be run. """

    received = pyqtSignal(str)

    def __init__(self, parent, name):
        super(__class__, self).__init__(parent)
        self._parent = parent
        self._name = name
        self._timeout = 1000
        self._socket = QLocalSocket(self)
        self._socket.connectToServer(self._name)
        self._is_already_running = self._socket.waitForConnected(self._timeout)
        if not self.isAlreadyRunning():
            self._server = QLocalServer(self)
            self._server.newConnection.connect(self._receive_data)
            self._server.removeServer(self._name)
            self._server.listen(self._name)

    def _send_data(self, data=None):
        """ Sends model to an server-application. """
        if not data:
            data = ""
        self._socket.write(data.encode())
        self._socket.waitForBytesWritten(self._timeout)
        self._socket.disconnectFromServer()

    def _receive_data(self):
        """ Receives model from an client-application. """
        socket = self._server.nextPendingConnection()
        if socket.waitForReadyRead(self._timeout):
            # Emit transmitted model.
            self.received.emit(socket.readAll().data().decode(
                "utf-8", "surrogateescape"))
        else:
            # When no model was transmitted just emit empty string.
            self.received.emit("")

    def newTab(self, input):
        """ Opens a new tab in an already running instance. """
        self._send_data(input)

    def isAlreadyRunning(self) -> bool:
        """ Returns True when an instance of the application is already running, otherwise False. """
        return self._is_already_running
Пример #11
0
    def __init__(self, win_id, *argv):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.info(f'Start Tribler application. Win id: "{win_id}". '
                         f'Sys argv: "{sys.argv}"')

        QApplication.__init__(self, *argv)

        self._id = win_id
        self._activation_window = None
        self._activate_on_message = False

        # Is there another instance running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected()

        self._outStream = None
        self._inSocket = None
        self._inStream = None
        self._server = None

        if self._isRunning:
            # Yes, there is.
            self.logger.info('Another instance is running')
            self._outStream = QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            # No, there isn't, at least not properly.
            # Cleanup any past, crashed server.
            error = self._outSocket.error()
            self.logger.info(f'No running instances (socket error: {error})')
            if error == QLocalSocket.ConnectionRefusedError:
                self.logger.info(
                    'Received QLocalSocket.ConnectionRefusedError; removing server.'
                )
                self.close()
                QLocalServer.removeServer(self._id)
            self._outSocket = None
            self._server = QLocalServer()
            self._server.listen(self._id)
            connect(self._server.newConnection, self._on_new_connection)
Пример #12
0
    def __init__(self, win_id, *argv):

        logfunc = logging.info
        logfunc(sys._getframe().f_code.co_name + '()')

        QApplication.__init__(self, *argv)

        self._id = win_id
        self._activation_window = None
        self._activate_on_message = False

        # Is there another instance running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected()

        self._outStream = None
        self._inSocket = None
        self._inStream = None
        self._server = None

        if self._isRunning:
            # Yes, there is.
            self._outStream = QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            # No, there isn't, at least not properly.
            # Cleanup any past, crashed server.
            error = self._outSocket.error()
            logfunc(LOGVARSTR % ('self._outSocket.error()', error))
            if error == QLocalSocket.ConnectionRefusedError:
                logfunc('received QLocalSocket.ConnectionRefusedError; ' + \
                        'removing server.')
                self.close()
                QLocalServer.removeServer(self._id)
            self._outSocket = None
            self._server = QLocalServer()
            self._server.listen(self._id)
            self._server.newConnection.connect(self._on_new_connection)

        logfunc(sys._getframe().f_code.co_name + '(): returning')
Пример #13
0
    def __init__(self, win_id, *argv):

        logfunc = logging.info
        logfunc(sys._getframe().f_code.co_name + '()')

        QApplication.__init__(self, *argv)

        self._id = win_id
        self._activation_window = None
        self._activate_on_message = False

        # Is there another instance running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected()

        self._outStream = None
        self._inSocket = None
        self._inStream = None
        self._server = None

        if self._isRunning:
            # Yes, there is.
            self._outStream = QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            # No, there isn't, at least not properly.
            # Cleanup any past, crashed server.
            error = self._outSocket.error()
            logfunc(LOGVARSTR % ('self._outSocket.error()', error))
            if error == QLocalSocket.ConnectionRefusedError:
                logfunc('received QLocalSocket.ConnectionRefusedError; ' + \
                        'removing server.')
                self.close()
                QLocalServer.removeServer(self._id)
            self._outSocket = None
            self._server = QLocalServer()
            self._server.listen(self._id)
            self._server.newConnection.connect(self._on_new_connection)

        logfunc(sys._getframe().f_code.co_name + '(): returning')
Пример #14
0
class QtSingleApplication(QApplication):
    """
    Adapted from https://stackoverflow.com/a/12712362/11038610

    Published by Johan Rade under 2-clause BSD license, opensource.org/licenses/BSD-2-Clause
    """
    message_received_event = pyqtSignal(str)

    def __init__(self, id, *argv):

        super().__init__(*argv)
        self._id = id

        # Is there another instance running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected()

        if self._isRunning:
            # Yes, there is.
            self._outStream = QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            # No, there isn't.
            self._outSocket = None
            self._outStream = None
            self._inSocket = None
            self._inStream = None
            self._server = QLocalServer()
            self._server.removeServer(self._id)
            self._server.listen(self._id)
            self._server.newConnection.connect(self._onNewConnection)

    def isRunning(self):
        return self._isRunning

    def id(self):
        return self._id

    def sendMessage(self, msg):
        if not self._outStream:
            return False
        self._outStream << msg << '\n'
        self._outStream.flush()
        return self._outSocket.waitForBytesWritten()

    def _onNewConnection(self):
        if self._inSocket:
            self._inSocket.readyRead.disconnect(self._onReadyRead)
        self._inSocket = self._server.nextPendingConnection()
        if not self._inSocket:
            return
        self._inStream = QTextStream(self._inSocket)
        self._inStream.setCodec('UTF-8')
        self._inSocket.readyRead.connect(self._onReadyRead)

    def _onReadyRead(self):
        while True:
            msg = self._inStream.readLine()
            if not msg:
                break
            self.message_received_event.emit(msg)
class TreeMainControl(QObject):
    """Class to handle all global controls.

    Provides methods for all controls and stores local control objects.
    """
    def __init__(self, pathObjects, parent=None):
        """Initialize the main tree controls

        Arguments:
            pathObjects -- a list of file objects to open
            parent -- the parent QObject if given
        """
        super().__init__(parent)
        self.localControls = []
        self.activeControl = None
        self.trayIcon = None
        self.isTrayMinimized = False
        self.configDialog = None
        self.sortDialog = None
        self.numberingDialog = None
        self.findTextDialog = None
        self.findConditionDialog = None
        self.findReplaceDialog = None
        self.filterTextDialog = None
        self.filterConditionDialog = None
        self.basicHelpView = None
        self.passwords = {}
        self.creatingLocalControlFlag = False
        globalref.mainControl = self
        self.allActions = {}
        try:
            # check for existing TreeLine session
            socket = QLocalSocket()
            socket.connectToServer('treeline3-session', QIODevice.WriteOnly)
            # if found, send files to open and exit TreeLine
            if socket.waitForConnected(1000):
                socket.write(
                    bytes(repr([str(path) for path in pathObjects]), 'utf-8'))
                if socket.waitForBytesWritten(1000):
                    socket.close()
                    sys.exit(0)
            # start local server to listen for attempt to start new session
            self.serverSocket = QLocalServer()
            # remove any old servers still around after a crash in linux
            self.serverSocket.removeServer('treeline3-session')
            self.serverSocket.listen('treeline3-session')
            self.serverSocket.newConnection.connect(self.getSocket)
        except AttributeError:
            print(_('Warning:  Could not create local socket'))
        mainVersion = '.'.join(__version__.split('.')[:2])
        globalref.genOptions = options.Options('general', 'TreeLine',
                                               mainVersion, 'bellz')
        optiondefaults.setGenOptionDefaults(globalref.genOptions)
        globalref.miscOptions = options.Options('misc')
        optiondefaults.setMiscOptionDefaults(globalref.miscOptions)
        globalref.histOptions = options.Options('history')
        optiondefaults.setHistOptionDefaults(globalref.histOptions)
        globalref.toolbarOptions = options.Options('toolbar')
        optiondefaults.setToolbarOptionDefaults(globalref.toolbarOptions)
        globalref.keyboardOptions = options.Options('keyboard')
        optiondefaults.setKeyboardOptionDefaults(globalref.keyboardOptions)
        try:
            globalref.genOptions.readFile()
            globalref.miscOptions.readFile()
            globalref.histOptions.readFile()
            globalref.toolbarOptions.readFile()
            globalref.keyboardOptions.readFile()
        except IOError:
            errorDir = options.Options.basePath
            if not errorDir:
                errorDir = _('missing directory')
            QMessageBox.warning(
                None, 'TreeLine',
                _('Error - could not write config file to {}').format(
                    errorDir))
            options.Options.basePath = None
        iconPathList = self.findResourcePaths('icons', iconPath)
        globalref.toolIcons = icondict.IconDict(
            [path / 'toolbar' for path in iconPathList],
            ['', '32x32', '16x16'])
        globalref.toolIcons.loadAllIcons()
        windowIcon = globalref.toolIcons.getIcon('treelogo')
        if windowIcon:
            QApplication.setWindowIcon(windowIcon)
        globalref.treeIcons = icondict.IconDict(iconPathList, ['', 'tree'])
        icon = globalref.treeIcons.getIcon('default')
        qApp.setStyle(QStyleFactory.create('Fusion'))
        self.colorSet = colorset.ColorSet()
        if globalref.miscOptions['ColorTheme'] != 'system':
            self.colorSet.setAppColors()
        self.recentFiles = recentfiles.RecentFileList()
        if globalref.genOptions['AutoFileOpen'] and not pathObjects:
            recentPath = self.recentFiles.firstPath()
            if recentPath:
                pathObjects = [recentPath]
        self.setupActions()
        self.systemFont = QApplication.font()
        self.updateAppFont()
        if globalref.genOptions['MinToSysTray']:
            self.createTrayIcon()
        qApp.focusChanged.connect(self.updateActionsAvail)
        if pathObjects:
            for pathObj in pathObjects:
                self.openFile(pathObj, True)
        else:
            self.createLocalControl()

    def getSocket(self):
        """Open a socket from an attempt to open a second Treeline instance.

        Opens the file (or raise and focus if open) in this instance.
        """
        socket = self.serverSocket.nextPendingConnection()
        if socket and socket.waitForReadyRead(1000):
            data = str(socket.readAll(), 'utf-8')
            try:
                paths = ast.literal_eval(data)
                if paths:
                    for path in paths:
                        pathObj = pathlib.Path(path)
                        if pathObj != self.activeControl.filePathObj:
                            self.openFile(pathObj, True)
                        else:
                            self.activeControl.activeWindow.activateAndRaise()
                else:
                    self.activeControl.activeWindow.activateAndRaise()
            except (SyntaxError, ValueError, TypeError, RuntimeError):
                pass

    def findResourcePaths(self, resourceName, preferredPath=''):
        """Return list of potential non-empty pathlib objects for the resource.

        List includes preferred, module and user option paths.
        Arguments:
            resourceName -- the typical name of the resource directory
            preferredPath -- add this as the second path if given
        """
        # use abspath() - pathlib's resolve() can be buggy with network drives
        modPath = pathlib.Path(os.path.abspath(sys.path[0]))
        if modPath.is_file():
            modPath = modPath.parent  # for frozen binary
        pathList = [modPath / '..' / resourceName, modPath / resourceName]
        if options.Options.basePath:
            basePath = pathlib.Path(options.Options.basePath)
            pathList.insert(0, basePath / resourceName)
        if preferredPath:
            pathList.insert(1, pathlib.Path(preferredPath))
        return [
            pathlib.Path(os.path.abspath(str(path))) for path in pathList
            if path.is_dir() and list(path.iterdir())
        ]

    def findResourceFile(self, fileName, resourceName, preferredPath=''):
        """Return a path object for a resource file.

        Add a language code before the extension if it exists.
        Arguments:
            fileName -- the name of the file to find
            resourceName -- the typical name of the resource directory
            preferredPath -- search this path first if given
        """
        fileList = [fileName]
        if globalref.lang and globalref.lang != 'C':
            fileList[0:0] = [
                fileName.replace('.', '_{0}.'.format(globalref.lang)),
                fileName.replace('.', '_{0}.'.format(globalref.lang[:2]))
            ]
        for fileName in fileList:
            for path in self.findResourcePaths(resourceName, preferredPath):
                if (path / fileName).is_file():
                    return path / fileName
        return None

    def defaultPathObj(self, dirOnly=False):
        """Return a reasonable default file path object.

        Used for open, save-as, import and export.
        Arguments:
            dirOnly -- if True, do not include basename of file
        """
        pathObj = None
        if self.activeControl:
            pathObj = self.activeControl.filePathObj
        if not pathObj:
            pathObj = self.recentFiles.firstDir()
            if not pathObj:
                pathObj = pathlib.Path.home()
        if dirOnly:
            pathObj = pathObj.parent
        return pathObj

    def openFile(self,
                 pathObj,
                 forceNewWindow=False,
                 checkModified=False,
                 importOnFail=True):
        """Open the file given by path if not already open.

        If already open in a different window, focus and raise the window.
        Arguments:
            pathObj -- the path object to read
            forceNewWindow -- if True, use a new window regardless of option
            checkModified -- if True & not new win, prompt if file modified
            importOnFail -- if True, prompts for import on non-TreeLine files
        """
        match = [
            control for control in self.localControls
            if pathObj == control.filePathObj
        ]
        if match and self.activeControl not in match:
            control = match[0]
            control.activeWindow.activateAndRaise()
            self.updateLocalControlRef(control)
            return
        if checkModified and not (forceNewWindow
                                  or globalref.genOptions['OpenNewWindow']
                                  or self.activeControl.checkSaveChanges()):
            return
        if not self.checkAutoSave(pathObj):
            if not self.localControls:
                self.createLocalControl()
            return
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            self.createLocalControl(pathObj, None, forceNewWindow)
            self.recentFiles.addItem(pathObj)
            if not (globalref.genOptions['SaveTreeStates'] and
                    self.recentFiles.retrieveTreeState(self.activeControl)):
                self.activeControl.expandRootNodes()
                self.activeControl.selectRootSpot()
            QApplication.restoreOverrideCursor()
        except IOError:
            QApplication.restoreOverrideCursor()
            QMessageBox.warning(
                QApplication.activeWindow(), 'TreeLine',
                _('Error - could not read file {0}').format(pathObj))
            self.recentFiles.removeItem(pathObj)
        except (ValueError, KeyError, TypeError):
            fileObj = pathObj.open('rb')
            fileObj, encrypted = self.decryptFile(fileObj)
            if not fileObj:
                if not self.localControls:
                    self.createLocalControl()
                QApplication.restoreOverrideCursor()
                return
            fileObj, compressed = self.decompressFile(fileObj)
            if compressed or encrypted:
                try:
                    textFileObj = io.TextIOWrapper(fileObj, encoding='utf-8')
                    self.createLocalControl(textFileObj, None, forceNewWindow)
                    fileObj.close()
                    textFileObj.close()
                    self.recentFiles.addItem(pathObj)
                    if not (globalref.genOptions['SaveTreeStates']
                            and self.recentFiles.retrieveTreeState(
                                self.activeControl)):
                        self.activeControl.expandRootNodes()
                        self.activeControl.selectRootSpot()
                    self.activeControl.compressed = compressed
                    self.activeControl.encrypted = encrypted
                    QApplication.restoreOverrideCursor()
                    return
                except (ValueError, KeyError, TypeError):
                    pass
            fileObj.close()
            importControl = imports.ImportControl(pathObj)
            structure = importControl.importOldTreeLine()
            if structure:
                self.createLocalControl(pathObj, structure, forceNewWindow)
                self.activeControl.printData.readData(
                    importControl.treeLineRootAttrib)
                self.recentFiles.addItem(pathObj)
                self.activeControl.expandRootNodes()
                self.activeControl.imported = True
                QApplication.restoreOverrideCursor()
                return
            QApplication.restoreOverrideCursor()
            if importOnFail:
                importControl = imports.ImportControl(pathObj)
                structure = importControl.interactiveImport(True)
                if structure:
                    self.createLocalControl(pathObj, structure, forceNewWindow)
                    self.activeControl.imported = True
                    return
            else:
                QMessageBox.warning(
                    QApplication.activeWindow(), 'TreeLine',
                    _('Error - invalid TreeLine file {0}').format(pathObj))
                self.recentFiles.removeItem(pathObj)
        if not self.localControls:
            self.createLocalControl()

    def decryptFile(self, fileObj):
        """Check for encryption and decrypt the fileObj if needed.

        Return a tuple of the file object and True if it was encrypted.
        Return None for the file object if the user cancels.
        Arguments:
            fileObj -- the file object to check and decrypt
        """
        if fileObj.read(len(encryptPrefix)) != encryptPrefix:
            fileObj.seek(0)
            return (fileObj, False)
        while True:
            pathObj = pathlib.Path(fileObj.name)
            password = self.passwords.get(pathObj, '')
            if not password:
                QApplication.restoreOverrideCursor()
                dialog = miscdialogs.PasswordDialog(
                    False, pathObj.name, QApplication.activeWindow())
                if dialog.exec_() != QDialog.Accepted:
                    fileObj.close()
                    return (None, True)
                QApplication.setOverrideCursor(Qt.WaitCursor)
                password = dialog.password
                if miscdialogs.PasswordDialog.remember:
                    self.passwords[pathObj] = password
            try:
                text = p3.p3_decrypt(fileObj.read(), password.encode())
                fileIO = io.BytesIO(text)
                fileIO.name = fileObj.name
                fileObj.close()
                return (fileIO, True)
            except p3.CryptError:
                try:
                    del self.passwords[pathObj]
                except KeyError:
                    pass

    def decompressFile(self, fileObj):
        """Check for compression and decompress the fileObj if needed.

        Return a tuple of the file object and True if it was compressed.
        Arguments:
            fileObj -- the file object to check and decompress
        """
        prefix = fileObj.read(2)
        fileObj.seek(0)
        if prefix != b'\037\213':
            return (fileObj, False)
        try:
            newFileObj = gzip.GzipFile(fileobj=fileObj)
        except zlib.error:
            return (fileObj, False)
        newFileObj.name = fileObj.name
        return (newFileObj, True)

    def checkAutoSave(self, pathObj):
        """Check for presence of auto save file & prompt user.

        Return True if OK to contimue, False if aborting or already loaded.
        Arguments:
            pathObj -- the base path object to search for a backup
        """
        if not globalref.genOptions['AutoSaveMinutes']:
            return True
        basePath = pathObj
        pathObj = pathlib.Path(str(pathObj) + '~')
        if not pathObj.is_file():
            return True
        msgBox = QMessageBox(
            QMessageBox.Information, 'TreeLine',
            _('Backup file "{}" exists.\nA previous '
              'session may have crashed').format(pathObj),
            QMessageBox.NoButton, QApplication.activeWindow())
        restoreButton = msgBox.addButton(_('&Restore Backup'),
                                         QMessageBox.ApplyRole)
        deleteButton = msgBox.addButton(_('&Delete Backup'),
                                        QMessageBox.DestructiveRole)
        cancelButton = msgBox.addButton(_('&Cancel File Open'),
                                        QMessageBox.RejectRole)
        msgBox.exec_()
        if msgBox.clickedButton() == restoreButton:
            self.openFile(pathObj)
            if self.activeControl.filePathObj != pathObj:
                return False
            try:
                basePath.unlink()
                pathObj.rename(basePath)
            except OSError:
                QMessageBox.warning(
                    QApplication.activeWindow(), 'TreeLine',
                    _('Error - could not rename "{0}" to "{1}"').format(
                        pathObj, basePath))
                return False
            self.activeControl.filePathObj = basePath
            self.activeControl.updateWindowCaptions()
            self.recentFiles.removeItem(pathObj)
            self.recentFiles.addItem(basePath)
            return False
        elif msgBox.clickedButton() == deleteButton:
            try:
                pathObj.unlink()
            except OSError:
                QMessageBox.warning(
                    QApplication.activeWindow(), 'TreeLine',
                    _('Error - could not remove backup file {}').format(
                        pathObj))
        else:  # cancel button
            return False
        return True

    def createLocalControl(self,
                           pathObj=None,
                           treeStruct=None,
                           forceNewWindow=False):
        """Create a new local control object and add it to the list.

        Use an imported structure if given or open the file if path is given.
        Arguments:
            pathObj -- the path object or file object for the control to open
            treeStruct -- the imported structure to use
            forceNewWindow -- if True, use a new window regardless of option
        """
        self.creatingLocalControlFlag = True
        localControl = treelocalcontrol.TreeLocalControl(
            self.allActions, pathObj, treeStruct, forceNewWindow)
        localControl.controlActivated.connect(self.updateLocalControlRef)
        localControl.controlClosed.connect(self.removeLocalControlRef)
        self.localControls.append(localControl)
        self.updateLocalControlRef(localControl)
        self.creatingLocalControlFlag = False
        localControl.updateRightViews()
        localControl.updateCommandsAvail()

    def updateLocalControlRef(self, localControl):
        """Set the given local control as active.

        Called by signal from a window becoming active.
        Also updates non-modal dialogs.
        Arguments:
            localControl -- the new active local control
        """
        if localControl != self.activeControl:
            self.activeControl = localControl
            if self.configDialog and self.configDialog.isVisible():
                self.configDialog.setRefs(self.activeControl)

    def removeLocalControlRef(self, localControl):
        """Remove ref to local control based on a closing signal.

        Also do application exit clean ups if last control closing.
        Arguments:
            localControl -- the local control that is closing
        """
        try:
            self.localControls.remove(localControl)
        except ValueError:
            return  # skip for unreporducible bug - odd race condition?
        if globalref.genOptions['SaveTreeStates']:
            self.recentFiles.saveTreeState(localControl)
        if not self.localControls and not self.creatingLocalControlFlag:
            if globalref.genOptions['SaveWindowGeom']:
                localControl.windowList[0].saveWindowGeom()
            else:
                localControl.windowList[0].resetWindowGeom()
            self.recentFiles.writeItems()
            localControl.windowList[0].saveToolbarPosition()
            globalref.histOptions.writeFile()
            if self.trayIcon:
                self.trayIcon.hide()
            # stop listening for session connections
            try:
                self.serverSocket.close()
                del self.serverSocket
            except AttributeError:
                pass
        if self.localControls:
            # make sure a window is active (may not be focused), to avoid
            # bugs due to a deleted current window
            newControl = self.localControls[0]
            newControl.setActiveWin(newControl.windowList[0])
        localControl.deleteLater()

    def createTrayIcon(self):
        """Create a new system tray icon if not already created.
        """
        if QSystemTrayIcon.isSystemTrayAvailable:
            if not self.trayIcon:
                self.trayIcon = QSystemTrayIcon(qApp.windowIcon(), qApp)
                self.trayIcon.activated.connect(self.toggleTrayShow)
            self.trayIcon.show()

    def trayMinimize(self):
        """Minimize to tray based on window minimize signal.
        """
        if self.trayIcon and QSystemTrayIcon.isSystemTrayAvailable:
            # skip minimize to tray if not all windows minimized
            for control in self.localControls:
                for window in control.windowList:
                    if not window.isMinimized():
                        return
            for control in self.localControls:
                for window in control.windowList:
                    window.hide()
            self.isTrayMinimized = True

    def toggleTrayShow(self):
        """Toggle show and hide application based on system tray icon click.
        """
        if self.isTrayMinimized:
            for control in self.localControls:
                for window in control.windowList:
                    window.show()
                    window.showNormal()
            self.activeControl.activeWindow.treeView.setFocus()
        else:
            for control in self.localControls:
                for window in control.windowList:
                    window.hide()
        self.isTrayMinimized = not self.isTrayMinimized

    def updateConfigDialog(self):
        """Update the config dialog for changes if it exists.
        """
        if self.configDialog:
            self.configDialog.reset()

    def currentStatusBar(self):
        """Return the status bar from the current main window.
        """
        return self.activeControl.activeWindow.statusBar()

    def windowActions(self):
        """Return a list of window menu actions from each local control.
        """
        actions = []
        for control in self.localControls:
            actions.extend(
                control.windowActions(
                    len(actions) + 1, control == self.activeControl))
        return actions

    def updateActionsAvail(self, oldWidget, newWidget):
        """Update command availability based on focus changes.

        Arguments:
            oldWidget -- the previously focused widget
            newWidget -- the newly focused widget
        """
        self.allActions['FormatSelectAll'].setEnabled(
            hasattr(newWidget, 'selectAll')
            and not hasattr(newWidget, 'editTriggers'))

    def setupActions(self):
        """Add the actions for contols at the global level.
        """
        fileNewAct = QAction(_('&New...'),
                             self,
                             toolTip=_('New File'),
                             statusTip=_('Start a new file'))
        fileNewAct.triggered.connect(self.fileNew)
        self.allActions['FileNew'] = fileNewAct

        fileOpenAct = QAction(_('&Open...'),
                              self,
                              toolTip=_('Open File'),
                              statusTip=_('Open a file from disk'))
        fileOpenAct.triggered.connect(self.fileOpen)
        self.allActions['FileOpen'] = fileOpenAct

        fileSampleAct = QAction(_('Open Sa&mple...'),
                                self,
                                toolTip=_('Open Sample'),
                                statusTip=_('Open a sample file'))
        fileSampleAct.triggered.connect(self.fileOpenSample)
        self.allActions['FileOpenSample'] = fileSampleAct

        fileImportAct = QAction(_('&Import...'),
                                self,
                                statusTip=_('Open a non-TreeLine file'))
        fileImportAct.triggered.connect(self.fileImport)
        self.allActions['FileImport'] = fileImportAct

        fileQuitAct = QAction(_('&Quit'),
                              self,
                              statusTip=_('Exit the application'))
        fileQuitAct.triggered.connect(self.fileQuit)
        self.allActions['FileQuit'] = fileQuitAct

        dataConfigAct = QAction(
            _('&Configure Data Types...'),
            self,
            statusTip=_('Modify data types, fields & output lines'),
            checkable=True)
        dataConfigAct.triggered.connect(self.dataConfigDialog)
        self.allActions['DataConfigType'] = dataConfigAct

        dataVisualConfigAct = QAction(
            _('Show C&onfiguration Structure...'),
            self,
            statusTip=_('Show read-only visualization of type structure'))
        dataVisualConfigAct.triggered.connect(self.dataVisualConfig)
        self.allActions['DataVisualConfig'] = dataVisualConfigAct

        dataSortAct = QAction(_('Sor&t Nodes...'),
                              self,
                              statusTip=_('Define node sort operations'),
                              checkable=True)
        dataSortAct.triggered.connect(self.dataSortDialog)
        self.allActions['DataSortNodes'] = dataSortAct

        dataNumberingAct = QAction(_('Update &Numbering...'),
                                   self,
                                   statusTip=_('Update node numbering fields'),
                                   checkable=True)
        dataNumberingAct.triggered.connect(self.dataNumberingDialog)
        self.allActions['DataNumbering'] = dataNumberingAct

        toolsFindTextAct = QAction(
            _('&Find Text...'),
            self,
            statusTip=_('Find text in node titles & data'),
            checkable=True)
        toolsFindTextAct.triggered.connect(self.toolsFindTextDialog)
        self.allActions['ToolsFindText'] = toolsFindTextAct

        toolsFindConditionAct = QAction(
            _('&Conditional Find...'),
            self,
            statusTip=_('Use field conditions to find nodes'),
            checkable=True)
        toolsFindConditionAct.triggered.connect(self.toolsFindConditionDialog)
        self.allActions['ToolsFindCondition'] = toolsFindConditionAct

        toolsFindReplaceAct = QAction(
            _('Find and &Replace...'),
            self,
            statusTip=_('Replace text strings in node data'),
            checkable=True)
        toolsFindReplaceAct.triggered.connect(self.toolsFindReplaceDialog)
        self.allActions['ToolsFindReplace'] = toolsFindReplaceAct

        toolsFilterTextAct = QAction(
            _('&Text Filter...'),
            self,
            statusTip=_('Filter nodes to only show text matches'),
            checkable=True)
        toolsFilterTextAct.triggered.connect(self.toolsFilterTextDialog)
        self.allActions['ToolsFilterText'] = toolsFilterTextAct

        toolsFilterConditionAct = QAction(
            _('C&onditional Filter...'),
            self,
            statusTip=_('Use field conditions to filter nodes'),
            checkable=True)
        toolsFilterConditionAct.triggered.connect(
            self.toolsFilterConditionDialog)
        self.allActions['ToolsFilterCondition'] = toolsFilterConditionAct

        toolsGenOptionsAct = QAction(
            _('&General Options...'),
            self,
            statusTip=_('Set user preferences for all files'))
        toolsGenOptionsAct.triggered.connect(self.toolsGenOptions)
        self.allActions['ToolsGenOptions'] = toolsGenOptionsAct

        toolsShortcutAct = QAction(_('Set &Keyboard Shortcuts...'),
                                   self,
                                   statusTip=_('Customize keyboard commands'))
        toolsShortcutAct.triggered.connect(self.toolsCustomShortcuts)
        self.allActions['ToolsShortcuts'] = toolsShortcutAct

        toolsToolbarAct = QAction(_('C&ustomize Toolbars...'),
                                  self,
                                  statusTip=_('Customize toolbar buttons'))
        toolsToolbarAct.triggered.connect(self.toolsCustomToolbars)
        self.allActions['ToolsToolbars'] = toolsToolbarAct

        toolsFontsAct = QAction(
            _('Customize Fo&nts...'),
            self,
            statusTip=_('Customize fonts in various views'))
        toolsFontsAct.triggered.connect(self.toolsCustomFonts)
        self.allActions['ToolsFonts'] = toolsFontsAct

        toolsColorsAct = QAction(
            _('Custo&mize Colors...'),
            self,
            statusTip=_('Customize GUI colors and themes'))
        toolsColorsAct.triggered.connect(self.toolsCustomColors)
        self.allActions['ToolsColors'] = toolsColorsAct

        formatSelectAllAct = QAction(
            _('&Select All'),
            self,
            statusTip=_('Select all text in an editor'))
        formatSelectAllAct.setEnabled(False)
        formatSelectAllAct.triggered.connect(self.formatSelectAll)
        self.allActions['FormatSelectAll'] = formatSelectAllAct

        helpBasicAct = QAction(_('&Basic Usage...'),
                               self,
                               statusTip=_('Display basic usage instructions'))
        helpBasicAct.triggered.connect(self.helpViewBasic)
        self.allActions['HelpBasic'] = helpBasicAct

        helpFullAct = QAction(
            _('&Full Documentation...'),
            self,
            statusTip=_('Open a TreeLine file with full documentation'))
        helpFullAct.triggered.connect(self.helpViewFull)
        self.allActions['HelpFull'] = helpFullAct

        helpAboutAct = QAction(
            _('&About TreeLine...'),
            self,
            statusTip=_('Display version info about this program'))
        helpAboutAct.triggered.connect(self.helpAbout)
        self.allActions['HelpAbout'] = helpAboutAct

        for name, action in self.allActions.items():
            icon = globalref.toolIcons.getIcon(name.lower())
            if icon:
                action.setIcon(icon)
            key = globalref.keyboardOptions[name]
            if not key.isEmpty():
                action.setShortcut(key)

    def fileNew(self):
        """Start a new blank file.
        """
        if (globalref.genOptions['OpenNewWindow']
                or self.activeControl.checkSaveChanges()):
            searchPaths = self.findResourcePaths('templates', templatePath)
            if searchPaths:
                dialog = miscdialogs.TemplateFileDialog(
                    _('New File'), _('&Select Template'), searchPaths)
                if dialog.exec_() == QDialog.Accepted:
                    self.createLocalControl(dialog.selectedPath())
                    self.activeControl.filePathObj = None
                    self.activeControl.updateWindowCaptions()
                    self.activeControl.expandRootNodes()
            else:
                self.createLocalControl()
            self.activeControl.selectRootSpot()

    def fileOpen(self):
        """Prompt for a filename and open it.
        """
        if (globalref.genOptions['OpenNewWindow']
                or self.activeControl.checkSaveChanges()):
            filters = ';;'.join((globalref.fileFilters['trlnopen'],
                                 globalref.fileFilters['all']))
            fileName, selFilter = QFileDialog.getOpenFileName(
                QApplication.activeWindow(), _('TreeLine - Open File'),
                str(self.defaultPathObj(True)), filters)
            if fileName:
                self.openFile(pathlib.Path(fileName))

    def fileOpenSample(self):
        """Open a sample file from the doc directories.
        """
        if (globalref.genOptions['OpenNewWindow']
                or self.activeControl.checkSaveChanges()):
            searchPaths = self.findResourcePaths('samples', samplePath)
            dialog = miscdialogs.TemplateFileDialog(_('Open Sample File'),
                                                    _('&Select Sample'),
                                                    searchPaths, False)
            if dialog.exec_() == QDialog.Accepted:
                self.createLocalControl(dialog.selectedPath())
                name = dialog.selectedName() + '.trln'
                self.activeControl.filePathObj = pathlib.Path(name)
                self.activeControl.updateWindowCaptions()
                self.activeControl.expandRootNodes()
                self.activeControl.imported = True

    def fileImport(self):
        """Prompt for an import type, then a file to import.
        """
        importControl = imports.ImportControl()
        structure = importControl.interactiveImport()
        if structure:
            self.createLocalControl(importControl.pathObj, structure)
            if importControl.treeLineRootAttrib:
                self.activeControl.printData.readData(
                    importControl.treeLineRootAttrib)
            self.activeControl.imported = True

    def fileQuit(self):
        """Close all windows to exit the applications.
        """
        for control in self.localControls[:]:
            control.closeWindows()

    def dataConfigDialog(self, show):
        """Show or hide the non-modal data config dialog.

        Arguments:
            show -- true if dialog should be shown, false to hide it
        """
        if show:
            if not self.configDialog:
                self.configDialog = configdialog.ConfigDialog()
                dataConfigAct = self.allActions['DataConfigType']
                self.configDialog.dialogShown.connect(dataConfigAct.setChecked)
            self.configDialog.setRefs(self.activeControl, True)
            self.configDialog.show()
        else:
            self.configDialog.close()

    def dataVisualConfig(self):
        """Show a TreeLine file to visualize the config structure.
        """
        structure = (
            self.activeControl.structure.treeFormats.visualConfigStructure(
                str(self.activeControl.filePathObj)))
        self.createLocalControl(treeStruct=structure, forceNewWindow=True)
        self.activeControl.filePathObj = pathlib.Path('structure.trln')
        self.activeControl.updateWindowCaptions()
        self.activeControl.expandRootNodes()
        self.activeControl.imported = True
        win = self.activeControl.activeWindow
        win.rightTabs.setCurrentWidget(win.outputSplitter)

    def dataSortDialog(self, show):
        """Show or hide the non-modal data sort nodes dialog.

        Arguments:
            show -- true if dialog should be shown, false to hide it
        """
        if show:
            if not self.sortDialog:
                self.sortDialog = miscdialogs.SortDialog()
                dataSortAct = self.allActions['DataSortNodes']
                self.sortDialog.dialogShown.connect(dataSortAct.setChecked)
            self.sortDialog.show()
        else:
            self.sortDialog.close()

    def dataNumberingDialog(self, show):
        """Show or hide the non-modal update node numbering dialog.

        Arguments:
            show -- true if dialog should be shown, false to hide it
        """
        if show:
            if not self.numberingDialog:
                self.numberingDialog = miscdialogs.NumberingDialog()
                dataNumberingAct = self.allActions['DataNumbering']
                self.numberingDialog.dialogShown.connect(
                    dataNumberingAct.setChecked)
            self.numberingDialog.show()
            if not self.numberingDialog.checkForNumberingFields():
                self.numberingDialog.close()
        else:
            self.numberingDialog.close()

    def toolsFindTextDialog(self, show):
        """Show or hide the non-modal find text dialog.

        Arguments:
            show -- true if dialog should be shown
        """
        if show:
            if not self.findTextDialog:
                self.findTextDialog = miscdialogs.FindFilterDialog()
                toolsFindTextAct = self.allActions['ToolsFindText']
                self.findTextDialog.dialogShown.connect(
                    toolsFindTextAct.setChecked)
            self.findTextDialog.selectAllText()
            self.findTextDialog.show()
        else:
            self.findTextDialog.close()

    def toolsFindConditionDialog(self, show):
        """Show or hide the non-modal conditional find dialog.

        Arguments:
            show -- true if dialog should be shown
        """
        if show:
            if not self.findConditionDialog:
                dialogType = conditional.FindDialogType.findDialog
                self.findConditionDialog = (conditional.ConditionDialog(
                    dialogType, _('Conditional Find')))
                toolsFindConditionAct = self.allActions['ToolsFindCondition']
                (self.findConditionDialog.dialogShown.connect(
                    toolsFindConditionAct.setChecked))
            else:
                self.findConditionDialog.loadTypeNames()
            self.findConditionDialog.show()
        else:
            self.findConditionDialog.close()

    def toolsFindReplaceDialog(self, show):
        """Show or hide the non-modal find and replace text dialog.

        Arguments:
            show -- true if dialog should be shown
        """
        if show:
            if not self.findReplaceDialog:
                self.findReplaceDialog = miscdialogs.FindReplaceDialog()
                toolsFindReplaceAct = self.allActions['ToolsFindReplace']
                self.findReplaceDialog.dialogShown.connect(
                    toolsFindReplaceAct.setChecked)
            else:
                self.findReplaceDialog.loadTypeNames()
            self.findReplaceDialog.show()
        else:
            self.findReplaceDialog.close()

    def toolsFilterTextDialog(self, show):
        """Show or hide the non-modal filter text dialog.

        Arguments:
            show -- true if dialog should be shown
        """
        if show:
            if not self.filterTextDialog:
                self.filterTextDialog = miscdialogs.FindFilterDialog(True)
                toolsFilterTextAct = self.allActions['ToolsFilterText']
                self.filterTextDialog.dialogShown.connect(
                    toolsFilterTextAct.setChecked)
            self.filterTextDialog.selectAllText()
            self.filterTextDialog.show()
        else:
            self.filterTextDialog.close()

    def toolsFilterConditionDialog(self, show):
        """Show or hide the non-modal conditional filter dialog.

        Arguments:
            show -- true if dialog should be shown
        """
        if show:
            if not self.filterConditionDialog:
                dialogType = conditional.FindDialogType.filterDialog
                self.filterConditionDialog = (conditional.ConditionDialog(
                    dialogType, _('Conditional Filter')))
                toolsFilterConditionAct = (
                    self.allActions['ToolsFilterCondition'])
                (self.filterConditionDialog.dialogShown.connect(
                    toolsFilterConditionAct.setChecked))
            else:
                self.filterConditionDialog.loadTypeNames()
            self.filterConditionDialog.show()
        else:
            self.filterConditionDialog.close()

    def toolsGenOptions(self):
        """Set general user preferences for all files.
        """
        oldAutoSaveMinutes = globalref.genOptions['AutoSaveMinutes']
        dialog = options.OptionDialog(globalref.genOptions,
                                      QApplication.activeWindow())
        dialog.setWindowTitle(_('General Options'))
        if (dialog.exec_() == QDialog.Accepted
                and globalref.genOptions.modified):
            globalref.genOptions.writeFile()
            self.recentFiles.updateOptions()
            if globalref.genOptions['MinToSysTray']:
                self.createTrayIcon()
            elif self.trayIcon:
                self.trayIcon.hide()
            autoSaveMinutes = globalref.genOptions['AutoSaveMinutes']
            for control in self.localControls:
                for window in control.windowList:
                    window.updateWinGenOptions()
                control.structure.undoList.setNumLevels()
                control.updateAll(False)
                if autoSaveMinutes != oldAutoSaveMinutes:
                    control.resetAutoSave()

    def toolsCustomShortcuts(self):
        """Show dialog to customize keyboard commands.
        """
        actions = self.activeControl.activeWindow.allActions
        dialog = miscdialogs.CustomShortcutsDialog(actions,
                                                   QApplication.activeWindow())
        dialog.exec_()

    def toolsCustomToolbars(self):
        """Show dialog to customize toolbar buttons.
        """
        actions = self.activeControl.activeWindow.allActions
        dialog = miscdialogs.CustomToolbarDialog(actions, self.updateToolbars,
                                                 QApplication.activeWindow())
        dialog.exec_()

    def updateToolbars(self):
        """Update toolbars after changes in custom toolbar dialog.
        """
        for control in self.localControls:
            for window in control.windowList:
                window.setupToolbars()

    def toolsCustomFonts(self):
        """Show dialog to customize fonts in various views.
        """
        dialog = miscdialogs.CustomFontDialog(QApplication.activeWindow())
        dialog.updateRequired.connect(self.updateCustomFonts)
        dialog.exec_()

    def toolsCustomColors(self):
        """Show dialog to customize GUI colors ans themes.
        """
        self.colorSet.showDialog(QApplication.activeWindow())

    def updateCustomFonts(self):
        """Update fonts in all windows based on a dialog signal.
        """
        self.updateAppFont()
        for control in self.localControls:
            for window in control.windowList:
                window.updateFonts()
            control.printData.setDefaultFont()
        for control in self.localControls:
            control.updateAll(False)

    def updateAppFont(self):
        """Update application default font from settings.
        """
        appFont = QFont(self.systemFont)
        appFontName = globalref.miscOptions['AppFont']
        if appFontName:
            appFont.fromString(appFontName)
        QApplication.setFont(appFont)

    def formatSelectAll(self):
        """Select all text in any currently focused editor.
        """
        try:
            QApplication.focusWidget().selectAll()
        except AttributeError:
            pass

    def helpViewBasic(self):
        """Display basic usage instructions.
        """
        if not self.basicHelpView:
            path = self.findResourceFile('basichelp.html', 'doc', docPath)
            if not path:
                QMessageBox.warning(QApplication.activeWindow(), 'TreeLine',
                                    _('Error - basic help file not found'))
                return
            self.basicHelpView = helpview.HelpView(path,
                                                   _('TreeLine Basic Usage'),
                                                   globalref.toolIcons)
        self.basicHelpView.show()

    def helpViewFull(self):
        """Open a TreeLine file with full documentation.
        """
        path = self.findResourceFile('documentation.trln', 'doc', docPath)
        if not path:
            QMessageBox.warning(QApplication.activeWindow(), 'TreeLine',
                                _('Error - documentation file not found'))
            return
        self.createLocalControl(path, forceNewWindow=True)
        self.activeControl.filePathObj = pathlib.Path('documentation.trln')
        self.activeControl.updateWindowCaptions()
        self.activeControl.expandRootNodes()
        self.activeControl.imported = True
        win = self.activeControl.activeWindow
        win.rightTabs.setCurrentWidget(win.outputSplitter)

    def helpAbout(self):
        """ Display version info about this program.
        """
        pyVersion = '.'.join([repr(num) for num in sys.version_info[:3]])
        textLines = [
            _('TreeLine version {0}').format(__version__),
            _('written by {0}').format(__author__), '',
            _('Library versions:'), '   Python:  {0}'.format(pyVersion),
            '   Qt:  {0}'.format(qVersion()),
            '   PyQt:  {0}'.format(PYQT_VERSION_STR),
            '   OS:  {0}'.format(platform.platform())
        ]
        dialog = miscdialogs.AboutDialog('TreeLine', textLines,
                                         QApplication.windowIcon(),
                                         QApplication.activeWindow())
        dialog.exec_()
Пример #16
0
    def __init__(self, _id, _viewer_id, *argv):

        if sys.platform.startswith(
                "darwin") and mp.current_process().name == "WebLCDs":
            import AppKit
            info = AppKit.NSBundle.mainBundle().infoDictionary(
            )  # @UndefinedVariable
            info["LSBackgroundOnly"] = "1"

        super(QtSingleApplication, self).__init__(*argv)

        self._id = _id
        self._viewer_id = _viewer_id
        self._activationWindow = None
        self._activateOnMessage = False

        self._outSocket = None
        self._isRunning = False
        self._server = None

        # we exclude the WebLCDs parallel process from participating any Artisan inter-app communication
        if mp.current_process().name != "WebLCDs":
            # Is there another instance running?
            self._outSocket = QLocalSocket()
            self._outSocket.connectToServer(self._id)
            self._isRunning = self._outSocket.waitForConnected(-1)
            if self._isRunning:
                # Yes, there is.
                self._outStream = QTextStream(self._outSocket)
                self._outStream.setCodec('UTF-8')
                # Is there another viewer running?
                self._outSocketViewer = QLocalSocket()
                self._outSocketViewer.connectToServer(self._viewer_id)
                self._isRunningViewer = self._outSocketViewer.waitForConnected(
                    -1)
                if self._isRunningViewer:
                    self._outStreamViewer = QTextStream(self._outSocketViewer)
                    self._outStreamViewer.setCodec('UTF-8')
                else:
                    # app is running, we announce us as viewer app
                    # First we remove existing servers of that name that might not have been properly closed as the server died
                    QLocalServer.removeServer(self._viewer_id)
                    self._outSocketViewer = None
                    self._outStreamViewer = None
                    self._inSocket = None
                    self._inStream = None
                    self._server = QLocalServer()
                    self._server.listen(self._viewer_id)
                    self._server.newConnection.connect(self._onNewConnection)
            else:
                self._isRunningViewer = False
                # No, there isn't.
                # First we remove existing servers of that name that might not have been properly closed as the server died
                QLocalServer.removeServer(self._id)
                self._outSocket = None
                self._outStream = None
                self._inSocket = None
                self._inStream = None
                self._server = QLocalServer()
                self._server.listen(self._id)
                self._server.newConnection.connect(self._onNewConnection)
Пример #17
0
class QSingleApplication(QApplication):

    messageReceived = pyqtSignal(str)

    def __init__(self, id, *argv):

        super(QSingleApplication, self).__init__(*argv)
        self._id = id
        self._activationWindow = None
        self._activateOnMessage = False
        self._server = None

        # Is there another instance running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._outSocket.error.connect(self.handleError)
        self._isRunning = self._outSocket.waitForConnected()

        if self._isRunning:
            # Yes, there is.
            self._outStream = QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            # No, there isn't.
            self._outSocket = None
            self._outStream = None
            self._inSocket = None
            self._inStream = None
            self._server = QLocalServer()
            self._server.listen(self._id)
            self._server.newConnection.connect(self._onNewConnection)
            self.aboutToQuit.connect(self.removeServer)

    def handleError(self, msg):
        print(msg)

    def server(self):
        return self._server

    def isRunning(self):
        return self._isRunning

    def id(self):
        return self._id

    def activationWindow(self):
        return self._activationWindow

    def setActivationWindow(self, activationWindow, activateOnMessage=True):
        self._activationWindow = activationWindow
        self._activateOnMessage = activateOnMessage

    def activateWindow(self):
        if not self._activationWindow:
            print("No registered ActivationWindow")
            return
        # Unfortunately this *doesn't* do much of any use, as it won't
        # bring the window to the foreground under KDE... sigh.
        self._activationWindow.setWindowState(
            self._activationWindow.windowState() & ~Qt.WindowMinimized)
        self._activationWindow.raise_()
        self._activationWindow.requestActivate()

    def sendMessage(self, msg, msecs=5000):
        if not self._outStream:
            return False
        self._outStream << msg << ''
        if not self._outSocket.waitForBytesWritten(msecs):
            raise RuntimeError("Bytes not written within %ss" %
                               (msecs / 1000.))

    def _onNewConnection(self):
        if self._inSocket:
            self._inSocket.readyRead.disconnect(self._onReadyRead)
        self._inSocket = self._server.nextPendingConnection()
        if not self._inSocket:
            return
        self._inStream = QTextStream(self._inSocket)
        self._inStream.setCodec('UTF-8')
        self._inSocket.readyRead.connect(self._onReadyRead)
        if self._activateOnMessage:
            self.activateWindow()

    def _onReadyRead(self):
        while True:
            msg = self._inStream.readLine()
            if not msg:
                break
            print("Message received")
            self.messageReceived.emit(msg)

    def removeServer(self):
        self._server.close()
        self._server.removeServer(self._id)
Пример #18
0
class QSingleApplication(QApplication):

    messageReceived = pyqtSignal(str)

    def __init__(self, *args, **kwargs):
        super(QSingleApplication, self).__init__(*args, **kwargs)
        appid = QApplication.applicationFilePath().lower().split("/")[-1]
        self._socketName = "qtsingleapp-" + appid
        print("socketName", self._socketName)
        self._activationWindow = None
        self._activateOnMessage = False
        self._socketServer = None
        self._socketIn = None
        self._socketOut = None
        self._running = False

        # 先尝试连接
        self._socketOut = QLocalSocket(self)
        self._socketOut.connectToServer(self._socketName)
        self._socketOut.error.connect(self.handleError)
        self._running = self._socketOut.waitForConnected()

        if not self._running:  # 程序未运行
            self._socketOut.close()
            del self._socketOut
            self._socketServer = QLocalServer(self)
            self._socketServer.listen(self._socketName)
            self._socketServer.newConnection.connect(self._onNewConnection)
            self.aboutToQuit.connect(self.removeServer)

    def handleError(self, message):
        print("handleError message: ", message)

    def isRunning(self):
        return self._running

    def activationWindow(self):
        return self._activationWindow

    def setActivationWindow(self, activationWindow, activateOnMessage=True):
        self._activationWindow = activationWindow
        self._activateOnMessage = activateOnMessage

    def activateWindow(self):
        if not self._activationWindow:
            return
        self._activationWindow.setWindowState(
            self._activationWindow.windowState() & ~Qt.WindowMinimized)
        self._activationWindow.raise_()
        self._activationWindow.activateWindow()

    def sendMessage(self, message, msecs=5000):
        if not self._socketOut:
            return False
        if not isinstance(message, bytes):
            message = str(message).encode()
        self._socketOut.write(message)
        if not self._socketOut.waitForBytesWritten(msecs):
            raise RuntimeError("Bytes not written within %ss" %
                               (msecs / 1000.))
        return True

    def _onNewConnection(self):
        if self._socketIn:
            self._socketIn.readyRead.disconnect(self._onReadyRead)
        self._socketIn = self._socketServer.nextPendingConnection()
        if not self._socketIn:
            return
        self._socketIn.readyRead.connect(self._onReadyRead)
        if self._activateOnMessage:
            self.activateWindow()

    def _onReadyRead(self):
        while 1:
            message = self._socketIn.readLine()
            if not message:
                break
            print("Message received: ", message)
            self.messageReceived.emit(message.data().decode())

    def removeServer(self):
        self._socketServer.close()
        self._socketServer.removeServer(self._socketName)
Пример #19
0
 def _remove_server(self):
     """Remove an existing server."""
     ok = QLocalServer.removeServer(SOCKETNAME)
     if not ok:
         raise IPCError(
             "Error while removing server {}!".format(SOCKETNAME))
Пример #20
0
class MainWindow(QtWidgets.QMainWindow):
    """
    A main window class.
    """
    def __init__(self, *args, **kwargs):
        """
        A function for connecting actions and creating a main window.
        """
        super(MainWindow, self).__init__(*args, **kwargs)
        # absolute path to icon:
        path_to_main = os.path.abspath(os.getcwd())
        self.icon_path = os.path.join(path_to_main, 'atomize/main', 'Icon.png')
        self.setWindowIcon(QIcon(self.icon_path))

        #self.destroyed.connect(MainWindow._on_destroyed)         # connect some actions to exit
        self.destroyed.connect(
            lambda: self._on_destroyed())  # connect some actions to exit
        # Load the UI Page
        uic.loadUi('atomize/main/gui/main_window.ui', self)  # Design file

        # important attribures
        if len(sys.argv) > 1 and sys.argv[1] != '':  # for bash option
            self.script = sys.argv[1]
            self.open_file(self.script)
        elif len(sys.argv) == 1:
            self.script = ''  # for not opened script
        self.test_flag = 0  # flag for not running script if test is failed
        self.flag_opened_script_changed = 0  # flag for saving changes in the opened script
        self.path = os.path.join(path_to_main, 'atomize/tests')
        # Connection of different action to different Menus and Buttons
        self.tabwidget.tabBar().setTabTextColor(0, QColor(193, 202, 227))
        self.tabwidget.tabBar().setTabTextColor(1, QColor(193, 202, 227))
        self.button_open.clicked.connect(self.open_file_dialog)
        self.button_open.setStyleSheet(
            "QPushButton {border-radius: 4px; background-color: rgb(63, 63, 97);\
         border-style: outset; color: rgb(193, 202, 227);}\
          QPushButton:pressed {background-color: rgb(211, 194, 78); ; border-style: inset}"
        )
        self.button_edit.clicked.connect(self.edit_file)
        self.button_edit.setStyleSheet(
            "QPushButton {border-radius: 4px; background-color: rgb(63, 63, 97);\
         border-style: outset; color: rgb(193, 202, 227);}\
          QPushButton:pressed {background-color: rgb(211, 194, 78); ; border-style: inset}"
        )
        self.button_test.clicked.connect(self.test)
        self.button_test.setStyleSheet(
            "QPushButton {border-radius: 4px; background-color: rgb(63, 63, 97);\
         border-style: outset; color: rgb(193, 202, 227);}\
          QPushButton:pressed {background-color: rgb(211, 194, 78); ; border-style: inset}"
        )
        self.button_reload.clicked.connect(self.reload)
        self.button_reload.setStyleSheet(
            "QPushButton {border-radius: 4px; background-color: rgb(63, 63, 97);\
         border-style: outset; color: rgb(193, 202, 227); }\
          QPushButton:pressed {background-color: rgb(211, 194, 78); ; border-style: inset}"
        )
        self.button_start.clicked.connect(self.start_experiment)
        self.button_start.setStyleSheet(
            "QPushButton {border-radius: 4px; background-color: rgb(63, 63, 97);\
         border-style: outset; color: rgb(193, 202, 227);}\
          QPushButton:pressed {background-color: rgb(211, 194, 78); border-style: inset}"
        )
        self.button_help.clicked.connect(self.help)
        self.button_help.setStyleSheet(
            "QPushButton {border-radius: 4px; background-color: rgb(63, 63, 97);\
         border-style: outset; color: rgb(193, 202, 227);}\
          QPushButton:pressed {background-color: rgb(211, 194, 78); border-style: inset}"
        )
        self.button_quit.clicked.connect(lambda: self.quit())
        self.button_quit.setStyleSheet(
            "QPushButton {border-radius: 4px; background-color: rgb(63, 63, 97);\
         border-style: outset; color: rgb(193, 202, 227);}\
          QPushButton:pressed {background-color: rgb(211, 194, 78); border-style: inset}"
        )
        self.textEdit.setStyleSheet(
            "QPlainTextEdit {background-color: rgb(42, 42, 64); color: rgb(211, 194, 78); }\
         QScrollBar:vertical {background-color: rgb(42, 42, 64);}")
        self.textEdit.textChanged.connect(self.save_edited_text)
        self.text_errors.setStyleSheet(
            "QPlainTextEdit {background-color: rgb(42, 42, 64); color: rgb(211, 194, 78); }"
        )
        self.text_errors.setCenterOnScroll(True)
        self.text_errors.ensureCursorVisible()

        # Liveplot tab setting
        self.dockarea = DockArea()
        self.namelist = NameList(self)
        self.tab_liveplot.setStyleSheet(
            "background-color: rgb(42, 42, 64); color: rgb(211, 194, 78); ")
        self.gridLayout_tab_liveplot.setColumnMinimumWidth(0, 200)
        self.gridLayout_tab_liveplot.setColumnStretch(1, 2000)
        self.gridLayout_tab_liveplot.addWidget(self.namelist, 0, 0)
        self.gridLayout_tab_liveplot.setAlignment(self.namelist,
                                                  QtConst.AlignLeft)
        self.gridLayout_tab_liveplot.addWidget(self.dockarea, 0, 1)
        #self.gridLayout_tab_liveplot.setAlignment(self.dockarea, QtConst.AlignRight)
        self.namelist.setStyleSheet(
            "background-color: rgb(42, 42, 64); color: rgb(211, 194, 78); border: 2px solid rgb(40, 30, 45)"
        )
        self.namelist.namelist_view.setStyleSheet(
            "QListView::item:selected:active {background-color: rgb(63, 63, 97);\
            color: rgb(211, 194, 78); } QListView::item:hover {background-color: rgb(48, 48, 75); }"
        )
        self.namelist.namelist_view.setStyleSheet(
            "QMenu::item:selected {background-color: rgb(48, 48, 75);  }")

        # Liveplot server settings
        self.server = QLocalServer()
        self.server.removeServer('LivePlot')
        self.server.listen('LivePlot')
        self.server.newConnection.connect(self.accept)
        self.bytes = bytearray()
        self.target_size = 0
        self.meta = None
        self.insert_dock_right = True
        self.conns = []
        self.shared_mems = []
        signal.signal(signal.SIGINT, self.close)

        # configuration data
        path_config_file = os.path.join(path_to_main, 'atomize/config.ini')
        config = configparser.ConfigParser()
        config.read(path_config_file)

        # for running different processes using QProcess
        self.process = QtCore.QProcess(self)
        self.process_text_editor = QtCore.QProcess(self)
        self.process_python = QtCore.QProcess(self)

        # check where we are
        self.system = platform.system()
        if self.system == 'Windows':
            self.process_text_editor.setProgram(
                str(config['DEFAULT']['editorW']))
            self.process.setProgram('python.exe')
            self.process_python.setProgram('python.exe')
        elif self.system == 'Linux':
            self.editor = str(config['DEFAULT']['editor'])
            if self.editor == 'nano' or self.editor == 'vi':
                self.process_text_editor.setProgram('xterm')
            else:
                self.process_text_editor.setProgram(
                    str(config['DEFAULT']['editor']))
            self.process.setProgram('python3')
            self.process_python.setProgram('python3')

        self.process.finished.connect(self.on_finished_checking)
        self.process_python.finished.connect(self.on_finished_script)

    ############################################## Liveplot Functions

    def close(self, sig=None, frame=None):
        #print('closing')
        for conn in self.conns:
            conn.close()
        for shm in self.shared_mems:
            shm.detach()
        self._on_destroyed()
        #QApplication.instance().exit()

    def accept(self):
        logging.debug('connection accepted')
        conn = self.server.nextPendingConnection()
        conn.waitForReadyRead()
        key = str(conn.read(36).decode())
        memory = QSharedMemory()
        memory.setKey(key)
        memory.attach()
        logging.debug('attached to memory %s with size %s' %
                      (key, memory.size()))
        #11-04-2021; Should be uncommented in case of problems
        #atexit.register(memory.detach)
        self.conns.append(conn)
        self.shared_mems.append(memory)
        conn.readyRead.connect(lambda: self.read_from(conn, memory))
        conn.disconnected.connect(memory.detach)
        conn.write(b'ok')

    # noinspection PyNoneFunctionAssignment
    def read_from(self, conn, memory):
        logging.debug('reading data')
        self.meta = json.loads(conn.read(300).decode())
        if self.meta['arrsize'] != 0:
            memory.lock()
            raw_data = memory.data()
            if raw_data != None:
                ba = raw_data[:self.meta['arrsize']]
                arr = np.frombuffer(memoryview(ba), dtype=self.meta['dtype'])
                memory.unlock()
                conn.write(b'ok')
                arr = arr.reshape(self.meta['shape']).copy()
            else:
                arr = None
        else:
            arr = None
        self.do_operation(arr)
        if conn.bytesAvailable():
            self.read_from(conn, memory)

    def do_operation(self, arr=None):
        def clear(name):
            self.namelist[name].clear()

        def close(name):
            self.namelist[name].close()

        def remove(name):
            del self.namelist[name]

        meta = self.meta
        operation = meta['operation']
        name = meta['name']

        if name in self.namelist:
            pw = self.namelist[name]
            if pw.closed:
                pw.closed = False
                self.dockarea.addDock(pw)

        elif name == "*":
            if operation == 'clear':
                list(map(clear, list(self.namelist.keys())))
            elif operation == 'close':
                list(map(close, list(self.namelist.keys())))
            elif operation == 'remove':
                list(map(remove, list(self.namelist.keys())))
            return
        else:
            if operation in ('clear', 'close', 'remove', 'none'):
                return
            pw = self.add_new_plot(meta['rank'], name)

        if operation == 'clear':
            pw.clear()
        elif operation == 'close':
            pw.close()
        elif operation == 'none':
            pass
        elif operation == 'remove':
            del self.namelist[name]

        elif operation == 'plot_y':
            start_step = meta['start_step']
            label = meta['label']
            if start_step is not None:
                x0, dx = start_step
                nx = len(arr)
                xs = np.linspace(x0, x0 + (nx - 1) * dx, nx)
                pw.plot(xs, arr, name=label, scatter='False')
            else:
                pw.plot(arr, name=label, scatter='False')

        elif operation == 'plot_xy':
            label = meta['label']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']
            scat = meta['Scatter']
            taxis = meta['TimeAxis']
            pw.plot(arr[0], arr[1], parametric=True, name=label, xname=xnam, xscale =xscal,\
             yname=ynam, yscale =yscal, scatter=scat, timeaxis=taxis)

        elif operation == 'plot_z':
            start_step = meta['start_step']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']
            znam = meta['Zname']
            zscal = meta['Z']
            if start_step is not None:
                (x0, dx), (y0, dy) = start_step
                pw.setAxisLabels(xname=xnam, xscale =xscal, yname=ynam, yscale =yscal,\
                zname=znam, zscale =zscal)
                pw.setImage(arr,
                            pos=(x0, y0),
                            scale=(dx, dy),
                            axes={
                                'y': 0,
                                'x': 1
                            })
            else:
                pw.setAxisLabels(xname=xnam, xscale =xscal, yname=ynam, yscale =yscal,\
                 zname=znam, zscale =zscal)
                pw.setImage(arr, axes={'y': 0, 'x': 1})

        elif operation == 'append_y':
            label = meta['label']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']
            scat = meta['Scatter']
            taxis = meta['TimeAxis']

            xs, ys = pw.get_data(label)
            new_ys = list(ys)
            new_ys.append(meta['value'])
            start_step = meta['start_step']
            if start_step is not None:
                x0, dx = start_step
                nx = len(new_ys)
                xs = np.linspace(x0, x0 + (nx - 1) * dx, nx)
                pw.plot(xs, new_ys, name=label, xname=xnam, xscale =xscal, yname=ynam,\
                 yscale =yscal, scatter=scat, timeaxis=taxis)
            else:
                pw.plot(new_ys, name=label, xname=xnam, xscale =xscal, yname=ynam,\
                 yscale =yscal, scatter=scat, timeaxis=taxis)

        elif operation == 'append_xy':
            label = meta['label']
            xs, ys = pw.get_data(label)
            xn, yn = meta['value']
            new_xs = list(xs)
            new_xs.append(xn)
            new_ys = list(ys)
            new_ys.append(yn)
            pw.plot(new_xs,
                    new_ys,
                    parametric=True,
                    name=label,
                    scatter='False')

        elif operation == 'append_z':
            image = pw.get_data()
            if image is None:
                image = np.array([arr])
            else:
                try:
                    image = np.vstack((np.transpose(image), [arr]))
                except ValueError:
                    image = np.array([arr])
            start_step = meta['start_step']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']
            znam = meta['Zname']
            zscal = meta['Z']
            if start_step is not None:
                (x0, dx), (y0, dy) = start_step
                pw.setAxisLabels(xname=xnam, xscale =xscal, yname=ynam, yscale =yscal,\
                 zname=znam, zscale =zscal)
                pw.setImage(image,
                            pos=(x0, y0),
                            scale=(dx, dy),
                            axes={
                                'y': 0,
                                'x': 1
                            })
            else:
                pw.setAxisLabels(xname=xnam,
                                 xscale=xscal,
                                 yname=ynam,
                                 yscale=yscal)
                pw.setImage(image, axes={'y': 0, 'x': 1})

        elif operation == 'label':
            pw.setTitle(meta['value'])

    def add_new_plot(self, rank, name):
        pw = widgets.get_widget(rank, name)
        self.add_plot(pw)
        self.namelist[name] = pw
        return pw

    def add_plot(self, pw):
        self.insert_dock_right = not self.insert_dock_right
        self.dockarea.addDock(pw,
                              position=['bottom',
                                        'bottom'][self.insert_dock_right])
        #print(['bottom', 'right'][self.insert_dock_right])
        #self.dockarea.moveDock(pw, 'above', self.dock_list[-1])   ## move d6 to stack on top of d4

    #####################################################

    def _on_destroyed(self):
        """
        A function to do some actions when the main window is closing.
        """
        self.process_python.close()

    def quit(self):
        """
        A function to quit the programm
        """
        self.process_python.terminate()
        sys.exit()
        ####
        #### QProcess: Destroyed while process ("python3") is still running.
        ####

    def start_experiment(self):
        """
        A function to run an experimental script using python.exe.
        """
        if self.script != '':
            stamp = os.stat(self.script).st_mtime
        else:
            self.text_errors.appendPlainText(
                'No experimental script is opened')
            return

        self.test()
        exec_code = self.process.waitForFinished()

        if self.test_flag == 1:
            self.text_errors.appendPlainText(
                "Experiment cannot be started, since test is not passed")
            return  # stop current function
        elif self.test_flag == 0 and exec_code == True:
            self.process_python.setArguments([self.script])
            self.process_python.start()

    def message_box_clicked(self, btn):
        """
        Message Box fow warning
        """
        if btn.text() == "Discrad and Run Experiment":
            self.start_experiment()
        elif btn.text() == "Update Script":
            self.reload()
        else:
            return

    def test(self):
        """
        A function to run a syntax check using pylint.
        """

        if self.script != '':
            stamp = os.stat(self.script).st_mtime
        else:
            self.text_errors.appendPlainText(
                'No experimental script is opened')
            return

        if stamp != self.cached_stamp and self.flag_opened_script_changed == 0:
            self.cached_stamp = stamp
            message = QMessageBox(self)
            # Message Box for warning of updated file
            message.setWindowTitle("Your script has been changed!")
            message.setStyleSheet(
                "QWidget { background-color : rgb(42, 42, 64); color: rgb(211, 194, 78); }"
            )
            message.addButton(
                QtWidgets.QPushButton('Discrad and Run Experiment'),
                QtWidgets.QMessageBox.YesRole)
            message.addButton(QtWidgets.QPushButton('Update Script'),
                              QtWidgets.QMessageBox.NoRole)
            message.setText("Your experimental script has been changed   ")
            message.show()
            message.buttonClicked.connect(
                self.message_box_clicked
            )  # connect function clicked to button; get the button name
            return  # stop current function

        #self.text_errors.appendPlainText("Testing... Please, wait!")
        #self.process.setArguments(['--errors-only', self.script])
        self.process.setArguments([self.script, 'test'])
        self.process.start()

    def reload(self):
        """
        A function to reload an experimental script.
        """
        self.cached_stamp = os.stat(self.script).st_mtime
        text = open(self.script).read()
        self.textEdit.setPlainText(text)

    def on_finished_checking(self):
        """
        A function to add the information about errors found during syntax checking
        to a dedicated text box in the main window of the programm.
        """
        #text = self.process.readAllStandardOutput().data().decode()
        #if text == '':
        #    self.text_errors.appendPlainText("No errors are found!")
        #else:
        #    self.text_errors.appendPlainText(text)
        #    self.text_errors.verticalScrollBar().setValue(self.text_errors.verticalScrollBar().maximum())

        # Version for real tests
        text = self.process.readAllStandardOutput().data().decode()
        text_errors_script = self.process.readAllStandardError().data().decode(
        )
        if text_errors_script == '':
            # if text == '' and text_errors_script == '':
            self.text_errors.appendPlainText("No errors are found")
            self.test_flag = 0
        elif text_errors_script != '':
            self.test_flag = 1
            self.text_errors.appendPlainText(text_errors_script)
            #self.text_errors.verticalScrollBar().setValue(self.text_errors.verticalScrollBar().maximum())

    def on_finished_script(self):
        """
        A function to add the information about errors found during syntax checking to a dedicated text box in the main window of the programm.
        """
        text = self.process_python.readAllStandardOutput().data().decode()
        text_errors_script = self.process_python.readAllStandardError().data(
        ).decode()
        if text_errors_script == '':
            #if text == '' and text_errors_script == '':
            self.text_errors.appendPlainText("Script done!")
        elif text_errors_script != '':
            self.text_errors.appendPlainText("Script done!")
            self.text_errors.appendPlainText(text_errors_script)
            #self.text_errors.verticalScrollBar().setValue(self.text_errors.verticalScrollBar().maximum())

    def help(self):
        """
        A function to open a documentation
        """
        pass

    def edit_file(self):
        """
        A function to open an experimental script in a text editor.
        """
        if self.system == 'Linux':
            if self.editor == 'nano':
                self.process_text_editor.setArguments(
                    ['-e', 'nano', self.script])
            elif self.editor == 'vi':
                self.process_text_editor.setArguments(
                    ['-e', 'vi', self.script])
            else:
                self.process_text_editor.setArguments([self.script])
        elif self.system == 'Windows':
            self.process_text_editor.setArguments([self.script])
        self.process_text_editor.start()

    def open_file(self, filename):
        """
        A function to open an experimental script.
        :param filename: string
        """
        self.cached_stamp = os.stat(filename).st_mtime
        text = open(filename).read()
        self.path = os.path.dirname(
            filename)  # for memorizing the path to the last used folder
        self.script = filename
        self.textEdit.setPlainText(text)

    def save_file(self, filename):
        """
        A function to save a new experimental script.
        :param filename: string
        """
        with open(filename, 'w') as file:
            file.write(self.textEdit.toPlainText())

        self.cached_stamp = os.stat(filename).st_mtime
        self.script = filename

    def open_file_dialog(self):
        """
        A function to open a new window for choosing an experimental script.
        """
        filedialog = QFileDialog(self, 'Open File', directory = self.path, filter ="python (*.py)",\
            options=QtWidgets.QFileDialog.DontUseNativeDialog)
        # use QFileDialog.DontUseNativeDialog to change directory
        filedialog.setStyleSheet(
            "QWidget { background-color : rgb(42, 42, 64); color: rgb(211, 194, 78);}"
        )
        filedialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
        filedialog.fileSelected.connect(self.open_file)
        filedialog.show()

    def save_file_dialog(self):
        """
        A function to open a new window for choosing a name for a new experimental script.
        """
        filedialog = QFileDialog(self, 'Save File', directory = self.path, filter ="python (*.py)",\
            options=QtWidgets.QFileDialog.DontUseNativeDialog)
        filedialog.setAcceptMode(QFileDialog.AcceptSave)
        # use QFileDialog.DontUseNativeDialog to change directory
        filedialog.setStyleSheet(
            "QWidget { background-color : rgb(42, 42, 64); color: rgb(211, 194, 78);}"
        )
        filedialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
        filedialog.fileSelected.connect(self.save_file)
        filedialog.show()

    def save_edited_text(self):
        if self.script:
            self.flag_opened_script_changed = 1
            with open(self.script, 'w') as file:
                file.write(self.textEdit.toPlainText())
        else:
            self.flag_opened_script_changed = 1
            if self.textEdit.toPlainText(
            ) != '':  # save file dialog will be opened after at least one character is added
                self.save_file_dialog()

    @QtCore.pyqtSlot(str)
    def add_error_message(self, data):
        """
        A function for adding an error message to a dedicated text box in the main window of the programm;
        This function runs when Helper.changedSignal.emit(str) is emitted.
        :param data: string
        """
        self.text_errors.appendPlainText(str(data))

        if data == 'Script stopped':

            path_to_main = os.path.abspath(os.getcwd())
            lib_path = os.path.join(path_to_main, 'atomize/general_modules',
                                    'libspinapi.so')
            lib_path2 = os.path.join(path_to_main, 'atomize/general_modules',
                                     'spinapi64.dll')

            if os.path.exists(lib_path) == False and os.path.exists(
                    lib_path2) == False:
                self.process_python.close()
            else:
                # check on windows?!
                import atomize.device_modules.PB_ESR_500_pro as pb_pro

                pb = pb_pro.PB_ESR_500_Pro()
                pb.pulser_stop()

                # AWG
                #hCard = spcm_hOpen (create_string_buffer (b'/dev/spcm0'))
                #if hCard == None:
                #    sys.stdout.write("no card found...\n")
                #    exit ()

                #spcm_dwSetParam_i32 (hCard, SPC_M2CMD, M2CMD_CARD_STOP)

                # clean up
                #spcm_vClose (hCard)
                ###

                self.process_python.close()
Пример #21
0
class SingleApplication(QtWidgets.QApplication):
    '''
    Inheriting from QApplication, executing main App instead.
    Watching whether the app is already running.
    If so, quit befor execution.
    '''
    messageReceived = QtCore.pyqtSignal(str)

    def __init__(self, id, *argv):

        super(SingleApplication, self).__init__(*argv)
        self._id = id
        self._activationWindow = None
        self._activateOnMessage = False

        # Check if another instance is running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected()

        if self._isRunning:
            self._outStream = QtCore.QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            self._outSocket = None
            self._outStream = None
            self._inSocket = None
            self._inStream = None
            self._server = QLocalServer()
            self._server.removeServer(self._id)  # if existing after crash-exit
            self._server.listen(self._id)
            self._server.newConnection.connect(self._onNewConnection)

    def isRunning(self):
        return self._isRunning

    def id(self):
        return self._id

    def activationWindow(self):
        return self._activationWindow

    def setActivationWindow(self, activationWindow, activateOnMessage=True):
        self._activationWindow = activationWindow
        self._activateOnMessage = activateOnMessage

    def activateWindow(self):
        if not self._activationWindow:
            return
        self._activationWindow.setWindowState(
            self._activationWindow.windowState() & ~QtCore.Qt.WindowMinimized)
        self._activationWindow.show()
        self._activationWindow.activateWindow()

    def sendMessage(self, msg):
        if not self._outStream:
            return False
        self._outStream << msg << '\n'
        self._outStream.flush()
        return self._outSocket.waitForBytesWritten()

    def _onNewConnection(self):
        if self._inSocket:
            self._inSocket.readyRead.disconnect(self._onReadyRead)
        self._inSocket = self._server.nextPendingConnection()
        if not self._inSocket:
            return
        self._inStream = QtCore.QTextStream(self._inSocket)
        self._inStream.setCodec('UTF-8')
        self._inSocket.readyRead.connect(self._onReadyRead)
        if self._activateOnMessage:
            self.activateWindow()

    def _onReadyRead(self):
        while True:
            msg = self._inStream.readLine()
            if not msg:
                break
            self.messageReceived.emit(msg)
Пример #22
0
   def __init__(self, parent = None):
      QDialog.__init__(self, parent)
      
      # If a Nemu instance is already running, this is as far as we go
      self.connectToRunning()
      
      self.holdOpen = False
      self.menuItems = []
      self.allItems = []
      self.favorites = []
      self.currentItem = None
      self.menuFile = os.path.expanduser('~/.nemu/menu')
      self.favoritesFile = os.path.expanduser('~/.nemu/favorites')
      # NOTE: If you change this, also update migrate-settings
      self.settingsFile = os.path.expanduser('~/.nemu/settings')
      self.initSettings()

      self.server = QLocalServer()
      self.server.newConnection.connect(self.handleConnection)
      QLocalServer.removeServer('nemuSocket')
      self.server.listen('nemuSocket')
      
      self.configDir = os.path.expanduser('~/.nemu')
      if not os.path.isdir(self.configDir):
         os.mkdir(self.configDir)
      self.menuItems = self.loadConfig(self.menuFile, self.menuItems)
      self.favorites = self.loadConfig(self.favoritesFile, self.favorites)
      # Don't load directly into self.settings so we can add new default values as needed
      try:
         tempSettings = self.loadConfig(self.settingsFile, self.settings)
         for key, value in tempSettings.items():
            self.settings[key] = value
      except SystemError:
         print('ERROR: Failed to load settings. You may need to run migrate-settings.')
         raise
      # This should never happen, but unfortunately bugs do, so clean up orphaned items.
      # We need to do this because these items won't show up in the UI, but may interfere with
      # merges if they duplicate something that is being merged in.
      self.menuItems[:] = [i for i in self.menuItems if i.parent == None or i.parent in self.menuItems]
      # Look for broken icon paths
      needSave = False
      for i in self.menuItems + self.favorites:
          if not os.path.exists(i.icon):
              i.findIcon()
              needSave = True
      if needSave:
         self.saveMenu()


      for i in self.menuItems:
         if not hasattr(i, 'imported'):
            i.imported = False
      
      self.setupUI()
      
      self.setContextMenuPolicy(Qt.ActionsContextMenu)
      self.createMenu(self)
      
      self.refresh(False)
      
      if len(self.menuItems) == 0:
         self.firstRun()
      
      self.show()
      
      self.keepaliveTimer = QTimer(self)
      self.keepaliveTimer.timeout.connect(self.keepalive)
      self.keepaliveTimer.start(60000)
Пример #23
0
class QSingleApplication(QApplication):
    # https://github.com/PyQt5/PyQt/blob/master/Demo/Lib/Application.py
    messageReceived = pyqtSignal(str)

    def __init__(self, *args, **kwargs):
        # logger.debug("{} init...".format(self.__class__.__name__))
        super(QSingleApplication, self).__init__(*args, **kwargs)
        # 使用路径作为appid
        # appid = QApplication.applicationFilePath().lower().split("/")[-1]
        # 使用固定的appid
        appid = "SHL_LHX_Wallet"
        # logger.debug("{} appid name: {}".format(self.__class__.__name__,appid))
        # self._socketName = "qtsingleapp-" + appid
        self._socketName = appid
        # print("socketName", self._socketName)
        self._activationWindow = None
        self._activateOnMessage = False
        self._socketServer = None
        self._socketIn = None
        self._socketOut = None
        self._running = False

        # 先尝试连接
        self._socketOut = QLocalSocket(self)
        self._socketOut.connectToServer(self._socketName)
        self._socketOut.error.connect(self.handleError)
        self._running = self._socketOut.waitForConnected()
        # logger.debug("local socket connect error: {}".format(self._socketOut.errorString()))

        if not self._running:  # 程序未运行
            # logger.debug("start init QLocalServer.")
            self._socketOut.close()
            del self._socketOut
            self._socketServer = QLocalServer(self)
            # 设置连接权限
            self._socketServer.setSocketOptions(QLocalServer.UserAccessOption)
            self._socketServer.listen(self._socketName)
            self._socketServer.newConnection.connect(self._onNewConnection)
            self.aboutToQuit.connect(self.removeServer)
            # logger.debug("QLocalServer finished init.")

    def handleError(self, message):
        print("handleError message: ", message)

    def isRunning(self):
        return self._running

    def activationWindow(self):
        return self._activationWindow

    def setActivationWindow(self, activationWindow, activateOnMessage=True):
        self._activationWindow = activationWindow
        self._activateOnMessage = activateOnMessage

    def activateWindow(self):
        if not self._activationWindow:
            return
        self._activationWindow.setWindowState(
            self._activationWindow.windowState() & ~Qt.WindowMinimized)
        self._activationWindow.raise_()
        self._activationWindow.activateWindow()
        # 增加了显示功能。
        # self._activationWindow.show()

    def sendMessage(self, message, msecs=5000):
        if not self._socketOut:
            return False
        if not isinstance(message, bytes):
            message = str(message).encode()
        self._socketOut.write(message)
        if not self._socketOut.waitForBytesWritten(msecs):
            raise RuntimeError("Bytes not written within %ss" %
                               (msecs / 1000.))
        return True

    def _onNewConnection(self):
        if self._socketIn:
            self._socketIn.readyRead.disconnect(self._onReadyRead)
        self._socketIn = self._socketServer.nextPendingConnection()
        if not self._socketIn:
            return
        self._socketIn.readyRead.connect(self._onReadyRead)
        if self._activateOnMessage:
            self.activateWindow()

    def _onReadyRead(self):
        while 1:
            message = self._socketIn.readLine()
            if not message:
                break
            # print("Message received: ", message)
            self.messageReceived.emit(message.data().decode())

    def removeServer(self):
        if self._socketServer is not None:
            self._socketServer.close()
            self._socketServer.removeServer(self._socketName)
Пример #24
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setStyleSheet("background-color: rgb(24, 25, 26); color: rgb(255, 170, 0); ") 
        self.setWindowTitle("Liveplot - Plotting dashboard!")
        self.setWindowIcon(QIcon('icon.ico'))
        self.dockarea = DockArea()
        self.setCentralWidget(self.dockarea)
        self.namelist = NameList(self)
        self.addDockWidget(QtConst.LeftDockWidgetArea, self.namelist)
        self.server = QLocalServer()
        self.server.removeServer('LivePlot')
        self.server.listen('LivePlot')
        self.server.newConnection.connect(self.accept)
        self.bytes = bytearray()
        self.target_size = 0
        self.meta = None
        self.insert_dock_right = True
        self.conns = []
        self.shared_mems = []
        signal.signal(signal.SIGINT, self.close)


    def close(self, sig=None, frame=None):
        print('closing')
        for conn in self.conns:
            conn.close()
        for shm in self.shared_mems:
            shm.detach()
        QApplication.instance().exit()


    def accept(self):
        logging.debug('connection accepted')
        conn = self.server.nextPendingConnection()
        conn.waitForReadyRead()
        key = str(conn.read(36).decode())
        memory = QSharedMemory()
        memory.setKey(key)
        memory.attach()
        logging.debug('attached to memory %s with size %s'%(key, memory.size()))
        atexit.register(memory.detach)
        self.conns.append(conn)
        self.shared_mems.append(memory)
        conn.readyRead.connect(lambda: self.read_from(conn, memory))
        conn.disconnected.connect(memory.detach)
        conn.write(b'ok')

    # noinspection PyNoneFunctionAssignment
    def read_from(self, conn, memory):
        logging.debug('reading data')
        self.meta = json.loads(conn.read(300).decode())
        if self.meta['arrsize'] != 0:
            memory.lock()
            ba = memory.data()[0:self.meta['arrsize']]
            arr = np.frombuffer(memoryview(ba))
            memory.unlock()
            conn.write(b'ok')
            arr = arr.reshape(self.meta['shape']).copy()
        else:
            arr = None
        self.do_operation(arr)
        if conn.bytesAvailable():
            self.read_from(conn, memory)


    #     if not self.target_size:
    #         self.meta = conn._socket.recv_json()
    #         self.target_size = self.meta['arrsize']
    #     if self.target_size > 0:
    #         n = self.target_size - len(self.bytes)
    #         data = bytearray(conn.read(n))
    #         self.bytes.extend(data)
    #     if len(self.bytes) == self.target_size:
    #         self.process_bytes()
    #     if conn.bytesAvailable():
    #         self.read_from(conn)
    #
    # def process_bytes(self):
    #     self.target_size = 0
    #     if len(self.bytes) > 0:
    #         arr = np.frombuffer(buffer(self.bytes), dtype=self.meta['dtype'])
    #         try:
    #             arr.resize(self.meta['shape'])
    #         except ValueError:
    #             arr = arr.reshape(self.meta['shape'])
    #     else:
    #         arr = None
    #     self.bytes = bytearray()
    #     self.do_operation(arr)

    def do_operation(self, arr=None):
        def clear(name):
            self.namelist[name].clear()

        def close(name):
            self.namelist[name].close()

        def remove(name):
            del self.namelist[name]

        meta = self.meta
        operation = meta['operation']
        name = meta['name']

        if name in self.namelist:
            pw = self.namelist[name]
            if pw.closed:
                pw.closed = False
                self.dockarea.addDock(pw)

        elif name == "*":
            if operation == 'clear':
                list(map(clear, list(self.namelist.keys())))
            elif operation == 'close':
                list(map(close, list(self.namelist.keys())))
            elif operation == 'remove':
                list(map(remove, list(self.namelist.keys())))
            return
        else:
            if operation in ('clear', 'close', 'remove','none'):
                return
            pw = self.add_new_plot(meta['rank'], name)

        if operation == 'clear':
            pw.clear()
        elif operation == 'close':
            pw.close()
        elif operation == 'none':
            pass
        elif operation == 'remove':
            del self.namelist[name]


        elif operation == 'plot_y':
            start_step = meta['start_step']
            label = meta['label']
            if start_step is not None:
                x0, dx = start_step
                nx = len(arr)
                xs = np.linspace(x0, x0 + (nx - 1)*dx, nx)
                pw.plot(xs, arr, name=label)
            else:
                pw.plot(arr, name=label)


        elif operation == 'plot_xy':
            label = meta['label']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']
            scat = meta['Scatter']
            pw.plot(arr[0], arr[1], parametric=True, name=label, xname=xnam, xscale =xscal, yname=ynam, yscale =yscal, scatter=scat)


        elif operation == 'plot_z':
            start_step = meta['start_step']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']
            znam = meta['Zname']
            zscal = meta['Z']
            if start_step is not None:
                (x0, dx), (y0, dy) = start_step
                pw.setAxisLabels(xname=xnam, xscale =xscal, yname=ynam, yscale =yscal, zname=znam, zscale =zscal)
                pw.setImage(arr, pos=(x0, y0), scale=(dx, dy), axes={'y':0, 'x':1})
            else:
                pw.setAxisLabels(xname=xnam, xscale =xscal, yname=ynam, yscale =yscal, zname=znam, zscale =zscal)
                pw.setImage(arr, axes={'y':0, 'x':1})


        elif operation == 'append_y':
            label = meta['label']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']

            xs, ys = pw.get_data(label)
            new_ys = list(ys)
            new_ys.append(meta['value'])
            start_step = meta['start_step']
            if start_step is not None:
                x0, dx = start_step
                nx = len(new_ys)
                xs = np.linspace(x0, x0 + (nx - 1)*dx, nx)
                pw.plot(xs, new_ys, name=label, xname=xnam, xscale =xscal, yname=ynam, yscale =yscal)
            else:
                pw.plot(new_ys, name=label, xname=xnam, xscale =xscal, yname=ynam, yscale =yscal)


        elif operation == 'append_xy':
            label = meta['label']
            xs, ys = pw.get_data(label)
            xn, yn = meta['value']
            new_xs = list(xs)
            new_xs.append(xn)
            new_ys = list(ys)
            new_ys.append(yn)
            pw.plot(new_xs, new_ys, parametric=True, name=label)


        elif operation == 'append_z':
            image = pw.get_data()
            if image is None:
                image = np.array([arr])
            else:
                try:
                    image = np.vstack((np.transpose(image), [arr]))
                except ValueError:
                    image = np.array([arr])
            start_step = meta['start_step']
            xnam = meta['Xname']
            xscal = meta['X']
            ynam = meta['Yname']
            yscal = meta['Y']
            znam = meta['Zname']
            zscal = meta['Z']
            if start_step is not None:
                (x0, dx), (y0, dy) = start_step
                pw.setAxisLabels(xname=xnam, xscale =xscal, yname=ynam, yscale =yscal, zname=znam, zscale =zscal)
                pw.setImage(image, pos=(x0, y0), scale=(dx, dy), axes={'y':0, 'x':1})
            else:
                pw.setAxisLabels(xname=xnam, xscale =xscal, yname=ynam, yscale =yscal)
                pw.setImage(image, axes={'y':0, 'x':1})


        elif operation == 'label':
            pw.setTitle(meta['value'])

    def add_new_plot(self, rank, name):
        pw = widgets.get_widget(rank, name)
        self.add_plot(pw)
        self.namelist[name] = pw
        return pw

    def add_plot(self, pw):
        self.insert_dock_right = not self.insert_dock_right
        self.dockarea.addDock(pw, position=['bottom', 'right'][self.insert_dock_right])

    def sizeHint(self):
        return QSize(1000, 600)
Пример #25
0
class QtSingleApplication(QApplication):
    """
    Adapted from https://stackoverflow.com/a/12712362/11038610

    Copyright 2013 Johan Råde

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above
    copyright notice, this list of conditions and the following
    disclaimer in the documentation and/or other materials provided
    with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
    CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
    BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.

    """
    message_received_event = pyqtSignal(str)

    def __init__(self, id, *argv):

        super().__init__(*argv)
        self._id = id

        # Is there another instance running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected()

        if self._isRunning:
            # Yes, there is.
            self._outStream = QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            # No, there isn't.
            self._outSocket = None
            self._outStream = None
            self._inSocket = None
            self._inStream = None
            self._server = QLocalServer()
            self._server.removeServer(self._id)
            self._server.listen(self._id)
            self._server.newConnection.connect(self._onNewConnection)

    def isRunning(self):
        return self._isRunning

    def id(self):
        return self._id

    def sendMessage(self, msg):
        if not self._outStream:
            return False
        self._outStream << msg << '\n'
        self._outStream.flush()
        return self._outSocket.waitForBytesWritten()

    def _onNewConnection(self):
        if self._inSocket:
            self._inSocket.readyRead.disconnect(self._onReadyRead)
        self._inSocket = self._server.nextPendingConnection()
        if not self._inSocket:
            return
        self._inStream = QTextStream(self._inSocket)
        self._inStream.setCodec('UTF-8')
        self._inSocket.readyRead.connect(self._onReadyRead)

    def _onReadyRead(self):
        while True:
            msg = self._inStream.readLine()
            if not msg:
                break
            self.message_received_event.emit(msg)
Пример #26
0
class SingleApplication(QtWidgets.QApplication):
    '''
    Inheriting from QApplication, executing main App instead.
    Watching whether the app is already running.
    If so, quit befor execution.
    '''
    messageReceived = QtCore.pyqtSignal(str)

    def __init__(self, id, *argv):

        super(SingleApplication, self).__init__(*argv)
        self._id = id
        self._activationWindow = None
        self._activateOnMessage = False

        # Check if another instance is running?
        self._outSocket = QLocalSocket()
        self._outSocket.connectToServer(self._id)
        self._isRunning = self._outSocket.waitForConnected()

        if self._isRunning:
            self._outStream = QtCore.QTextStream(self._outSocket)
            self._outStream.setCodec('UTF-8')
        else:
            self._outSocket = None
            self._outStream = None
            self._inSocket = None
            self._inStream = None
            self._server = QLocalServer()
            self._server.removeServer(self._id)  # if existing after crash-exit
            self._server.listen(self._id)
            self._server.newConnection.connect(self._onNewConnection)

    def isRunning(self):
        return self._isRunning

    def id(self):
        return self._id

    def activationWindow(self):
        return self._activationWindow

    def setActivationWindow(self, activationWindow, activateOnMessage=True):
        self._activationWindow = activationWindow
        self._activateOnMessage = activateOnMessage

    def activateWindow(self):
        if not self._activationWindow:
            return
        self._activationWindow.setWindowState(
            self._activationWindow.windowState() & ~QtCore.Qt.WindowMinimized)
        self._activationWindow.show()
        self._activationWindow.activateWindow()

    def sendMessage(self, msg):
        if not self._outStream:
            return False
        self._outStream << msg << '\n'
        self._outStream.flush()
        return self._outSocket.waitForBytesWritten()

    def _onNewConnection(self):
        if self._inSocket:
            self._inSocket.readyRead.disconnect(self._onReadyRead)
        self._inSocket = self._server.nextPendingConnection()
        if not self._inSocket:
            return
        self._inStream = QtCore.QTextStream(self._inSocket)
        self._inStream.setCodec('UTF-8')
        self._inSocket.readyRead.connect(self._onReadyRead)
        if self._activateOnMessage:
            self.activateWindow()

    def _onReadyRead(self):
        while True:
            msg = self._inStream.readLine()
            if not msg:
                break
            self.messageReceived.emit(msg)
Пример #27
0
 def _remove_server(self):
     """Remove an existing server."""
     ok = QLocalServer.removeServer(self._socketname)
     if not ok:
         raise Error("Error while removing server {}!".format(self._socketname))
Пример #28
0
class QSingleApplication(QApplication):
    messageReceived = pyqtSignal(str)

    def __init__(self, name, *args, **kwargs):
        super(QSingleApplication, self).__init__(*args, **kwargs)

        self._socketName = name
        self._activationWindow = None
        self._socketServer = None
        self._socketIn = None
        self._socketOut = None
        self._running = False

        # 先尝试连接
        self._socketOut = QLocalSocket(self)
        self._socketOut.connectToServer(self._socketName)
        self._socketOut.error.connect(self.handleError)
        self._running = self._socketOut.waitForConnected()

        if not self._running:  # 程序未运行
            self._socketOut.close()
            del self._socketOut
            # 创建本地server
            self._socketServer = QLocalServer(self)
            self._socketServer.listen(self._socketName)
            self._socketServer.newConnection.connect(self._onNewConnection)
            self.aboutToQuit.connect(self.removeServer)

    def handleError(self, message):
        print("handleError message: ", message)

    def isRunning(self):
        return self._running

    def setActivationWindow(self, activationWindow):
        # 设置当前窗口
        self._activationWindow = activationWindow

    def activateWindow(self):
        # 激活当前窗口
        try:
            self._activationWindow.setWindowState(
                self._activationWindow.windowState() & ~Qt.WindowMinimized)
            #self._activationWindow.raise_() # 提升窗口到最上面
            self._activationWindow.showNormal()
            self._activationWindow.activateWindow()
        except Exception as e:
            print(e)

    def sendMessage(self, message, msecs=5000):
        if not self._socketOut:
            return False
        if not isinstance(message, bytes):
            message = str(message).encode()
        self._socketOut.write(message)
        if not self._socketOut.waitForBytesWritten(msecs):
            raise Exception("Bytes not written within %ss" % (msecs / 1000.))
        return True

    def _onNewConnection(self):
        if self._socketIn:
            self._socketIn.readyRead.disconnect(self._onReadyRead)
        self._socketIn = self._socketServer.nextPendingConnection()
        if not self._socketIn:
            return
        self._socketIn.readyRead.connect(self._onReadyRead)

    def _onReadyRead(self):
        while 1:
            message = self._socketIn.readLine()
            if not message:
                break
            if message == b'show':
                self.activateWindow()
            self.messageReceived.emit(message.data().decode())

    def removeServer(self):
        self._socketServer.close()
        self._socketServer.removeServer(self._socketName)
Пример #29
0
 def _remove_server(self):
     """Remove an existing server."""
     ok = QLocalServer.removeServer(self._socketname)
     if not ok:
         raise Error("Error while removing server {}!".format(
             self._socketname))
Пример #30
0
 def _remove_server(self):
     """Remove an existing server."""
     ok = QLocalServer.removeServer(SOCKETNAME)
     if not ok:
         raise IPCError("Error while removing server {}!".format(
             SOCKETNAME))