mirror of https://github.com/borgbase/vorta
Implement QtSingleApplication by @Hofer-Julian
- Use C-style single app implementation with messaging across instances of the same app. - Open main window when application already runs.
This commit is contained in:
parent
b8a3819303
commit
ab04d84fc1
|
@ -19,7 +19,6 @@ def main():
|
|||
# We assume that a frozen binary is a fat single-file binary made with
|
||||
# PyInstaller. These are not compatible with forking into background here:
|
||||
if not (want_foreground or frozen_binary):
|
||||
print('Forking to background (see system tray).')
|
||||
if os.fork():
|
||||
sys.exit()
|
||||
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
import os
|
||||
import sys
|
||||
import fcntl
|
||||
|
||||
import qdarkstyle
|
||||
from PyQt5 import QtCore
|
||||
import sip
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
import qdarkstyle
|
||||
|
||||
from .i18n import init_translations, translate
|
||||
from .tray_menu import TrayMenu
|
||||
from .scheduler import VortaScheduler
|
||||
from .models import BackupProfileModel, SettingsModel
|
||||
from .borg.create import BorgCreateThread
|
||||
from .views.main_window import MainWindow
|
||||
from .i18n import init_translations, translate
|
||||
from .models import BackupProfileModel, SettingsModel
|
||||
from .qt_single_application import QtSingleApplication
|
||||
from .scheduler import VortaScheduler
|
||||
from .tray_menu import TrayMenu
|
||||
from .utils import parse_args, set_tray_icon
|
||||
from vorta.config import SETTINGS_DIR
|
||||
from .views.main_window import MainWindow
|
||||
|
||||
APP_ID = "vorta"
|
||||
|
||||
|
||||
class VortaApp(QApplication):
|
||||
class VortaApp(QtSingleApplication):
|
||||
"""
|
||||
All windows and QWidgets are children of this app.
|
||||
|
||||
|
@ -32,20 +31,12 @@ class VortaApp(QApplication):
|
|||
|
||||
def __init__(self, args_raw, single_app=False):
|
||||
|
||||
# Ensure only one app instance is running.
|
||||
# From https://stackoverflow.com/questions/220525/
|
||||
# ensure-a-single-instance-of-an-application-in-linux#221159
|
||||
if single_app:
|
||||
pid_file = os.path.join(SETTINGS_DIR, 'vorta.pid')
|
||||
lockfile = open(pid_file, 'w+')
|
||||
try:
|
||||
fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
self.lockfile = lockfile
|
||||
except OSError:
|
||||
print('An instance of Vorta is already running.')
|
||||
sys.exit(1)
|
||||
super().__init__(APP_ID, args_raw)
|
||||
if self.isRunning() and single_app:
|
||||
self.sendMessage("open main window")
|
||||
print('An instance of Vorta is already running. Opening main window.')
|
||||
sys.exit()
|
||||
|
||||
super().__init__(args_raw)
|
||||
init_translations(self)
|
||||
|
||||
self.setQuitOnLastWindowClosed(False)
|
||||
|
@ -65,6 +56,7 @@ class VortaApp(QApplication):
|
|||
self.backup_started_event.connect(self.backup_started_event_response)
|
||||
self.backup_finished_event.connect(self.backup_finished_event_response)
|
||||
self.backup_cancelled_event.connect(self.backup_cancelled_event_response)
|
||||
self.message_received_event.connect(self.message_received_event_response)
|
||||
|
||||
def create_backup_action(self, profile_id=None):
|
||||
if not profile_id:
|
||||
|
@ -98,3 +90,7 @@ class VortaApp(QApplication):
|
|||
|
||||
def backup_cancelled_event_response(self):
|
||||
set_tray_icon(self.tray)
|
||||
|
||||
def message_received_event_response(self, message):
|
||||
if message == "open main window":
|
||||
self.open_main_window_action()
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
from PyQt5.QtCore import QTextStream
|
||||
from PyQt5.QtNetwork import QLocalSocket, QLocalServer
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
|
||||
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)
|
Loading…
Reference in New Issue