summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/gui/mainwindow.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/bitmask/gui/mainwindow.py')
-rw-r--r--src/leap/bitmask/gui/mainwindow.py405
1 files changed, 203 insertions, 202 deletions
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 0950462b..200d68aa 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -19,23 +19,23 @@ Main window for Bitmask.
"""
import logging
import os
-import platform
-import tempfile
-from functools import partial
import keyring
from PySide import QtCore, QtGui
from twisted.internet import threads
+from leap.bitmask import __version__ as VERSION
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.srpauth import SRPAuth
from leap.bitmask.gui.loggerwindow import LoggerWindow
-from leap.bitmask.gui.preferenceswindow import PreferencesWindow
-from leap.bitmask.gui.wizard import Wizard
from leap.bitmask.gui.login import LoginWidget
+from leap.bitmask.gui.preferenceswindow import PreferencesWindow
+from leap.bitmask.gui import statemachines
from leap.bitmask.gui.statuspanel import StatusPanelWidget
+from leap.bitmask.gui.wizard import Wizard
+
from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper
from leap.bitmask.services.eip.eipconfig import EIPConfig
from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
@@ -47,6 +47,8 @@ from leap.bitmask.services.mail import imap
from leap.bitmask.platform_init import IS_WIN, IS_MAC
from leap.bitmask.platform_init.initializers import init_platform
+from leap.bitmask.services.eip import get_openvpn_management
+from leap.bitmask.services.eip.connection import EIPConnection
from leap.bitmask.services.eip.vpnprocess import VPN
from leap.bitmask.services.eip.vpnprocess import OpenVPNAlreadyRunning
from leap.bitmask.services.eip.vpnprocess import AlienOpenVPNAlreadyRunning
@@ -58,7 +60,6 @@ from leap.bitmask.services.eip.vpnlaunchers import \
EIPNoPolkitAuthAgentAvailable
from leap.bitmask.services.eip.vpnlaunchers import EIPNoTunKextLoaded
-from leap.bitmask import __version__ as VERSION
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.leap_log_handler import LeapLogHandler
@@ -107,7 +108,6 @@ class MainWindow(QtGui.QMainWindow):
user_stopped_eip = False
def __init__(self, quit_callback,
- standalone=False,
openvpn_verb=1,
bypass_checks=False):
"""
@@ -117,10 +117,6 @@ class MainWindow(QtGui.QMainWindow):
the application.
:type quit_callback: callable
- :param standalone: Set to true if the app should use configs
- inside its pwd
- :type standalone: bool
-
:param bypass_checks: Set to true if the app should bypass
first round of checks for CA
certificates at bootstrap
@@ -147,7 +143,7 @@ class MainWindow(QtGui.QMainWindow):
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
- self._settings = LeapSettings(standalone)
+ self._settings = LeapSettings()
self._login_widget = LoginWidget(
self._settings,
@@ -171,12 +167,17 @@ class MainWindow(QtGui.QMainWindow):
self.ui.stackedWidget.setCurrentIndex(self.LOGIN_INDEX)
- self._status_panel.start_eip.connect(self._start_eip)
- self._status_panel.stop_eip.connect(self._stop_eip)
+ self._eip_connection = EIPConnection()
+
+ self._eip_connection.qtsigs.connecting_signal.connect(
+ self._start_eip)
+ self._eip_connection.qtsigs.disconnecting_signal.connect(
+ self._stop_eip)
+ self._status_panel.eip_connection_connected.connect(
+ self._on_eip_connected)
# This is loaded only once, there's a bug when doing that more
# than once
- self._standalone = standalone
self._provider_config = ProviderConfig()
# Used for automatic start of EIP
self._provisional_provider_config = ProviderConfig()
@@ -211,12 +212,20 @@ class MainWindow(QtGui.QMainWindow):
# This thread is similar to the provider bootstrapper
self._eip_bootstrapper = EIPBootstrapper()
+ # EIP signals ---- move to eip conductor.
# TODO change the name of "download_config" signal to
# something less confusing (config_ready maybe)
self._eip_bootstrapper.download_config.connect(
self._eip_intermediate_stage)
self._eip_bootstrapper.download_client_certificate.connect(
self._finish_eip_bootstrap)
+ self._vpn = VPN(openvpn_verb=openvpn_verb)
+ self._vpn.qtsigs.state_changed.connect(
+ self._status_panel.update_vpn_state)
+ self._vpn.qtsigs.status_changed.connect(
+ self._status_panel.update_vpn_status)
+ self._vpn.qtsigs.process_finished.connect(
+ self._eip_finished)
self._soledad_bootstrapper = SoledadBootstrapper()
self._soledad_bootstrapper.download_config.connect(
@@ -230,14 +239,6 @@ class MainWindow(QtGui.QMainWindow):
self._smtp_bootstrapper.download_config.connect(
self._smtp_bootstrapped_stage)
- self._vpn = VPN(openvpn_verb=openvpn_verb)
- self._vpn.qtsigs.state_changed.connect(
- self._status_panel.update_vpn_state)
- self._vpn.qtsigs.status_changed.connect(
- self._status_panel.update_vpn_status)
- self._vpn.qtsigs.process_finished.connect(
- self._eip_finished)
-
self.ui.action_log_out.setEnabled(False)
self.ui.action_log_out.triggered.connect(self._logout)
self.ui.action_about_leap.triggered.connect(self._about)
@@ -251,30 +252,12 @@ class MainWindow(QtGui.QMainWindow):
self._systray = None
- self._action_eip_provider = QtGui.QAction(
- self.tr("No default provider"), self)
- self._action_eip_provider.setEnabled(False)
-
- self._action_eip_status = QtGui.QAction(
- self.tr("Encrypted internet is OFF"),
- self)
- self._action_eip_status.setEnabled(False)
- self._status_panel.set_action_eip_status(
- self._action_eip_status)
-
- self._action_mail_status = QtGui.QAction(
- self.tr("Encrypted Mail is OFF"), self)
+ self._action_mail_status = QtGui.QAction(self.tr("Mail is OFF"), self)
self._action_mail_status.setEnabled(False)
- self._status_panel.set_action_mail_status(
- self._action_mail_status)
+ self._status_panel.set_action_mail_status(self._action_mail_status)
- self._action_eip_startstop = QtGui.QAction(
- self.tr("Turn OFF"), self)
- self._action_eip_startstop.triggered.connect(
- self._stop_eip)
- self._action_eip_startstop.setEnabled(False)
- self._status_panel.set_action_eip_startstop(
- self._action_eip_startstop)
+ self._action_eip_startstop = QtGui.QAction("", self)
+ self._status_panel.set_action_eip_startstop(self._action_eip_startstop)
self._action_preferences = QtGui.QAction(self.tr("Preferences"), self)
self._action_preferences.triggered.connect(self._show_preferences)
@@ -322,8 +305,7 @@ class MainWindow(QtGui.QMainWindow):
if self._first_run():
self._wizard_firstrun = True
- self._wizard = Wizard(standalone=standalone,
- bypass_checks=bypass_checks)
+ self._wizard = Wizard(bypass_checks=bypass_checks)
# Give this window time to finish init and then show the wizard
QtCore.QTimer.singleShot(1, self._launch_wizard)
self._wizard.accepted.connect(self._finish_init)
@@ -331,6 +313,17 @@ class MainWindow(QtGui.QMainWindow):
else:
self._finish_init()
+ # Eip machine is a public attribute where the state machine for
+ # the eip connection will be available to the different components.
+ # Remember that this will not live in the +1600LOC mainwindow for
+ # all the eternity, so at some point we will be moving this to
+ # the EIPConductor or some other clever component that we will
+ # instantiate from here.
+ self.eip_machine = None
+
+ # start event machines
+ self.start_eip_machine()
+
def _rejected_wizard(self):
"""
SLOT
@@ -428,8 +421,7 @@ class MainWindow(QtGui.QMainWindow):
Displays the preferences window.
"""
- preferences_window = PreferencesWindow(
- self, self._srp_auth, self._settings, self._standalone)
+ preferences_window = PreferencesWindow(self, self._srp_auth)
if self._soledad_ready:
preferences_window.set_soledad_ready(self._soledad)
@@ -594,8 +586,6 @@ class MainWindow(QtGui.QMainWindow):
"no default provider configured")
return
- self._action_eip_provider.setText(default_provider)
-
self._enabled_services = self._settings.get_enabled_services(
default_provider)
@@ -604,6 +594,10 @@ class MainWindow(QtGui.QMainWindow):
"providers",
default_provider,
"provider.json")):
+ # XXX I think we should not try to re-download config every time,
+ # it adds some delay.
+ # Maybe if it's the first run in a session,
+ # or we can try only if it fails.
self._download_eip_config()
else:
# XXX: Display a proper message to the user
@@ -626,9 +620,11 @@ class MainWindow(QtGui.QMainWindow):
systrayMenu = QtGui.QMenu(self)
systrayMenu.addAction(self._action_visible)
systrayMenu.addSeparator()
- systrayMenu.addAction(self._action_eip_provider)
- systrayMenu.addAction(self._action_eip_status)
- systrayMenu.addAction(self._action_eip_startstop)
+
+ eip_menu = systrayMenu.addMenu(self.tr("Encrypted Internet is OFF"))
+ eip_menu.addAction(self._action_eip_startstop)
+ self._status_panel.set_eip_status_menu(eip_menu)
+
systrayMenu.addAction(self._action_mail_status)
systrayMenu.addSeparator()
systrayMenu.addAction(self._action_preferences)
@@ -682,14 +678,23 @@ class MainWindow(QtGui.QMainWindow):
Toggles the window visibility
"""
visible = self.isVisible() and self.isActiveWindow()
+ qApp = QtCore.QCoreApplication.instance()
+
if not visible:
+ qApp.setQuitOnLastWindowClosed(True)
self.show()
self.activateWindow()
self.raise_()
else:
+ # We set this in order to avoid dialogs shutting down the
+ # app on close, as they will be the only visible window.
+ # e.g.: PreferencesWindow, LoggerWindow
+ qApp.setQuitOnLastWindowClosed(False)
self.hide()
- self._update_hideshow_menu()
+ # Wait a bit until the window visibility has changed so
+ # the menu is set with the correct value.
+ QtCore.QTimer.singleShot(500, self._update_hideshow_menu)
def _center_window(self):
"""
@@ -957,6 +962,7 @@ class MainWindow(QtGui.QMainWindow):
self._login_widget.set_enabled(True)
def _switch_to_status(self):
+ # TODO this method name is confusing as hell.
"""
Changes the stackedWidget index to the EIP status one and
triggers the eip bootstrapping
@@ -968,12 +974,13 @@ class MainWindow(QtGui.QMainWindow):
self.ui.stackedWidget.setCurrentIndex(self.EIP_STATUS_INDEX)
+ # TODO separate UI from logic.
+ # TODO soledad should check if we want to run only over EIP.
self._soledad_bootstrapper.run_soledad_setup_checks(
self._provider_config,
self._login_widget.get_user(),
self._login_widget.get_password(),
- download_if_needed=True,
- standalone=self._standalone)
+ download_if_needed=True)
self._download_eip_config()
@@ -1048,17 +1055,6 @@ class MainWindow(QtGui.QMainWindow):
self._provider_config,
self._smtp_config,
True)
- else:
- if self._enabled_services.count(self.MX_SERVICE) > 0:
- pass # TODO show MX status
- #self._status_panel.set_eip_status(
- # self.tr("%s does not support MX") %
- # (self._provider_config.get_domain(),),
- # error=True)
- else:
- pass # TODO show MX status
- #self._status_panel.set_eip_status(
- # self.tr("MX is disabled"))
###################################################################
# Service control methods: smtp
@@ -1081,7 +1077,12 @@ class MainWindow(QtGui.QMainWindow):
logger.error(data[self._smtp_bootstrapper.ERROR_KEY])
return
logger.debug("Done bootstrapping SMTP")
+ self._check_smtp_config()
+ def _check_smtp_config(self):
+ """
+ Checks smtp config and tries to download smtp client cert if needed.
+ """
hosts = self._smtp_config.get_hosts()
# TODO handle more than one host and define how to choose
if len(hosts) > 0:
@@ -1089,24 +1090,40 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("Using hostname %s for SMTP" % (hostname,))
host = hosts[hostname][self.IP_KEY].encode("utf-8")
port = hosts[hostname][self.PORT_KEY]
- # TODO move the start to _start_smtp_service
-
- # TODO Make the encrypted_only configurable
- # TODO pick local smtp port in a better way
- # TODO remove hard-coded port and let leap.mail set
- # the specific default.
-
- from leap.mail.smtp import setup_smtp_relay
- client_cert = self._eip_config.get_client_cert_path(
- self._provider_config)
- self._smtp_service = setup_smtp_relay(
- port=2013,
- keymanager=self._keymanager,
- smtp_host=host,
- smtp_port=port,
- smtp_cert=client_cert,
- smtp_key=client_cert,
- encrypted_only=False)
+
+ client_cert = self._smtp_config.get_client_cert_path(
+ self._provider_config,
+ about_to_download=True)
+
+ if not os.path.isfile(client_cert):
+ self._smtp_bootstrapper._download_client_certificates()
+ if os.path.isfile(client_cert):
+ self._start_smtp_service(host, port, client_cert)
+ else:
+ logger.warning("Tried to download email client "
+ "certificate, but could not find any")
+
+ else:
+ logger.warning("No smtp hosts configured")
+
+ def _start_smtp_service(self, host, port, cert):
+ """
+ Starts the smtp service.
+ """
+ # TODO Make the encrypted_only configurable
+ # TODO pick local smtp port in a better way
+ # TODO remove hard-coded port and let leap.mail set
+ # the specific default.
+
+ from leap.mail.smtp import setup_smtp_relay
+ self._smtp_service = setup_smtp_relay(
+ port=2013,
+ keymanager=self._keymanager,
+ smtp_host=host,
+ smtp_port=port,
+ smtp_cert=cert,
+ smtp_key=cert,
+ encrypted_only=False)
def _stop_smtp_service(self):
"""
@@ -1174,26 +1191,36 @@ class MainWindow(QtGui.QMainWindow):
###################################################################
# Service control methods: eip
- def _get_socket_host(self):
+ def start_eip_machine(self):
"""
- Returns the socket and port to be used for VPN
-
- :rtype: tuple (str, str) (host, port)
+ Initializes and starts the EIP state machine
"""
- # TODO make this properly multiplatform
- # TODO get this out of gui/
+ button = self._status_panel.eip_button
+ action = self._action_eip_startstop
+ label = self._status_panel.eip_label
+ builder = statemachines.ConnectionMachineBuilder(self._eip_connection)
+ eip_machine = builder.make_machine(button=button,
+ action=action,
+ label=label)
+ self.eip_machine = eip_machine
+ self.eip_machine.start()
- if platform.system() == "Windows":
- host = "localhost"
- port = "9876"
- else:
- # XXX cleanup this on exit too
- host = os.path.join(tempfile.mkdtemp(prefix="leap-tmp"),
- 'openvpn.socket')
- port = "unix"
+ @QtCore.Slot()
+ def _on_eip_connected(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._status_panel.eip_connection_connected
+ Emits the EIPConnection.qtsigs.connected_signal
- return host, port
+ This is a little workaround for connecting the vpn-connected
+ signal that currently is beeing processed under status_panel.
+ After the refactor to EIPConductor this should not be necessary.
+ """
+ logger.debug('EIP connected signal received ...')
+ self._eip_connection.qtsigs.connected_signal.emit()
+ @QtCore.Slot()
def _start_eip(self):
"""
SLOT
@@ -1204,35 +1231,27 @@ class MainWindow(QtGui.QMainWindow):
Starts EIP
"""
+ provider_config = self._get_best_provider_config()
+ provider = provider_config.get_domain()
self._status_panel.eip_pre_up()
self.user_stopped_eip = False
- provider_config = self._get_best_provider_config()
try:
- host, port = self._get_socket_host()
+ # XXX move this to EIPConductor
+ host, port = get_openvpn_management()
self._vpn.start(eipconfig=self._eip_config,
providerconfig=provider_config,
socket_host=host,
socket_port=port)
-
- self._settings.set_defaultprovider(
- provider_config.get_domain())
-
- provider = provider_config.get_domain()
+ self._settings.set_defaultprovider(provider)
if self._logged_user is not None:
provider = "%s@%s" % (self._logged_user, provider)
+ # XXX move to the state machine too
self._status_panel.set_provider(provider)
- self._action_eip_provider.setText(provider_config.get_domain())
-
- self._status_panel.eip_started()
-
- # XXX refactor into status_panel method?
- self._action_eip_startstop.setText(self.tr("Turn OFF"))
- self._action_eip_startstop.disconnect(self)
- self._action_eip_startstop.triggered.connect(
- self._stop_eip)
+ # TODO refactor exceptions so they provide translatable
+ # usef-facing messages.
except EIPNoPolkitAuthAgentAvailable:
self._status_panel.set_global_status(
# XXX this should change to polkit-kde where
@@ -1284,26 +1303,7 @@ class MainWindow(QtGui.QMainWindow):
else:
self._already_started_eip = True
- def _set_eipstatus_off(self):
- """
- Sets eip status to off
- """
- self._status_panel.set_eip_status(self.tr("OFF"), error=True)
- self._status_panel.set_eip_status_icon("error")
- self._status_panel.set_startstop_enabled(True)
- self._status_panel.eip_stopped()
-
- self._set_action_eipstart_off()
-
- def _set_action_eipstart_off(self):
- """
- Sets eip startstop action to OFF status.
- """
- self._action_eip_startstop.setText(self.tr("Turn ON"))
- self._action_eip_startstop.disconnect(self)
- self._action_eip_startstop.triggered.connect(
- self._start_eip)
-
+ @QtCore.Slot()
def _stop_eip(self, abnormal=False):
"""
SLOT
@@ -1327,34 +1327,20 @@ class MainWindow(QtGui.QMainWindow):
self._set_eipstatus_off()
self._already_started_eip = False
+
+ # XXX do via signal
self._settings.set_defaultprovider(None)
if self._logged_user:
self._status_panel.set_provider(
"%s@%s" % (self._logged_user,
self._get_best_provider_config().get_domain()))
- def _get_best_provider_config(self):
+ def _set_eipstatus_off(self):
"""
- Returns the best ProviderConfig to use at a moment. We may
- have to use self._provider_config or
- self._provisional_provider_config depending on the start
- status.
-
- :rtype: ProviderConfig
+ Sets eip status to off
"""
- leap_assert(self._provider_config is not None or
- self._provisional_provider_config is not None,
- "We need a provider config")
-
- provider_config = None
- if self._provider_config.loaded():
- provider_config = self._provider_config
- elif self._provisional_provider_config.loaded():
- provider_config = self._provisional_provider_config
- else:
- leap_assert(False, "We could not find any usable ProviderConfig.")
-
- return provider_config
+ self._status_panel.set_eip_status(self.tr("OFF"), error=True)
+ self._status_panel.set_eip_status_icon("error")
def _download_eip_config(self):
"""
@@ -1368,6 +1354,7 @@ class MainWindow(QtGui.QMainWindow):
self._enabled_services.count(self.OPENVPN_SERVICE) > 0 and \
not self._already_started_eip:
+ # XXX this should be handled by the state machine.
self._status_panel.set_eip_status(
self.tr("Starting..."))
self._eip_bootstrapper.run_eip_setup_checks(
@@ -1381,7 +1368,6 @@ class MainWindow(QtGui.QMainWindow):
error=True)
else:
self._status_panel.set_eip_status(self.tr("Disabled"))
- self._status_panel.set_startstop_enabled(False)
def _finish_eip_bootstrap(self, data):
"""
@@ -1402,7 +1388,6 @@ class MainWindow(QtGui.QMainWindow):
return
provider_config = self._get_best_provider_config()
-
domain = provider_config.get_domain()
loaded = self._eip_config.loaded()
@@ -1414,13 +1399,41 @@ class MainWindow(QtGui.QMainWindow):
loaded = self._eip_config.load(eip_config_path)
if loaded:
- self._start_eip()
+ # DO START EIP Connection!
+ self._eip_connection.qtsigs.do_connect_signal.emit()
else:
self._status_panel.set_eip_status(
self.tr("Could not load Encrypted Internet "
"Configuration."),
error=True)
+ # end eip methods -------------------------------------------
+
+ def _get_best_provider_config(self):
+ """
+ Returns the best ProviderConfig to use at a moment. We may
+ have to use self._provider_config or
+ self._provisional_provider_config depending on the start
+ status.
+
+ :rtype: ProviderConfig
+ """
+ # TODO move this out of gui.
+ leap_assert(self._provider_config is not None or
+ self._provisional_provider_config is not None,
+ "We need a provider config")
+
+ provider_config = None
+ if self._provider_config.loaded():
+ provider_config = self._provider_config
+ elif self._provisional_provider_config.loaded():
+ provider_config = self._provisional_provider_config
+ else:
+ leap_assert(False, "We could not find any usable ProviderConfig.")
+
+ return provider_config
+
+ @QtCore.Slot()
def _logout(self):
"""
SLOT
@@ -1444,13 +1457,16 @@ class MainWindow(QtGui.QMainWindow):
Switches the stackedWidget back to the login stage after
logging out
"""
- self._logged_user = None
- self.ui.action_log_out.setEnabled(False)
- self.ui.stackedWidget.setCurrentIndex(self.LOGIN_INDEX)
- self._login_widget.set_password("")
- self._login_widget.set_enabled(True)
- self._login_widget.set_status("")
- self.ui.btnPreferences.setEnabled(False)
+ if ok:
+ self._logged_user = None
+ self.ui.action_log_out.setEnabled(False)
+ self.ui.stackedWidget.setCurrentIndex(self.LOGIN_INDEX)
+ self._login_widget.set_password("")
+ self._login_widget.set_enabled(True)
+ self._login_widget.set_status("")
+ else:
+ status_text = self.tr("Something went wrong with the logout.")
+ self._status_panel.set_global_status(status_text, error=True)
def _intermediate_stage(self, data):
"""
@@ -1498,6 +1514,7 @@ class MainWindow(QtGui.QMainWindow):
Triggered when the EIP/VPN process finishes to set the UI
accordingly.
"""
+ # TODO move to EIPConductor.
logger.info("VPN process finished with exitCode %s..."
% (exitCode,))
@@ -1532,7 +1549,20 @@ class MainWindow(QtGui.QMainWindow):
if exitCode == 0 and IS_MAC:
# XXX remove this warning after I fix cocoasudo.
logger.warning("The above exit code MIGHT BE WRONG.")
- self._stop_eip(abnormal)
+
+ # We emit signals to trigger transitions in the state machine:
+ qtsigs = self._eip_connection.qtsigs
+ if abnormal:
+ signal = qtsigs.connection_died_signal
+ else:
+ signal = qtsigs.disconnected_signal
+
+ # XXX verify that the logic kees the same w/o the abnormal flag
+ # after the refactor to EIPConnection has been completed
+ # (eipconductor taking the most of the logic under transitions
+ # that right now are handled under status_panel)
+ #self._stop_eip(abnormal)
+ signal.emit()
def _on_raise_window_event(self, req):
"""
@@ -1611,6 +1641,11 @@ class MainWindow(QtGui.QMainWindow):
"""
# TODO separate the shutting down of services from the
# UI stuff.
+
+ # Set this in case that the app is hidden
+ qApp = QtCore.QCoreApplication.instance()
+ qApp.setQuitOnLastWindowClosed(True)
+
self._cleanup_and_quit()
self._really_quit = True
@@ -1627,37 +1662,3 @@ class MainWindow(QtGui.QMainWindow):
self._quit_callback()
logger.debug('Bye.')
-
-
-if __name__ == "__main__":
- import signal
-
- def sigint_handler(*args, **kwargs):
- logger.debug('SIGINT catched. shutting down...')
- mainwindow = args[0]
- mainwindow.quit()
-
- import sys
-
- logger = logging.getLogger(name='leap')
- logger.setLevel(logging.DEBUG)
- console = logging.StreamHandler()
- console.setLevel(logging.DEBUG)
- formatter = logging.Formatter(
- '%(asctime)s '
- '- %(name)s - %(levelname)s - %(message)s')
- console.setFormatter(formatter)
- logger.addHandler(console)
-
- app = QtGui.QApplication(sys.argv)
- mainwindow = MainWindow()
- mainwindow.show()
-
- timer = QtCore.QTimer()
- timer.start(500)
- timer.timeout.connect(lambda: None)
-
- sigint = partial(sigint_handler, mainwindow)
- signal.signal(signal.SIGINT, sigint)
-
- sys.exit(app.exec_())