Another attempt at removing the visual glitches
[gc-dialer] / src / dialcentral_qt.py
1 #!/usr/bin/env python
2 # -*- coding: UTF8 -*-
3
4 from __future__ import with_statement
5
6 import os
7 import base64
8 import ConfigParser
9 import functools
10 import logging
11
12 from PyQt4 import QtGui
13 from PyQt4 import QtCore
14
15 import constants
16 from util import qtpie
17 from util import qwrappers
18 from util import qui_utils
19 from util import misc as misc_utils
20
21 import session
22
23
24 _moduleLogger = logging.getLogger(__name__)
25
26
27 class LedWrapper(object):
28
29         def __init__(self):
30                 self._ledHandler = None
31                 self._init = False
32
33         def off(self):
34                 self._lazy_init()
35                 if self._ledHandler is not None:
36                         self._ledHandler.off()
37
38         def _lazy_init(self):
39                 if self._init:
40                         return
41                 self._init = True
42                 try:
43                         import led_handler
44                         self._ledHandler = led_handler.LedHandler()
45                 except Exception, e:
46                         _moduleLogger.exception('Unable to initialize LED Handling: "%s"' % str(e))
47                         self._ledHandler = None
48
49
50 class Dialcentral(qwrappers.ApplicationWrapper):
51
52         _DATA_PATHS = [
53                 os.path.join(os.path.dirname(__file__), "../share"),
54                 os.path.join(os.path.dirname(__file__), "../data"),
55         ]
56
57         def __init__(self, app):
58                 self._dataPath = None
59                 self._aboutDialog = None
60                 self._ledHandler = LedWrapper()
61                 self.notifyOnMissed = False
62                 self.notifyOnVoicemail = False
63                 self.notifyOnSms = False
64
65                 try:
66                         import alarm_handler
67                         if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
68                                 self._alarmHandler = alarm_handler.AlarmHandler()
69                         else:
70                                 self._alarmHandler = None
71                 except (ImportError, OSError):
72                         self._alarmHandler = None
73                 except Exception:
74                         _moduleLogger.exception("Notification failure")
75                         self._alarmHandler = None
76                 if self._alarmHandler is None:
77                         _moduleLogger.info("No notification support")
78
79                 qwrappers.ApplicationWrapper.__init__(self, app, constants)
80
81         def load_settings(self):
82                 try:
83                         config = ConfigParser.SafeConfigParser()
84                         config.read(constants._user_settings_)
85                 except IOError, e:
86                         _moduleLogger.info("No settings")
87                         return
88                 except ValueError:
89                         _moduleLogger.info("Settings were corrupt")
90                         return
91                 except ConfigParser.MissingSectionHeaderError:
92                         _moduleLogger.info("Settings were corrupt")
93                         return
94                 except Exception:
95                         _moduleLogger.exception("Unknown loading error")
96
97                 blobs = "", ""
98                 isFullscreen = False
99                 isPortrait = qui_utils.screen_orientation() == QtCore.Qt.Vertical
100                 tabIndex = 0
101                 try:
102                         blobs = [
103                                 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
104                                 for i in xrange(len(self._mainWindow.get_default_credentials()))
105                         ]
106                         isFullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen")
107                         tabIndex = config.getint(constants.__pretty_app_name__, "tab")
108                         isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait")
109                 except ConfigParser.NoOptionError, e:
110                         _moduleLogger.info(
111                                 "Settings file %s is missing option %s" % (
112                                         constants._user_settings_,
113                                         e.option,
114                                 ),
115                         )
116                 except ConfigParser.NoSectionError, e:
117                         _moduleLogger.info(
118                                 "Settings file %s is missing section %s" % (
119                                         constants._user_settings_,
120                                         e.section,
121                                 ),
122                         )
123                 except Exception:
124                         _moduleLogger.exception("Unknown loading error")
125
126                 if self._alarmHandler is not None:
127                         try:
128                                 self._alarmHandler.load_settings(config, "alarm")
129                                 self.notifyOnMissed = config.getboolean("2 - Account Info", "notifyOnMissed")
130                                 self.notifyOnVoicemail = config.getboolean("2 - Account Info", "notifyOnVoicemail")
131                                 self.notifyOnSms = config.getboolean("2 - Account Info", "notifyOnSms")
132                         except ConfigParser.NoOptionError, e:
133                                 _moduleLogger.info(
134                                         "Settings file %s is missing option %s" % (
135                                                 constants._user_settings_,
136                                                 e.option,
137                                         ),
138                                 )
139                         except ConfigParser.NoSectionError, e:
140                                 _moduleLogger.info(
141                                         "Settings file %s is missing section %s" % (
142                                                 constants._user_settings_,
143                                                 e.section,
144                                         ),
145                                 )
146                         except Exception:
147                                 _moduleLogger.exception("Unknown loading error")
148
149                 creds = (
150                         base64.b64decode(blob)
151                         for blob in blobs
152                 )
153                 self._mainWindow.set_default_credentials(*creds)
154                 self._fullscreenAction.setChecked(isFullscreen)
155                 self._orientationAction.setChecked(isPortrait)
156                 self._mainWindow.set_current_tab(tabIndex)
157                 self._mainWindow.load_settings(config)
158
159         def save_settings(self):
160                 _moduleLogger.info("Saving settings")
161                 config = ConfigParser.SafeConfigParser()
162
163                 config.add_section(constants.__pretty_app_name__)
164                 config.set(constants.__pretty_app_name__, "tab", str(self._mainWindow.get_current_tab()))
165                 config.set(constants.__pretty_app_name__, "fullscreen", str(self._fullscreenAction.isChecked()))
166                 config.set(constants.__pretty_app_name__, "portrait", str(self._orientationAction.isChecked()))
167                 for i, value in enumerate(self._mainWindow.get_default_credentials()):
168                         blob = base64.b64encode(value)
169                         config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
170
171                 if self._alarmHandler is not None:
172                         config.add_section("alarm")
173                         self._alarmHandler.save_settings(config, "alarm")
174                 config.add_section("2 - Account Info")
175                 config.set("2 - Account Info", "notifyOnMissed", repr(self.notifyOnMissed))
176                 config.set("2 - Account Info", "notifyOnVoicemail", repr(self.notifyOnVoicemail))
177                 config.set("2 - Account Info", "notifyOnSms", repr(self.notifyOnSms))
178
179                 self._mainWindow.save_settings(config)
180
181                 with open(constants._user_settings_, "wb") as configFile:
182                         config.write(configFile)
183
184         def get_icon(self, name):
185                 if self._dataPath is None:
186                         for path in self._DATA_PATHS:
187                                 if os.path.exists(os.path.join(path, name)):
188                                         self._dataPath = path
189                                         break
190                 if self._dataPath is not None:
191                         icon = QtGui.QIcon(os.path.join(self._dataPath, name))
192                         return icon
193                 else:
194                         return None
195
196         def _close_windows(self):
197                 qwrappers.ApplicationWrapper._close_windows(self)
198                 if self._aboutDialog  is not None:
199                         self._aboutDialog.close()
200
201         @property
202         def fsContactsPath(self):
203                 return os.path.join(constants._data_path_, "contacts")
204
205         @property
206         def alarmHandler(self):
207                 return self._alarmHandler
208
209         @property
210         def ledHandler(self):
211                 return self._ledHandler
212
213         def _new_main_window(self):
214                 return MainWindow(None, self)
215
216         @QtCore.pyqtSlot()
217         @QtCore.pyqtSlot(bool)
218         @misc_utils.log_exception(_moduleLogger)
219         def _on_about(self, checked = True):
220                 with qui_utils.notify_error(self._errorLog):
221                         if self._aboutDialog is None:
222                                 import dialogs
223                                 self._aboutDialog = dialogs.AboutDialog(self)
224                         response = self._aboutDialog.run(self._mainWindow.window)
225
226
227 class DelayedWidget(object):
228
229         def __init__(self, app, settingsNames):
230                 self._layout = QtGui.QVBoxLayout()
231                 self._layout.setContentsMargins(0, 0, 0, 0)
232                 self._widget = QtGui.QWidget()
233                 self._widget.setContentsMargins(0, 0, 0, 0)
234                 self._widget.setLayout(self._layout)
235                 self._settings = dict((name, "") for name in settingsNames)
236
237                 self._child = None
238                 self._isEnabled = True
239
240         @property
241         def toplevel(self):
242                 return self._widget
243
244         def has_child(self):
245                 return self._child is not None
246
247         def set_child(self, child):
248                 if self._child is not None:
249                         self._layout.removeWidget(self._child.toplevel)
250                 self._child = child
251                 if self._child is not None:
252                         self._layout.addWidget(self._child.toplevel)
253
254                 self._child.set_settings(self._settings)
255
256                 if self._isEnabled:
257                         self._child.enable()
258                 else:
259                         self._child.disable()
260
261         def enable(self):
262                 self._isEnabled = True
263                 if self._child is not None:
264                         self._child.enable()
265
266         def disable(self):
267                 self._isEnabled = False
268                 if self._child is not None:
269                         self._child.disable()
270
271         def clear(self):
272                 if self._child is not None:
273                         self._child.clear()
274
275         def refresh(self, force=True):
276                 if self._child is not None:
277                         self._child.refresh(force)
278
279         def get_settings(self):
280                 if self._child is not None:
281                         return self._child.get_settings()
282                 else:
283                         return self._settings
284
285         def set_settings(self, settings):
286                 if self._child is not None:
287                         self._child.set_settings(settings)
288                 else:
289                         self._settings = settings
290
291
292 def _tab_factory(tab, app, session, errorLog):
293         import gv_views
294         return gv_views.__dict__[tab](app, session, errorLog)
295
296
297 class MainWindow(qwrappers.WindowWrapper):
298
299         KEYPAD_TAB = 0
300         RECENT_TAB = 1
301         MESSAGES_TAB = 2
302         CONTACTS_TAB = 3
303         MAX_TABS = 4
304
305         _TAB_TITLES = [
306                 "Dialpad",
307                 "History",
308                 "Messages",
309                 "Contacts",
310         ]
311         assert len(_TAB_TITLES) == MAX_TABS
312
313         _TAB_ICONS = [
314                 "dialpad.png",
315                 "history.png",
316                 "messages.png",
317                 "contacts.png",
318         ]
319         assert len(_TAB_ICONS) == MAX_TABS
320
321         _TAB_CLASS = [
322                 functools.partial(_tab_factory, "Dialpad"),
323                 functools.partial(_tab_factory, "History"),
324                 functools.partial(_tab_factory, "Messages"),
325                 functools.partial(_tab_factory, "Contacts"),
326         ]
327         assert len(_TAB_CLASS) == MAX_TABS
328
329         # Hack to allow delay importing/loading of tabs
330         _TAB_SETTINGS_NAMES = [
331                 (),
332                 ("filter", ),
333                 ("status", "type"),
334                 ("selectedAddressbook", ),
335         ]
336         assert len(_TAB_SETTINGS_NAMES) == MAX_TABS
337
338         def __init__(self, parent, app):
339                 qwrappers.WindowWrapper.__init__(self, parent, app)
340                 self._window.setWindowTitle("%s" % constants.__pretty_app_name__)
341                 #self._freezer = qwrappers.AutoFreezeWindowFeature(self._app, self._window)
342                 self._errorLog = self._app.errorLog
343
344                 self._session = session.Session(self._errorLog, constants._data_path_)
345                 self._session.error.connect(self._on_session_error)
346                 self._session.loggedIn.connect(self._on_login)
347                 self._session.loggedOut.connect(self._on_logout)
348                 self._session.draft.recipientsChanged.connect(self._on_recipients_changed)
349                 self._defaultCredentials = "", ""
350                 self._curentCredentials = "", ""
351                 self._currentTab = 0
352
353                 self._credentialsDialog = None
354                 self._smsEntryDialog = None
355                 self._accountDialog = None
356
357                 self._tabsContents = [
358                         DelayedWidget(self._app, self._TAB_SETTINGS_NAMES[i])
359                         for i in xrange(self.MAX_TABS)
360                 ]
361                 for tab in self._tabsContents:
362                         tab.disable()
363
364                 self._tabWidget = QtGui.QTabWidget()
365                 if qui_utils.screen_orientation() == QtCore.Qt.Vertical:
366                         self._tabWidget.setTabPosition(QtGui.QTabWidget.South)
367                 else:
368                         self._tabWidget.setTabPosition(QtGui.QTabWidget.West)
369                 defaultTabIconSize = self._tabWidget.iconSize()
370                 defaultTabIconWidth, defaultTabIconHeight = defaultTabIconSize.width(), defaultTabIconSize.height()
371                 for tabIndex, (tabTitle, tabIcon) in enumerate(
372                         zip(self._TAB_TITLES, self._TAB_ICONS)
373                 ):
374                         icon = self._app.get_icon(tabIcon)
375                         if constants.IS_MAEMO and icon is not None:
376                                 tabTitle = ""
377
378                         if icon is None:
379                                 self._tabWidget.addTab(self._tabsContents[tabIndex].toplevel, tabTitle)
380                         else:
381                                 iconSize = icon.availableSizes()[0]
382                                 defaultTabIconWidth = max(defaultTabIconWidth, iconSize.width())
383                                 defaultTabIconHeight = max(defaultTabIconHeight, iconSize.height())
384                                 self._tabWidget.addTab(self._tabsContents[tabIndex].toplevel, icon, tabTitle)
385                 defaultTabIconWidth = max(defaultTabIconWidth, 32)
386                 defaultTabIconHeight = max(defaultTabIconHeight, 32)
387                 self._tabWidget.setIconSize(QtCore.QSize(defaultTabIconWidth, defaultTabIconHeight))
388                 self._tabWidget.currentChanged.connect(self._on_tab_changed)
389                 self._tabWidget.setContentsMargins(0, 0, 0, 0)
390
391                 self._layout.addWidget(self._tabWidget)
392
393                 self._loginTabAction = QtGui.QAction(None)
394                 self._loginTabAction.setText("Login")
395                 self._loginTabAction.triggered.connect(self._on_login_requested)
396
397                 self._importTabAction = QtGui.QAction(None)
398                 self._importTabAction.setText("Import")
399                 self._importTabAction.triggered.connect(self._on_import)
400
401                 self._accountTabAction = QtGui.QAction(None)
402                 self._accountTabAction.setText("Account")
403                 self._accountTabAction.triggered.connect(self._on_account)
404
405                 self._refreshTabAction = QtGui.QAction(None)
406                 self._refreshTabAction.setText("Refresh")
407                 self._refreshTabAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
408                 self._refreshTabAction.triggered.connect(self._on_refresh)
409
410                 fileMenu = self._window.menuBar().addMenu("&File")
411                 fileMenu.addAction(self._loginTabAction)
412                 fileMenu.addAction(self._refreshTabAction)
413
414                 toolsMenu = self._window.menuBar().addMenu("&Tools")
415                 toolsMenu.addAction(self._accountTabAction)
416                 toolsMenu.addAction(self._importTabAction)
417                 toolsMenu.addAction(self._app.aboutAction)
418
419                 self._initialize_tab(self._tabWidget.currentIndex())
420                 self.set_fullscreen(self._app.fullscreenAction.isChecked())
421                 self.set_orientation(self._app.orientationAction.isChecked())
422
423         def set_default_credentials(self, username, password):
424                 self._defaultCredentials = username, password
425
426         def get_default_credentials(self):
427                 return self._defaultCredentials
428
429         def walk_children(self):
430                 if self._smsEntryDialog is not None:
431                         return (self._smsEntryDialog, )
432                 else:
433                         return ()
434
435         def start(self):
436                 qwrappers.WindowWrapper.start(self)
437                 assert self._session.state == self._session.LOGGEDOUT_STATE, "Initialization messed up"
438                 if self._defaultCredentials != ("", ""):
439                         username, password = self._defaultCredentials[0], self._defaultCredentials[1]
440                         self._curentCredentials = username, password
441                         self._session.login(username, password)
442                 else:
443                         self._prompt_for_login()
444
445         def close(self):
446                 for diag in (
447                         self._credentialsDialog,
448                         self._accountDialog,
449                 ):
450                         if diag is not None:
451                                 diag.close()
452                 for child in self.walk_children():
453                         child.window.destroyed.disconnect(self._on_child_close)
454                         child.window.closed.disconnect(self._on_child_close)
455                         child.close()
456                 self._window.close()
457
458         def destroy(self):
459                 qwrappers.WindowWrapper.destroy(self)
460                 if self._session.state != self._session.LOGGEDOUT_STATE:
461                         self._session.logout()
462
463         def get_current_tab(self):
464                 return self._currentTab
465
466         def set_current_tab(self, tabIndex):
467                 self._tabWidget.setCurrentIndex(tabIndex)
468
469         def load_settings(self, config):
470                 backendId = 2 # For backwards compatibility
471                 for tabIndex, tabTitle in enumerate(self._TAB_TITLES):
472                         sectionName = "%s - %s" % (backendId, tabTitle)
473                         settings = self._tabsContents[tabIndex].get_settings()
474                         for settingName in settings.iterkeys():
475                                 try:
476                                         settingValue = config.get(sectionName, settingName)
477                                 except ConfigParser.NoOptionError, e:
478                                         _moduleLogger.info(
479                                                 "Settings file %s is missing section %s" % (
480                                                         constants._user_settings_,
481                                                         e.section,
482                                                 ),
483                                         )
484                                         return
485                                 except ConfigParser.NoSectionError, e:
486                                         _moduleLogger.info(
487                                                 "Settings file %s is missing section %s" % (
488                                                         constants._user_settings_,
489                                                         e.section,
490                                                 ),
491                                         )
492                                         return
493                                 except Exception:
494                                         _moduleLogger.exception("Unknown loading error")
495                                         return
496                                 settings[settingName] = settingValue
497                         self._tabsContents[tabIndex].set_settings(settings)
498
499         def save_settings(self, config):
500                 backendId = 2 # For backwards compatibility
501                 for tabIndex, tabTitle in enumerate(self._TAB_TITLES):
502                         sectionName = "%s - %s" % (backendId, tabTitle)
503                         config.add_section(sectionName)
504                         tabSettings = self._tabsContents[tabIndex].get_settings()
505                         for settingName, settingValue in tabSettings.iteritems():
506                                 config.set(sectionName, settingName, settingValue)
507
508         def set_orientation(self, isPortrait):
509                 qwrappers.WindowWrapper.set_orientation(self, isPortrait)
510                 if isPortrait:
511                         self._tabWidget.setTabPosition(QtGui.QTabWidget.South)
512                 else:
513                         self._tabWidget.setTabPosition(QtGui.QTabWidget.West)
514
515         def _initialize_tab(self, index):
516                 assert index < self.MAX_TABS, "Invalid tab"
517                 if not self._tabsContents[index].has_child():
518                         tab = self._TAB_CLASS[index](self._app, self._session, self._errorLog)
519                         self._tabsContents[index].set_child(tab)
520                         self._tabsContents[index].refresh(force=False)
521
522         def _prompt_for_login(self):
523                 if self._credentialsDialog is None:
524                         import dialogs
525                         self._credentialsDialog = dialogs.CredentialsDialog(self._app)
526                 credentials = self._credentialsDialog.run(
527                         self._defaultCredentials[0], self._defaultCredentials[1], self.window
528                 )
529                 if credentials is None:
530                         return
531                 username, password = credentials
532                 self._curentCredentials = username, password
533                 self._session.login(username, password)
534
535         def _show_account_dialog(self):
536                 if self._accountDialog is None:
537                         import dialogs
538                         self._accountDialog = dialogs.AccountDialog(self._app)
539                         if self._app.alarmHandler is None:
540                                 self._accountDialog.setIfNotificationsSupported(False)
541                 if self._app.alarmHandler is not None:
542                         self._accountDialog.notifications = self._app.alarmHandler.isEnabled
543                         self._accountDialog.notificationTime = self._app.alarmHandler.recurrence
544                         self._accountDialog.notifyOnMissed = self._app.notifyOnMissed
545                         self._accountDialog.notifyOnVoicemail = self._app.notifyOnVoicemail
546                         self._accountDialog.notifyOnSms = self._app.notifyOnSms
547                 self._accountDialog.set_callbacks(
548                         self._session.get_callback_numbers(), self._session.get_callback_number()
549                 )
550                 self._accountDialog.accountNumber = self._session.get_account_number()
551                 response = self._accountDialog.run(self.window)
552                 if response == QtGui.QDialog.Accepted:
553                         if self._accountDialog.doClear:
554                                 self._session.logout_and_clear()
555                         else:
556                                 callbackNumber = self._accountDialog.selectedCallback
557                                 self._session.set_callback_number(callbackNumber)
558                         if self._app.alarmHandler is not None:
559                                 self._app.alarmHandler.apply_settings(self._accountDialog.notifications, self._accountDialog.notificationTime)
560                                 self._app.notifyOnMissed = self._accountDialog.notifyOnMissed
561                                 self._app.notifyOnVoicemail = self._accountDialog.notifyOnVoicemail
562                                 self._app.notifyOnSms = self._accountDialog.notifyOnSms
563                 elif response == QtGui.QDialog.Rejected:
564                         _moduleLogger.info("Cancelled")
565                 else:
566                         _moduleLogger.info("Unknown response")
567
568         @QtCore.pyqtSlot(str)
569         @misc_utils.log_exception(_moduleLogger)
570         def _on_session_error(self, message):
571                 with qui_utils.notify_error(self._errorLog):
572                         self._errorLog.push_error(message)
573
574         @QtCore.pyqtSlot()
575         @misc_utils.log_exception(_moduleLogger)
576         def _on_login(self):
577                 with qui_utils.notify_error(self._errorLog):
578                         changedAccounts = self._defaultCredentials != self._curentCredentials
579                         noCallback = not self._session.get_callback_number()
580                         if changedAccounts or noCallback:
581                                 self._show_account_dialog()
582
583                         self._defaultCredentials = self._curentCredentials
584
585                         for tab in self._tabsContents:
586                                 tab.enable()
587
588         @QtCore.pyqtSlot()
589         @misc_utils.log_exception(_moduleLogger)
590         def _on_logout(self):
591                 with qui_utils.notify_error(self._errorLog):
592                         for tab in self._tabsContents:
593                                 tab.disable()
594
595         @QtCore.pyqtSlot()
596         @misc_utils.log_exception(_moduleLogger)
597         def _on_recipients_changed(self):
598                 with qui_utils.notify_error(self._errorLog):
599                         if self._session.draft.get_num_contacts() == 0:
600                                 return
601
602                         if self._smsEntryDialog is None:
603                                 import dialogs
604                                 self._smsEntryDialog = dialogs.SMSEntryWindow(self.window, self._app, self._session, self._errorLog)
605                                 self._smsEntryDialog.window.destroyed.connect(self._on_child_close)
606                                 self._smsEntryDialog.window.closed.connect(self._on_child_close)
607
608         @misc_utils.log_exception(_moduleLogger)
609         def _on_child_close(self, obj = None):
610                 self._smsEntryDialog = None
611
612         @QtCore.pyqtSlot()
613         @QtCore.pyqtSlot(bool)
614         @misc_utils.log_exception(_moduleLogger)
615         def _on_login_requested(self, checked = True):
616                 with qui_utils.notify_error(self._errorLog):
617                         self._prompt_for_login()
618
619         @QtCore.pyqtSlot(int)
620         @misc_utils.log_exception(_moduleLogger)
621         def _on_tab_changed(self, index):
622                 with qui_utils.notify_error(self._errorLog):
623                         self._currentTab = index
624                         self._initialize_tab(index)
625
626         @QtCore.pyqtSlot()
627         @QtCore.pyqtSlot(bool)
628         @misc_utils.log_exception(_moduleLogger)
629         def _on_refresh(self, checked = True):
630                 with qui_utils.notify_error(self._errorLog):
631                         self._tabsContents[self._currentTab].refresh(force=True)
632
633         @QtCore.pyqtSlot()
634         @QtCore.pyqtSlot(bool)
635         @misc_utils.log_exception(_moduleLogger)
636         def _on_import(self, checked = True):
637                 with qui_utils.notify_error(self._errorLog):
638                         csvName = QtGui.QFileDialog.getOpenFileName(self._window, caption="Import", filter="CSV Files (*.csv)")
639                         if not csvName:
640                                 return
641                         import shutil
642                         shutil.copy2(csvName, self._app.fsContactsPath)
643                         self._tabsContents[self.CONTACTS_TAB].update_addressbooks()
644
645         @QtCore.pyqtSlot()
646         @QtCore.pyqtSlot(bool)
647         @misc_utils.log_exception(_moduleLogger)
648         def _on_account(self, checked = True):
649                 with qui_utils.notify_error(self._errorLog):
650                         self._show_account_dialog()
651
652
653 def run():
654         app = QtGui.QApplication([])
655         handle = Dialcentral(app)
656         qtpie.init_pies()
657         return app.exec_()
658
659
660 if __name__ == "__main__":
661         import sys
662
663         logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s'
664         logging.basicConfig(level=logging.DEBUG, format=logFormat)
665         try:
666                 os.makedirs(constants._data_path_)
667         except OSError, e:
668                 if e.errno != 17:
669                         raise
670
671         val = run()
672         sys.exit(val)