Merge branch 'single_instance'

This commit is contained in:
Simon Edwards 2017-02-07 13:45:11 +01:00
commit 71d2990ca9
2 changed files with 94 additions and 2 deletions

View File

@ -1,5 +1,7 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtNetwork import QLocalServer
from PyQt5.QtNetwork import QLocalSocket
from UM.Qt.QtApplication import QtApplication from UM.Qt.QtApplication import QtApplication
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
@ -59,7 +61,8 @@ import numpy
import copy import copy
import urllib.parse import urllib.parse
import os import os
import argparse
import json
numpy.seterr(all="ignore") numpy.seterr(all="ignore")
@ -420,13 +423,99 @@ class CuraApplication(QtApplication):
self._plugins_loaded = True self._plugins_loaded = True
@classmethod
def addCommandLineOptions(self, parser): def addCommandLineOptions(self, parser):
super().addCommandLineOptions(parser) super().addCommandLineOptions(parser)
parser.add_argument("file", nargs="*", help="Files to load after starting the application.") parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
parser.add_argument("--single-instance", action="store_true", default=False)
# Set up a local socket server which listener which coordinates single instances Curas and accepts commands.
def _setUpSingleInstanceServer(self):
if self.getCommandLineOption("single_instance", False):
self.__single_instance_server = QLocalServer()
self.__single_instance_server.newConnection.connect(self._singleInstanceServerNewConnection)
self.__single_instance_server.listen("ultimaker-cura")
def _singleInstanceServerNewConnection(self):
Logger.log("i", "New connection recevied on our single-instance server")
remote_cura_connection = self.__single_instance_server.nextPendingConnection()
if remote_cura_connection is not None:
def readCommands():
line = remote_cura_connection.readLine()
while len(line) != 0: # There is also a .canReadLine()
try:
Logger.log('d', "JSON command: " + str(line, encoding="ASCII"))
payload = json.loads(str(line, encoding="ASCII").strip())
command = payload["command"]
# Command: Remove all models from the build plate.
if command == "clear-all":
self.deleteAll()
# Command: Load a model file
elif command == "open":
self._openFile(payload["filePath"])
# FIXME ^ this method is async and we really should wait until
# the file load is complete before processing more commands.
# Command: Activate the window and bring it to the top.
elif command == "focus":
self.getMainWindow().raise_()
self.focusWindow()
else:
Logger.log("w", "Received an unrecognized command " + str(command))
except json.decoder.JSONDecodeError as ex:
Logger.log("w", "Unable to parse JSON command in _singleInstanceServerNewConnection(): " + repr(ex))
line = remote_cura_connection.readLine()
remote_cura_connection.readyRead.connect(readCommands)
remote_cura_connection.disconnected.connect(readCommands) # Get any last commands before it is destroyed.
## Perform any checks before creating the main application.
#
# This should be called directly before creating an instance of CuraApplication.
# \returns \type{bool} True if the whole Cura app should continue running.
@classmethod
def preStartUp(cls):
# Peek the arguments and look for the 'single-instance' flag.
parser = argparse.ArgumentParser(prog="cura") # pylint: disable=bad-whitespace
CuraApplication.addCommandLineOptions(parser)
parsed_command_line = vars(parser.parse_args())
if "single_instance" in parsed_command_line and parsed_command_line["single_instance"]:
Logger.log("i", "Checking for the presence of an ready running Cura instance.")
single_instance_socket = QLocalSocket()
single_instance_socket.connectToServer("ultimaker-cura")
single_instance_socket.waitForConnected()
if single_instance_socket.state() == QLocalSocket.ConnectedState:
Logger.log("i", "Connection has been made to the single-instance Cura socket.")
# Protocol is one line of JSON terminated with a carriage return.
# "command" field is required and holds the name of the command to execute.
# Other fields depend on the command.
payload = {"command": "clear-all"}
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII"))
payload = {"command": "focus"}
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII"))
if len(parsed_command_line["file"]) != 0:
for filename in parsed_command_line["file"]:
payload = {"command": "open", "filePath": filename}
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII"))
single_instance_socket.flush()
single_instance_socket.close()
return False
return True
def run(self): def run(self):
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene...")) self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
self._setUpSingleInstanceServer()
controller = self.getController() controller = self.getController()
controller.setActiveView("SolidView") controller.setActiveView("SolidView")

View File

@ -2,7 +2,6 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
import os import os
import sys import sys
import platform import platform
@ -58,5 +57,9 @@ if Platform.isWindows() and hasattr(sys, "frozen"):
# Force an instance of CuraContainerRegistry to be created and reused later. # Force an instance of CuraContainerRegistry to be created and reused later.
cura.Settings.CuraContainerRegistry.getInstance() cura.Settings.CuraContainerRegistry.getInstance()
# This prestart up check is needed to determine if we should start the application at all.
if not cura.CuraApplication.CuraApplication.preStartUp():
sys.exit(0)
app = cura.CuraApplication.CuraApplication.getInstance() app = cura.CuraApplication.CuraApplication.getInstance()
app.run() app.run()