From 8597b5c9bef30aae8f98fd2da685b59a7adc9bc2 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Mon, 25 Jul 2016 22:25:26 +0200 Subject: [PATCH 01/12] CURA-1923: SliceInfo: Sending data threaded The idea is to make the sending process, so it won't block the UI until the data is sent or the timeout has reached. This commit does the following: * Increasing the timeout to 5s -> As we are sending our data threaded now this won't hurt. At least for clients in slow networks. * Moving section for "sending data to info_url" into a seperate thread. * Starting the thread instead and collecting all threads in a list * Removing all threads from the list, if they have already finished their work. * When the SliceInfo Extension gets __del__'d, it will wait for the threads until they finish. Contributes to CURA-1923 --- plugins/SliceInfoPlugin/SliceInfo.py | 64 ++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index d50194d40f..737d3b03d4 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -12,10 +12,7 @@ from UM.Logger import Logger from UM.Platform import Platform from UM.Qt.Duration import DurationFormat -import collections -import json -import os.path -import copy +from threading import Thread import platform import math import urllib.request @@ -24,6 +21,33 @@ import ssl catalog = i18nCatalog("cura") +class SliceInfoThread(Thread): + data = None + url = None + + def __init__(self, url, data): + Thread.__init__(self) + self.url = url + self.data = data + + def run(self): + if not self.url or not self.data: + Logger.log("e", "URL or DATA for sending slice info was not set!") + + # Submit data + kwoptions = {"data" : self.data, + "timeout" : 5 + } + + if Platform.isOSX(): + kwoptions["context"] = ssl._create_unverified_context() + + try: + f = urllib.request.urlopen(self.url, **kwoptions) + Logger.log("i", "Sent anonymous slice info to %s", self.url) + f.close() + except Exception: + Logger.logException("e", "An exception occurred while trying to send slice information") ## This Extension runs in the background and sends several bits of information to the Ultimaker servers. # The data is only sent when the user in question gave permission to do so. All data is anonymous and @@ -43,11 +67,25 @@ class SliceInfo(Extension): self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered) self.send_slice_info_message.show() + self.runningThreads = [] + + def __del__(self): + for thread in self.threadedReports: + if thread.is_alive(): + thread.join() # Wait for threads - shouldn't take much more time than the timeout. See above.. + + def _removeFinishedThreads(self): + for process in self.runningThreads: + if not process.is_alive(): + self.runningThreads.remove(process) # Remove finished threads + def messageActionTriggered(self, message_id, action_id): self.send_slice_info_message.hide() Preferences.getInstance().setValue("info/asked_send_slice_info", True) def _onWriteStarted(self, output_device): + self._removeFinishedThreads() + try: if not Preferences.getInstance().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") @@ -112,19 +150,11 @@ class SliceInfo(Extension): submitted_data = urllib.parse.urlencode(submitted_data) binary_data = submitted_data.encode("utf-8") - # Submit data - kwoptions = {"data" : binary_data, - "timeout" : 1 - } - if Platform.isOSX(): - kwoptions["context"] = ssl._create_unverified_context() - try: - f = urllib.request.urlopen(self.info_url, **kwoptions) - Logger.log("i", "Sent anonymous slice info to %s", self.info_url) - f.close() - except Exception as e: - Logger.logException("e", "An exception occurred while trying to send slice information") + # Sending slice info non-blocking + reportThread = SliceInfoThread(self.info_url, binary_data) + self.runningThreads.append(reportThread) + reportThread.start() except: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. - pass + Logger.logException("e", "Exception raised while sending slice info") # But we should be notified about these problems of course. \ No newline at end of file From 36f4f5180420314143baf73c4d6120b571d65618 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Tue, 26 Jul 2016 18:25:54 +0200 Subject: [PATCH 02/12] Adding missing return Without the function would be still progressed of course. --- plugins/SliceInfoPlugin/SliceInfo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 737d3b03d4..8c7402762a 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -33,6 +33,7 @@ class SliceInfoThread(Thread): def run(self): if not self.url or not self.data: Logger.log("e", "URL or DATA for sending slice info was not set!") + return # Submit data kwoptions = {"data" : self.data, From 260b68286475afc59eb31ab2b7a995b268e9f43e Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Tue, 26 Jul 2016 18:56:57 +0200 Subject: [PATCH 03/12] CURA-1923: Slicinfo: Using UM.Job instead As suggested by @awhiemstra handling our "thread" here should be done with UM.Job. --- plugins/SliceInfoPlugin/SliceInfo.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 8c7402762a..20e670dca7 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -11,8 +11,9 @@ from UM.i18n import i18nCatalog from UM.Logger import Logger from UM.Platform import Platform from UM.Qt.Duration import DurationFormat +from UM.Job import Job -from threading import Thread +import time import platform import math import urllib.request @@ -21,12 +22,12 @@ import ssl catalog = i18nCatalog("cura") -class SliceInfoThread(Thread): +class SliceInfoThread(Job): data = None url = None def __init__(self, url, data): - Thread.__init__(self) + Job.__init__(self) self.url = url self.data = data @@ -68,17 +69,17 @@ class SliceInfo(Extension): self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered) self.send_slice_info_message.show() - self.runningThreads = [] + self.runningJobs = [] def __del__(self): - for thread in self.threadedReports: - if thread.is_alive(): - thread.join() # Wait for threads - shouldn't take much more time than the timeout. See above.. + for job in self.runningJobs: + while not job.isFinished(): + time.sleep(1) # Wait for threads - shouldn't take much more time than the timeout. See above.. def _removeFinishedThreads(self): - for process in self.runningThreads: - if not process.is_alive(): - self.runningThreads.remove(process) # Remove finished threads + for job in self.runningJobs: + if job.isFinished(): + self.runningJobs.remove(job) # Remove finished threads def messageActionTriggered(self, message_id, action_id): self.send_slice_info_message.hide() @@ -152,9 +153,9 @@ class SliceInfo(Extension): binary_data = submitted_data.encode("utf-8") # Sending slice info non-blocking - reportThread = SliceInfoThread(self.info_url, binary_data) - self.runningThreads.append(reportThread) - reportThread.start() + reportJob = SliceInfoThread(self.info_url, binary_data) + self.runningJobs.append(reportJob) + reportJob.start() except: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. From 7581aded7aa68e7c7c42db0493e6e694f45ca74d Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 20:32:44 +0200 Subject: [PATCH 04/12] CURA-1923: Remove tracking of jobs Like suggested by @awhiemstra. Contributes to CURA-1923 --- plugins/SliceInfoPlugin/SliceInfo.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 20e670dca7..7fd0e38309 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -13,7 +13,6 @@ from UM.Platform import Platform from UM.Qt.Duration import DurationFormat from UM.Job import Job -import time import platform import math import urllib.request @@ -69,18 +68,6 @@ class SliceInfo(Extension): self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered) self.send_slice_info_message.show() - self.runningJobs = [] - - def __del__(self): - for job in self.runningJobs: - while not job.isFinished(): - time.sleep(1) # Wait for threads - shouldn't take much more time than the timeout. See above.. - - def _removeFinishedThreads(self): - for job in self.runningJobs: - if job.isFinished(): - self.runningJobs.remove(job) # Remove finished threads - def messageActionTriggered(self, message_id, action_id): self.send_slice_info_message.hide() Preferences.getInstance().setValue("info/asked_send_slice_info", True) @@ -154,7 +141,6 @@ class SliceInfo(Extension): # Sending slice info non-blocking reportJob = SliceInfoThread(self.info_url, binary_data) - self.runningJobs.append(reportJob) reportJob.start() except: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device From dd9220068e7ccc1fba15a453dffd1d5030c5b3b4 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 20:34:52 +0200 Subject: [PATCH 05/12] CURA-1923: Using super.__init__() --- plugins/SliceInfoPlugin/SliceInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 7fd0e38309..1324f60948 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -26,7 +26,7 @@ class SliceInfoThread(Job): url = None def __init__(self, url, data): - Job.__init__(self) + super.__init__(self) self.url = url self.data = data From c7956542c41499a22a8efabbf700c2a542967259 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 20:39:55 +0200 Subject: [PATCH 06/12] CURA-1923: Rename SliceInfoThread to SliceInfoJob --- plugins/SliceInfoPlugin/SliceInfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 1324f60948..5630f76805 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -21,7 +21,7 @@ import ssl catalog = i18nCatalog("cura") -class SliceInfoThread(Job): +class SliceInfoJob(Job): data = None url = None @@ -140,7 +140,7 @@ class SliceInfo(Extension): binary_data = submitted_data.encode("utf-8") # Sending slice info non-blocking - reportJob = SliceInfoThread(self.info_url, binary_data) + reportJob = SliceInfoJob(self.info_url, binary_data) reportJob.start() except: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device From 44c64c4ac766fc7e085d338cb1f21c949851c9ba Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 21:14:21 +0200 Subject: [PATCH 07/12] Clean up __init__ --- plugins/3MFReader/ThreeMFReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 57d76b2783..12c74ce73c 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -20,7 +20,7 @@ import xml.etree.ElementTree as ET ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): def __init__(self): - super(ThreeMFReader, self).__init__() + super().__init__() self._supported_extensions = [".3mf"] self._namespaces = { From b699efa55e388cf0de97b7385fecbeb57fa3a997 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 21:20:55 +0200 Subject: [PATCH 08/12] Revert "Clean up __init__" This reverts commit 44c64c4ac766fc7e085d338cb1f21c949851c9ba. --- plugins/3MFReader/ThreeMFReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 12c74ce73c..57d76b2783 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -20,7 +20,7 @@ import xml.etree.ElementTree as ET ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): def __init__(self): - super().__init__() + super(ThreeMFReader, self).__init__() self._supported_extensions = [".3mf"] self._namespaces = { From 8e92cb4c91bff762396d6603013c11a0f092894e Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Sun, 7 Aug 2016 08:16:22 +0200 Subject: [PATCH 09/12] CURA-1923: Don't return a complete traceback on HTTPError As discussed on GitHub we don't need a complete traceback on HTTPError. However URLError should return a full traceback, like any other exception that might occur. --- plugins/SliceInfoPlugin/SliceInfo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 5630f76805..57a1db7244 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -47,7 +47,9 @@ class SliceInfoJob(Job): f = urllib.request.urlopen(self.url, **kwoptions) Logger.log("i", "Sent anonymous slice info to %s", self.url) f.close() - except Exception: + except urllib.error.HTTPError as http_exception: + Logger.log("e", "An exception occurred while trying to send slice information: %s" %(repr(http_exception))) + except Exception: # Includes urllib.error.HTTPError, was discussed to be handled like any other exception Logger.logException("e", "An exception occurred while trying to send slice information") ## This Extension runs in the background and sends several bits of information to the Ultimaker servers. From e5ff77bc5c4d94051880ab2a44e3f20c5dea589c Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Sun, 7 Aug 2016 08:17:31 +0200 Subject: [PATCH 10/12] Showing less traceinfo --- plugins/SliceInfoPlugin/SliceInfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 57a1db7244..5d48a2ec4f 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -144,7 +144,7 @@ class SliceInfo(Extension): # Sending slice info non-blocking reportJob = SliceInfoJob(self.info_url, binary_data) reportJob.start() - except: + except Exception as e: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. - Logger.logException("e", "Exception raised while sending slice info") # But we should be notified about these problems of course. \ No newline at end of file + Logger.log("e", "Exception raised while sending slice info: %s" %(repr(e))) # But we should be notified about these problems of course. \ No newline at end of file From dbe1d140c4f30ad7c64f760d42a372e64c59749b Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Sun, 7 Aug 2016 08:54:58 +0200 Subject: [PATCH 11/12] Removing old function --- plugins/SliceInfoPlugin/SliceInfo.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 5d48a2ec4f..5d1cca5f24 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -75,8 +75,6 @@ class SliceInfo(Extension): Preferences.getInstance().setValue("info/asked_send_slice_info", True) def _onWriteStarted(self, output_device): - self._removeFinishedThreads() - try: if not Preferences.getInstance().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") From ecd80974f712a2463ce8a4ffd824f32d3e0cbb50 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Sun, 7 Aug 2016 09:04:24 +0200 Subject: [PATCH 12/12] CURA-1923: Just something I missed Forgot to remove 'self' from 'super().__init__()' --- plugins/SliceInfoPlugin/SliceInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 5d1cca5f24..7277bfaeec 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -26,7 +26,7 @@ class SliceInfoJob(Job): url = None def __init__(self, url, data): - super.__init__(self) + super().__init__() self.url = url self.data = data