summaryrefslogtreecommitdiff
path: root/src/leap
diff options
context:
space:
mode:
authorkali <kali@leap.se>2012-11-21 23:01:06 +0900
committerkali <kali@leap.se>2012-11-22 01:56:20 +0900
commit7bf4c0aa6db8cbaa1befdb2841f722554a3a0731 (patch)
treef212526bf7eb7899fe13f323d6250ec717d24dd6 /src/leap
parent53c6c92e26970de7de0bddca0034e72af7d0ce48 (diff)
fixed ui freeze using queue for passing status between worker and parent
Diffstat (limited to 'src/leap')
-rw-r--r--src/leap/gui/firstrun/providerinfo.py119
-rw-r--r--src/leap/gui/firstrun/providerselect.py200
-rw-r--r--src/leap/gui/progress.py97
-rw-r--r--src/leap/gui/threads.py8
4 files changed, 265 insertions, 159 deletions
diff --git a/src/leap/gui/firstrun/providerinfo.py b/src/leap/gui/firstrun/providerinfo.py
index c5f39938..48763357 100644
--- a/src/leap/gui/firstrun/providerinfo.py
+++ b/src/leap/gui/firstrun/providerinfo.py
@@ -98,120 +98,8 @@ class ProviderInfoPage(ValidationPage):
"""
executes actual checks in a separate thread
"""
- finish = lambda: update_signal.emit("end_sentinel", 100)
-
- def pause_and_finish():
- # only for local debug
- finish()
- pause_for_user()
-
- wizard = self.wizard()
- prevpage = "providerselection"
-
- full_domain = self.field('provider_domain')
-
- # we check if we have a port in the domain string.
- domain, port = get_https_domain_and_port(full_domain)
- _domain = u"%s:%s" % (domain, port) if port != 443 else unicode(domain)
-
- netchecker = wizard.netchecker()
- providercertchecker = wizard.providercertchecker()
- eipconfigchecker = wizard.eipconfigchecker(domain=_domain)
-
- update_signal.emit("head_sentinel", 0)
- pause_for_user()
-
- ########################
- # 1) try name resolution
- ########################
- update_signal.emit("Checking that server is reachable", 20)
- logger.debug('checking name resolution')
- try:
- netchecker.check_name_resolution(
- domain)
-
- except baseexceptions.LeapException as exc:
- logger.error(exc.message)
- wizard.set_validation_error(
- prevpage, exc.usermessage)
- pause_and_finish()
- return False
-
- #########################
- # 2) try https connection
- #########################
- update_signal.emit("Checking secure connection to provider", 40)
- logger.debug('checking https connection')
- try:
- providercertchecker.is_https_working(
- "https://%s" % _domain,
- verify=True)
-
- except eipexceptions.HttpsBadCertError as exc:
- logger.debug('exception')
- # XXX skipping for now...
- ##############################################
- # We had this validation logic
- # in the provider selection page before
- ##############################################
- #if self.trustProviderCertCheckBox.isChecked():
- #pass
- #else:
- wizard.set_validation_error(
- prevpage, exc.usermessage)
- #fingerprint = certs.get_cert_fingerprint(
- #domain=domain, sep=" ")
-
- # it's ok if we've trusted this fgprt before
- #trustedcrts = wizard.trusted_certs
- #if trustedcrts and fingerprint.replace(' ', '') in trustedcrts:
- #pass
- #else:
- # let your user face panick :P
- #self.add_cert_info(fingerprint)
- #self.did_cert_check = True
- #self.completeChanged.emit()
- #return False
- finish()
- return False
-
- except baseexceptions.LeapException as exc:
- wizard.set_validation_error(
- prevpage, exc.usermessage)
- finish()
- return False
-
- ##################################
- # 3) try download provider info...
- ##################################
-
- update_signal.emit("Downloading provider info", 70)
- try:
- # XXX we already set _domain in the initialization
- # so it should not be needed here.
- eipconfigchecker.fetch_definition(domain=_domain)
- wizard.set_providerconfig(
- eipconfigchecker.defaultprovider.config)
- except requests.exceptions.SSLError:
- # XXX we should have catched this before.
- # but cert checking is broken.
- wizard.set_validation_error(
- prevpage,
- "Could not get info from provider.")
- finish()
- return False
- except requests.exceptions.ConnectionError:
- wizard.set_validation_error(
- prevpage,
- "Could not download provider info "
- "(refused conn.).")
- finish()
- return False
- # XXX catch more errors...
-
# We're done!
self.set_done()
- finish()
def _do_validation(self):
"""
@@ -247,7 +135,12 @@ class ProviderInfoPage(ValidationPage):
self.completeChanged.emit()
def cleanupPage(self):
- del self.wizard().providerconfig
+ wizard = self.wizard()
+
+ # XXX makes sense now?
+ # this was created on previous...
+ if hasattr(wizard, 'providerconfig'):
+ del self.wizard().providerconfig
if self.infoWidget:
QtCore.QObjectCleanupHandler().add(
diff --git a/src/leap/gui/firstrun/providerselect.py b/src/leap/gui/firstrun/providerselect.py
index 2786c494..08e09ee3 100644
--- a/src/leap/gui/firstrun/providerselect.py
+++ b/src/leap/gui/firstrun/providerselect.py
@@ -1,14 +1,17 @@
"""
Select Provider Page, used in First Run Wizard
"""
+from functools import partial
import logging
+import requests
+
from PyQt4 import QtCore
from PyQt4 import QtGui
from leap.base import exceptions as baseexceptions
#from leap.crypto import certs
-#from leap.eip import exceptions as eipexceptions
+from leap.eip import exceptions as eipexceptions
from leap.gui.constants import APP_LOGO
from leap.gui.progress import InlineValidationPage
@@ -17,8 +20,24 @@ from leap.util.web import get_https_domain_and_port
logger = logging.getLogger(__name__)
+# XXX check newer version in progress...
+
+
+def delay(obj, method_str):
+ """
+ this is a hack to get responsiveness in the ui
+ """
+ QtCore.QTimer().singleShot(
+ 10,
+ lambda: QtCore.QMetaObject.invokeMethod(
+ obj, method_str))
+
class SelectProviderPage(InlineValidationPage):
+
+ #disableCheckButton = QtCore.pyqtSignal()
+ launchChecks = QtCore.pyqtSignal()
+
def __init__(self, parent=None, providers=None):
super(SelectProviderPage, self).__init__(parent)
@@ -39,6 +58,11 @@ class SelectProviderPage(InlineValidationPage):
self.setupSteps()
self.setupUI()
+ #self.disableCheckButton.connect(
+ #self.onDisableCheckButton)
+ self.launchChecks.connect(
+ self.launch_checks)
+
def setupUI(self):
"""
initializes the UI
@@ -149,21 +173,41 @@ class SelectProviderPage(InlineValidationPage):
valFrame.setLayout(valframeLayout)
self.valFrame = valFrame
- # check domain
-
- def onCheckButtonClicked(self):
+ @QtCore.pyqtSlot()
+ def onDisableCheckButton(self):
+ print 'CHECK BUTTON DISABLED!!!'
self.providerCheckButton.setDisabled(True)
- self.valFrame.show()
+
+ @QtCore.pyqtSlot()
+ def launch_checks(self):
+ # trying to delay this...
+ #timer = QtCore.QTimer()
+ #timer.singleShot(0, self.do_checks)
self.do_checks()
- def _do_checks(self, update_signal=None, failed_signal=None):
+ def onCheckButtonClicked(self):
+ #self.disableCheckButton.emit()
+ # XXX trying to get responsiveness.
+ # UI here is blocking, although I'm using
+ # threads and signals :(
+ QtCore.QMetaObject.invokeMethod(
+ self, "onDisableCheckButton")
+
+ QtCore.QMetaObject.invokeMethod(
+ self, "showStepsFrame")
+
+ delay(self, "launch_checks")
+
+ print 'ON CHECK BUTTON --- DONE!'
+ print 'timer.....'
+
+ def _do_checks(self):
"""
executes actual checks in a separate thread
"""
- finish = lambda: update_signal.emit("end_sentinel", 100)
wizard = self.wizard()
- prevpage = "providerselection"
+ curpage = "providerselection"
full_domain = self.providerNameEdit.text()
@@ -173,35 +217,143 @@ class SelectProviderPage(InlineValidationPage):
netchecker = wizard.netchecker()
- #providercertchecker = wizard.providercertchecker()
- #eipconfigchecker = wizard.eipconfigchecker(domain=_domain)
+ providercertchecker = wizard.providercertchecker()
+ eipconfigchecker = wizard.eipconfigchecker(domain=_domain)
+
+ def fail():
+ self.is_done = False
+ return False
- update_signal.emit("head_sentinel", 0)
+ yield(("head_sentinel", 0), lambda: None)
########################
# 1) try name resolution
########################
- update_signal.emit(self.tr("Can reach provider"), 20)
logger.debug('checking name resolution')
- try:
- netchecker.check_name_resolution(
- domain)
-
- except baseexceptions.LeapException as exc:
- logger.error(exc.message)
- wizard.set_validation_error(
- prevpage, exc.usermessage)
- failed_signal.emit()
- self.is_done = False
- return False
+ def namecheck():
+ try:
+ netchecker.check_name_resolution(
+ domain)
+
+ except baseexceptions.LeapException as exc:
+ logger.error(exc.message)
+ wizard.set_validation_error(
+ curpage, exc.usermessage)
+ return fail()
+
+ except Exception as exc:
+ wizard.set_validation_error(
+ curpage, exc.message)
+ return fail()
+
+ else:
+ return True
+
+ # XXX catch more exceptions
+
+ yield(("check name", 20), namecheck)
+
+ #########################
+ # 2) try https connection
+ #########################
+
+ logger.debug('checking https connection')
+
+ def httpscheck():
+ try:
+ providercertchecker.is_https_working(
+ "https://%s" % _domain,
+ verify=True)
+
+ except eipexceptions.HttpsBadCertError as exc:
+ logger.debug('exception')
+ # XXX skipping for now...
+ ##############################################
+ # We had this validation logic
+ # in the provider selection page before
+ ##############################################
+ #if self.trustProviderCertCheckBox.isChecked():
+ #pass
+ #else:
+ wizard.set_validation_error(
+ curpage, exc.usermessage)
+ #fingerprint = certs.get_cert_fingerprint(
+ #domain=domain, sep=" ")
+
+ # it's ok if we've trusted this fgprt before
+ #trustedcrts = wizard.trusted_certs
+ #if trustedcrts and \
+ # fingerprint.replace(' ', '') in trustedcrts:
+ #pass
+ #else:
+ # let your user face panick :P
+ #self.add_cert_info(fingerprint)
+ #self.did_cert_check = True
+ #self.completeChanged.emit()
+ #return False
+ return fail()
+
+ except baseexceptions.LeapException as exc:
+ wizard.set_validation_error(
+ curpage, exc.usermessage)
+ return fail()
+
+ except Exception as exc:
+ wizard.set_validation_error(
+ curpage, exc.message)
+ return fail()
+
+ else:
+ return True
+
+ yield(("https check", 40), httpscheck)
+
+ ##################################
+ # 3) try download provider info...
+ ##################################
+
+ def fetchinfo():
+ try:
+ # XXX we already set _domain in the initialization
+ # so it should not be needed here.
+ eipconfigchecker.fetch_definition(domain=_domain)
+ wizard.set_providerconfig(
+ eipconfigchecker.defaultprovider.config)
+ except requests.exceptions.SSLError:
+ # XXX we should have catched this before.
+ # but cert checking is broken.
+ wizard.set_validation_error(
+ curpage,
+ self.tr(
+ "Could not get info from provider."))
+ return fail()
+ except requests.exceptions.ConnectionError:
+ wizard.set_validation_error(
+ curpage,
+ self.tr(
+ "Could not download provider info "
+ "(refused conn.)."))
+ return fail()
+
+ except Exception as exc:
+ wizard.set_validation_error(
+ curpage, exc.message)
+ return fail()
+
+ else:
+ return True
+ yield(("fetch info", 80), fetchinfo)
+
+ # done!
self.is_done = True
- finish()
+ yield(("end_sentinel", 100), lambda: None)
def _inline_validation_ready(self):
"""
called after _do_checks has finished.
"""
+ print 'VALIDATION READY ---------------'
self.domain_checked = True
if self.is_done:
self.wizard().clean_validation_error(self.current_page)
diff --git a/src/leap/gui/progress.py b/src/leap/gui/progress.py
index 33b0cb8c..9a5b352c 100644
--- a/src/leap/gui/progress.py
+++ b/src/leap/gui/progress.py
@@ -24,6 +24,24 @@ ICON_WAITING = ":/images/Emblem-question.png"
logger = logging.getLogger(__name__)
+# XXX import this from threads
+def delay(obj, method_str=None, call_args=None):
+ """
+ this is a hack to get responsiveness in the ui
+ """
+ if callable(obj) and not method_str:
+ QtCore.QTimer().singleShot(
+ 50,
+ lambda: obj())
+ return
+
+ if method_str:
+ QtCore.QTimer().singleShot(
+ 50,
+ lambda: QtCore.QMetaObject.invokeMethod(
+ obj, method_str))
+
+
class ImgWidget(QtGui.QWidget):
# XXX move to widgets
@@ -168,6 +186,22 @@ class WithStepsMixIn(object):
self.progress.setValue(progress)
self.progress.update()
+ def processStepsQueue(self):
+ """
+ consume steps queue
+ and pass messages
+ to the ui updater functions
+ """
+ while self.queue.qsize():
+ try:
+ status = self.queue.get(0)
+ if status == "failed":
+ self.set_failed_icon()
+ else:
+ self.onStepStatusChanged(*status)
+ except Queue.Empty:
+ pass
+
def setupSteps(self):
self.steps = ProgressStepContainer()
# steps table widget
@@ -266,38 +300,59 @@ class WithStepsMixIn(object):
"""
-Resist the temptation to refactor the declaration of the signal
-to the mixin.
-PyQt and multiple inheritance do not mix well together.
-You can only have one QObject base.
-Therefore, we will use one base class for the intermediate pages
+We will use one base class for the intermediate pages
and another one for the in-page validations, both sharing the creation
of the tablewidgets.
+The logic of this split comes from where I was trying to solve
+the ui update using signals, but now that it's working well with
+queues I could join them again.
"""
+import Queue
+from functools import partial
-class InlineValidationPage(QtGui.QWizardPage, WithStepsMixIn):
- # signals
- stepChanged = QtCore.pyqtSignal([str, int])
- stepFailed = QtCore.pyqtSignal()
+class InlineValidationPage(QtGui.QWizardPage, WithStepsMixIn):
def __init__(self, parent=None):
super(InlineValidationPage, self).__init__(parent)
- self.connect_step_status()
- self.connect_failstep_status()
+
+ self.queue = Queue.Queue()
+ self.timer = QtCore.QTimer()
+ self.timer.timeout.connect(self.processStepsQueue)
+ self.timer.start(100)
+ self.threads = []
def do_checks(self):
- """
- launches a thread to do the checks
- """
- beupdate = self.stepChanged
- befailed = self.stepFailed
- self.checks = FunThread(
- self._do_checks(update_signal=beupdate, failed_signal=befailed))
- self.checks.finished.connect(self._inline_validation_ready)
- self.checks.begin()
- #self.checks.wait()
+
+ # yo dawg, I heard you like checks
+ # so I put a __do_checks in your do_checks
+ # for calling others' _do_checks
+
+ def __do_checks(fun=None, queue=None):
+
+ for checkcase in fun():
+ checkmsg, checkfun = checkcase
+
+ queue.put(checkmsg)
+ if checkfun() is False:
+ queue.put("failed")
+ break
+
+ t = FunThread(fun=partial(
+ __do_checks,
+ fun=self._do_checks,
+ queue=self.queue))
+ t.finished.connect(self._inline_validation_ready)
+ t.begin()
+ self.threads.append(t)
+
+ # slot
+
+ @QtCore.pyqtSlot()
+ def showStepsFrame(self):
+ self.valFrame.show()
+ self.update()
class ValidationPage(QtGui.QWizardPage, WithStepsMixIn):
diff --git a/src/leap/gui/threads.py b/src/leap/gui/threads.py
index 176c19b1..8aad8866 100644
--- a/src/leap/gui/threads.py
+++ b/src/leap/gui/threads.py
@@ -3,10 +3,16 @@ from PyQt4 import QtCore
class FunThread(QtCore.QThread):
- def __init__(self, fun, parent=None):
+ def __init__(self, fun=None, parent=None):
+
QtCore.QThread.__init__(self, parent)
+ self.exiting = False
self.fun = fun
+ def __del__(self):
+ self.exiting = True
+ self.wait()
+
def run(self):
if self.fun:
self.fun()