4 from __future__ import with_statement
12 from PyQt4 import QtGui
13 from PyQt4 import QtCore
16 from util import qui_utils
17 from util import qtpie
18 from util import misc as misc_utils
23 _moduleLogger = logging.getLogger(__name__)
29 class Dialcentral(object):
32 os.path.dirname(__file__),
33 os.path.join(os.path.dirname(__file__), "../data"),
34 os.path.join(os.path.dirname(__file__), "../lib"),
35 '/usr/share/%s' % constants.__app_name__,
36 '/usr/lib/%s' % constants.__app_name__,
39 def __init__(self, app):
42 self._hiddenCategories = set()
43 self._hiddenUnits = {}
44 self._clipboard = QtGui.QApplication.clipboard()
46 self._mainWindow = None
48 self._fullscreenAction = QtGui.QAction(None)
49 self._fullscreenAction.setText("Fullscreen")
50 self._fullscreenAction.setCheckable(True)
51 self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
52 self._fullscreenAction.toggled.connect(self._on_toggle_fullscreen)
54 self._logAction = QtGui.QAction(None)
55 self._logAction.setText("Log")
56 self._logAction.setShortcut(QtGui.QKeySequence("CTRL+l"))
57 self._logAction.triggered.connect(self._on_log)
59 self._quitAction = QtGui.QAction(None)
60 self._quitAction.setText("Quit")
61 self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
62 self._quitAction.triggered.connect(self._on_quit)
64 self._app.lastWindowClosed.connect(self._on_app_quit)
67 self._mainWindow = MainWindow(None, self)
68 self._mainWindow.window.destroyed.connect(self._on_child_close)
70 def load_settings(self):
72 with open(constants._user_settings_, "r") as settingsFile:
73 settings = simplejson.load(settingsFile)
75 _moduleLogger.info("No settings")
78 _moduleLogger.info("Settings were corrupt")
81 self._fullscreenAction.setChecked(settings.get("isFullScreen", False))
83 def save_settings(self):
85 "isFullScreen": self._fullscreenAction.isChecked(),
87 with open(constants._user_settings_, "w") as settingsFile:
88 simplejson.dump(settings, settingsFile)
91 def fullscreenAction(self):
92 return self._fullscreenAction
96 return self._logAction
100 return self._quitAction
102 def _close_windows(self):
103 if self._mainWindow is not None:
104 self._mainWindow.window.destroyed.disconnect(self._on_child_close)
105 self._mainWindow.close()
106 self._mainWindow = None
109 @QtCore.pyqtSlot(bool)
110 @misc_utils.log_exception(_moduleLogger)
111 def _on_app_quit(self, checked = False):
114 @QtCore.pyqtSlot(QtCore.QObject)
115 @misc_utils.log_exception(_moduleLogger)
116 def _on_child_close(self, obj = None):
117 self._mainWindow = None
120 @QtCore.pyqtSlot(bool)
121 @misc_utils.log_exception(_moduleLogger)
122 def _on_toggle_fullscreen(self, checked = False):
123 for window in self._walk_children():
124 window.set_fullscreen(checked)
127 @QtCore.pyqtSlot(bool)
128 @misc_utils.log_exception(_moduleLogger)
129 def _on_log(self, checked = False):
130 with open(constants._user_logpath_, "r") as f:
131 logLines = f.xreadlines()
132 log = "".join(logLines)
133 self._clipboard.setText(log)
136 @QtCore.pyqtSlot(bool)
137 @misc_utils.log_exception(_moduleLogger)
138 def _on_quit(self, checked = False):
139 self._close_windows()
142 class CredentialsDialog(object):
145 self._usernameField = QtGui.QLineEdit()
146 self._passwordField = QtGui.QLineEdit()
147 self._passwordField.setEchoMode(QtGui.QLineEdit.PasswordEchoOnEdit)
149 self._credLayout = QtGui.QGridLayout()
150 self._credLayout.addWidget(QtGui.QLabel("Username"), 0, 0)
151 self._credLayout.addWidget(self._usernameField, 0, 1)
152 self._credLayout.addWidget(QtGui.QLabel("Password"), 1, 0)
153 self._credLayout.addWidget(self._passwordField, 1, 1)
155 self._loginButton = QtGui.QPushButton("&Login")
156 self._buttonLayout = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Cancel)
157 self._buttonLayout.addButton(self._loginButton, QtGui.QDialogButtonBox.AcceptRole)
159 self._layout = QtGui.QVBoxLayout()
160 self._layout.addLayout(self._credLayout)
161 self._layout.addWidget(self._buttonLayout)
163 self._dialog = QtGui.QDialog()
164 self._dialog.setWindowTitle("Login")
165 self._dialog.setLayout(self._layout)
166 self._dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose, False)
167 qui_utils.set_autorient(self._dialog, True)
168 self._buttonLayout.accepted.connect(self._dialog.accept)
169 self._buttonLayout.rejected.connect(self._dialog.reject)
171 def run(self, defaultUsername, defaultPassword, parent=None):
172 self._dialog.setParent(parent, QtCore.Qt.Dialog)
174 self._usernameField.setText(defaultUsername)
175 self._passwordField.setText(defaultPassword)
177 response = self._dialog.exec_()
178 if response == QtGui.QDialog.Accepted:
179 return str(self._usernameField.text()), str(self._passwordField.text())
180 elif response == QtGui.QDialog.Rejected:
181 raise RuntimeError("Login Cancelled")
183 self._dialog.setParent(None, QtCore.Qt.Dialog)
186 class AccountDialog(object):
189 self._accountNumberLabel = QtGui.QLabel("NUMBER NOT SET")
190 self._clearButton = QtGui.QPushButton("Clear Account")
191 self._clearButton.clicked.connect(self._on_clear)
192 self._doClear = False
194 self._credLayout = QtGui.QGridLayout()
195 self._credLayout.addWidget(QtGui.QLabel("Account"), 0, 0)
196 self._credLayout.addWidget(self._accountNumberLabel, 0, 1)
197 self._credLayout.addWidget(QtGui.QLabel("Callback"), 1, 0)
198 self._credLayout.addWidget(self._clearButton, 2, 1)
200 self._loginButton = QtGui.QPushButton("&Login")
201 self._buttonLayout = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Cancel)
202 self._buttonLayout.addButton(self._loginButton, QtGui.QDialogButtonBox.AcceptRole)
204 self._layout = QtGui.QVBoxLayout()
205 self._layout.addLayout(self._credLayout)
206 self._layout.addLayout(self._buttonLayout)
208 self._dialog = QtGui.QDialog()
209 self._dialog.setWindowTitle("Login")
210 self._dialog.setLayout(self._layout)
211 qui_utils.set_autorient(self._dialog, True)
212 self._buttonLayout.accepted.connect(self._dialog.accept)
213 self._buttonLayout.rejected.connect(self._dialog.reject)
219 accountNumber = property(
220 lambda self: str(self._accountNumberLabel.text()),
221 lambda self, num: self._accountNumberLabel.setText(num),
224 def run(self, defaultUsername, defaultPassword, parent=None):
225 self._doClear = False
226 self._dialog.setParent(parent)
227 self._usernameField.setText(defaultUsername)
228 self._passwordField.setText(defaultPassword)
230 response = self._dialog.exec_()
231 if response == QtGui.QDialog.Accepted:
232 return str(self._usernameField.text()), str(self._passwordField.text())
233 elif response == QtGui.QDialog.Rejected:
234 raise RuntimeError("Login Cancelled")
237 @QtCore.pyqtSlot(bool)
238 def _on_clear(self, checked = False):
240 self._dialog.accept()
243 class SMSEntryWindow(object):
245 def __init__(self, parent, app, session, errorLog):
248 self._session = session
249 self._errorLog = errorLog
251 self._history = QtGui.QListView()
252 self._smsEntry = QtGui.QTextEdit()
253 self._smsEntry.textChanged.connect(self._on_letter_count_changed)
255 self._entryLayout = QtGui.QVBoxLayout()
256 self._entryLayout.addWidget(self._history)
257 self._entryLayout.addWidget(self._smsEntry)
258 self._entryWidget = QtGui.QWidget()
259 self._entryWidget.setLayout(self._entryLayout)
260 self._scrollEntry = QtGui.QScrollArea()
261 self._scrollEntry.setWidget(self._entryWidget)
262 self._scrollEntry.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignBottom)
263 self._scrollEntry.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
264 self._scrollEntry.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
266 self._characterCountLabel = QtGui.QLabel("Letters: %s" % 0)
267 self._numberSelector = None
268 self._smsButton = QtGui.QPushButton("SMS")
269 self._dialButton = QtGui.QPushButton("Dial")
271 self._buttonLayout = QtGui.QHBoxLayout()
272 self._buttonLayout.addWidget(self._characterCountLabel)
273 self._buttonLayout.addWidget(self._smsButton)
274 self._buttonLayout.addWidget(self._dialButton)
276 self._layout = QtGui.QVBoxLayout()
277 self._layout.addLayout(self._entryLayout)
278 self._layout.addLayout(self._buttonLayout)
280 centralWidget = QtGui.QWidget()
281 centralWidget.setLayout(self._layout)
283 self._window = QtGui.QMainWindow(parent)
284 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, False)
285 qui_utils.set_autorient(self._window, True)
286 qui_utils.set_stackable(self._window, True)
287 self._window.setWindowTitle("Contact")
288 self._window.setCentralWidget(centralWidget)
290 def _update_letter_count(self):
291 count = self._smsEntry.toPlainText().size()
292 self._characterCountLabel.setText("Letters: %s" % count)
294 def _update_button_state(self):
295 if len(self._contacts) == 0:
296 self._dialButton.setEnabled(False)
297 self._smsButton.setEnabled(False)
298 elif len(self._contacts) == 1:
299 count = self._smsEntry.toPlainText().size()
301 self._dialButton.setEnabled(True)
302 self._smsButton.setEnabled(False)
304 self._dialButton.setEnabled(False)
305 self._smsButton.setEnabled(True)
307 self._dialButton.setEnabled(False)
308 self._smsButton.setEnabled(True)
311 @misc_utils.log_exception(_moduleLogger)
312 def _on_letter_count_changed(self):
313 self._update_letter_count()
314 self._update_button_state()
317 class DelayedWidget(object):
319 def __init__(self, app):
320 self._layout = QtGui.QVBoxLayout()
321 self._widget = QtGui.QWidget()
322 self._widget.setLayout(self._layout)
325 self._isEnabled = True
332 return self._child is not None
334 def set_child(self, child):
335 if self._child is not None:
336 self._layout.removeWidget(self._child.toplevel)
338 if self._child is not None:
339 self._layout.addWidget(self._child.toplevel)
344 self._child.disable()
347 self._isEnabled = True
348 if self._child is not None:
352 self._isEnabled = False
353 if self._child is not None:
354 self._child.disable()
357 if self._child is not None:
361 if self._child is not None:
362 self._child.refresh()
365 class Dialpad(object):
367 def __init__(self, app, session, errorLog):
369 self._session = session
370 self._errorLog = errorLog
372 self._plus = self._generate_key_button("+", "")
373 self._entry = QtGui.QLineEdit()
375 backAction = QtGui.QAction(None)
376 backAction.setText("Back")
377 backAction.triggered.connect(self._on_backspace)
378 backPieItem = qtpie.QActionPieItem(backAction)
379 clearAction = QtGui.QAction(None)
380 clearAction.setText("Clear")
381 clearAction.triggered.connect(self._on_clear_text)
382 clearPieItem = qtpie.QActionPieItem(clearAction)
383 self._back = qtpie.QPieButton(backPieItem)
384 self._back.set_center(backPieItem)
385 self._back.insertItem(qtpie.PieFiling.NULL_CENTER)
386 self._back.insertItem(clearPieItem)
387 self._back.insertItem(qtpie.PieFiling.NULL_CENTER)
388 self._back.insertItem(qtpie.PieFiling.NULL_CENTER)
390 self._entryLayout = QtGui.QHBoxLayout()
391 self._entryLayout.addWidget(self._plus, 0, QtCore.Qt.AlignCenter)
392 self._entryLayout.addWidget(self._entry, 10)
393 self._entryLayout.addWidget(self._back, 0, QtCore.Qt.AlignCenter)
395 self._smsButton = QtGui.QPushButton("SMS")
396 self._smsButton.clicked.connect(self._on_sms_clicked)
397 self._callButton = QtGui.QPushButton("Call")
398 self._callButton.clicked.connect(self._on_call_clicked)
400 self._padLayout = QtGui.QGridLayout()
401 rows = [0, 0, 0, 1, 1, 1, 2, 2, 2]
402 columns = [0, 1, 2] * 3
414 for (num, letters), (row, column) in zip(keys, zip(rows, columns)):
415 self._padLayout.addWidget(
416 self._generate_key_button(num, letters), row, column, QtCore.Qt.AlignCenter
418 self._padLayout.addWidget(self._smsButton, 3, 0)
419 self._padLayout.addWidget(
420 self._generate_key_button("0", ""), 3, 1, QtCore.Qt.AlignCenter
422 self._padLayout.addWidget(self._callButton, 3, 2)
424 self._layout = QtGui.QVBoxLayout()
425 self._layout.addLayout(self._entryLayout)
426 self._layout.addLayout(self._padLayout)
427 self._widget = QtGui.QWidget()
428 self._widget.setLayout(self._layout)
435 self._smsButton.setEnabled(True)
436 self._callButton.setEnabled(True)
439 self._smsButton.setEnabled(False)
440 self._callButton.setEnabled(False)
448 def _generate_key_button(self, center, letters):
449 centerPieItem = self._generate_button_slice(center)
450 button = qtpie.QPieButton(centerPieItem)
451 button.set_center(centerPieItem)
453 if len(letters) == 0:
455 pieItem = qtpie.PieFiling.NULL_CENTER
456 button.insertItem(pieItem)
457 elif len(letters) in [3, 4]:
458 for i in xrange(6 - len(letters)):
459 pieItem = qtpie.PieFiling.NULL_CENTER
460 button.insertItem(pieItem)
462 for letter in letters:
463 pieItem = self._generate_button_slice(letter)
464 button.insertItem(pieItem)
467 pieItem = qtpie.PieFiling.NULL_CENTER
468 button.insertItem(pieItem)
470 raise NotImplementedError("Cannot handle %r" % letters)
473 def _generate_button_slice(self, letter):
474 action = QtGui.QAction(None)
475 action.setText(letter)
476 action.triggered.connect(lambda: self._on_keypress(letter))
477 pieItem = qtpie.QActionPieItem(action)
480 @misc_utils.log_exception(_moduleLogger)
481 def _on_keypress(self, key):
482 self._entry.insert(key)
484 @misc_utils.log_exception(_moduleLogger)
485 def _on_backspace(self, toggled = False):
486 self._entry.backspace()
488 @misc_utils.log_exception(_moduleLogger)
489 def _on_clear_text(self, toggled = False):
493 @QtCore.pyqtSlot(bool)
494 @misc_utils.log_exception(_moduleLogger)
495 def _on_sms_clicked(self, checked = False):
496 number = str(self._entry.text())
498 self._session.draft.add_contact(number, [])
501 @QtCore.pyqtSlot(bool)
502 @misc_utils.log_exception(_moduleLogger)
503 def _on_call_clicked(self, checked = False):
504 number = str(self._entry.text())
506 self._session.draft.add_contact(number, [])
507 self._session.draft.call()
510 class History(object):
518 HISTORY_ITEM_TYPES = ["All", "Received", "Missed", "Placed"]
519 HISTORY_COLUMNS = ["When", "What", "Number", "From"]
520 assert len(HISTORY_COLUMNS) == MAX_IDX
522 def __init__(self, app, session, errorLog):
523 self._selectedFilter = self.HISTORY_ITEM_TYPES[0]
525 self._session = session
526 self._session.historyUpdated.connect(self._on_history_updated)
527 self._errorLog = errorLog
529 self._typeSelection = QtGui.QComboBox()
530 self._typeSelection.addItems(self.HISTORY_ITEM_TYPES)
531 self._typeSelection.setCurrentIndex(
532 self.HISTORY_ITEM_TYPES.index(self._selectedFilter)
534 self._typeSelection.currentIndexChanged.connect(self._on_filter_changed)
536 self._itemStore = QtGui.QStandardItemModel()
537 self._itemStore.setHorizontalHeaderLabels(self.HISTORY_COLUMNS)
539 self._itemView = QtGui.QTreeView()
540 self._itemView.setModel(self._itemStore)
541 self._itemView.setUniformRowHeights(True)
542 self._itemView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
543 self._itemView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
544 self._itemView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
545 self._itemView.setHeaderHidden(True)
546 self._itemView.activated.connect(self._on_row_activated)
548 self._layout = QtGui.QVBoxLayout()
549 self._layout.addWidget(self._typeSelection)
550 self._layout.addWidget(self._itemView)
551 self._widget = QtGui.QWidget()
552 self._widget.setLayout(self._layout)
554 self._populate_items()
561 self._itemView.setEnabled(True)
564 self._itemView.setEnabled(False)
567 self._itemView.clear()
572 def _populate_items(self):
573 self._errorLog.push_message("Not supported")
575 @QtCore.pyqtSlot(str)
576 @misc_utils.log_exception(_moduleLogger)
577 def _on_filter_changed(self, newItem):
578 self._selectedFilter = str(newItem)
581 @misc_utils.log_exception(_moduleLogger)
582 def _on_history_updated(self):
583 self._populate_items()
585 @QtCore.pyqtSlot(QtCore.QModelIndex)
586 @misc_utils.log_exception(_moduleLogger)
587 def _on_row_activated(self, index):
588 rowIndex = index.row()
589 #self._session.draft.add_contact(number, details)
592 class Messages(object):
595 VOICEMAIL_MESSAGES = "Voicemail"
596 TEXT_MESSAGES = "SMS"
597 ALL_TYPES = "All Messages"
598 MESSAGE_TYPES = [NO_MESSAGES, VOICEMAIL_MESSAGES, TEXT_MESSAGES, ALL_TYPES]
600 UNREAD_STATUS = "Unread"
601 UNARCHIVED_STATUS = "Inbox"
603 MESSAGE_STATUSES = [UNREAD_STATUS, UNARCHIVED_STATUS, ALL_STATUS]
605 def __init__(self, app, session, errorLog):
606 self._selectedTypeFilter = self.ALL_TYPES
607 self._selectedStatusFilter = self.ALL_STATUS
609 self._session = session
610 self._session.messagesUpdated.connect(self._on_messages_updated)
611 self._errorLog = errorLog
613 self._typeSelection = QtGui.QComboBox()
614 self._typeSelection.addItems(self.MESSAGE_TYPES)
615 self._typeSelection.setCurrentIndex(
616 self.MESSAGE_TYPES.index(self._selectedTypeFilter)
618 self._typeSelection.currentIndexChanged.connect(self._on_type_filter_changed)
620 self._statusSelection = QtGui.QComboBox()
621 self._statusSelection.addItems(self.MESSAGE_STATUSES)
622 self._statusSelection.setCurrentIndex(
623 self.MESSAGE_STATUSES.index(self._selectedStatusFilter)
625 self._statusSelection.currentIndexChanged.connect(self._on_status_filter_changed)
627 self._selectionLayout = QtGui.QHBoxLayout()
628 self._selectionLayout.addWidget(self._typeSelection)
629 self._selectionLayout.addWidget(self._statusSelection)
631 self._itemStore = QtGui.QStandardItemModel()
632 self._itemStore.setHorizontalHeaderLabels(["Messages"])
634 self._itemView = QtGui.QTreeView()
635 self._itemView.setModel(self._itemStore)
636 self._itemView.setUniformRowHeights(True)
637 self._itemView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
638 self._itemView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
639 self._itemView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
640 self._itemView.setHeaderHidden(True)
641 self._itemView.activated.connect(self._on_row_activated)
643 self._layout = QtGui.QVBoxLayout()
644 self._layout.addLayout(self._selectionLayout)
645 self._layout.addWidget(self._itemView)
646 self._widget = QtGui.QWidget()
647 self._widget.setLayout(self._layout)
649 self._populate_items()
656 self._itemView.setEnabled(True)
659 self._itemView.setEnabled(False)
662 self._itemView.clear()
667 def _populate_items(self):
668 self._errorLog.push_message("Not supported")
670 @QtCore.pyqtSlot(str)
671 @misc_utils.log_exception(_moduleLogger)
672 def _on_type_filter_changed(self, newItem):
673 self._selectedTypeFilter = str(newItem)
675 @QtCore.pyqtSlot(str)
676 @misc_utils.log_exception(_moduleLogger)
677 def _on_status_filter_changed(self, newItem):
678 self._selectedStatusFilter = str(newItem)
681 @misc_utils.log_exception(_moduleLogger)
682 def _on_messages_updated(self):
683 self._populate_items()
685 @QtCore.pyqtSlot(QtCore.QModelIndex)
686 @misc_utils.log_exception(_moduleLogger)
687 def _on_row_activated(self, index):
688 rowIndex = index.row()
689 #self._session.draft.add_contact(number, details)
692 class Contacts(object):
694 def __init__(self, app, session, errorLog):
695 self._selectedFilter = ""
697 self._session = session
698 self._session.contactsUpdated.connect(self._on_contacts_updated)
699 self._errorLog = errorLog
701 self._listSelection = QtGui.QComboBox()
702 self._listSelection.addItems([])
703 #self._listSelection.setCurrentIndex(self.HISTORY_ITEM_TYPES.index(self._selectedFilter))
704 self._listSelection.currentIndexChanged.connect(self._on_filter_changed)
706 self._itemStore = QtGui.QStandardItemModel()
707 self._itemStore.setHorizontalHeaderLabels(["Contacts"])
709 self._itemView = QtGui.QTreeView()
710 self._itemView.setModel(self._itemStore)
711 self._itemView.setUniformRowHeights(True)
712 self._itemView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
713 self._itemView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
714 self._itemView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
715 self._itemView.setHeaderHidden(True)
716 self._itemView.activated.connect(self._on_row_activated)
718 self._layout = QtGui.QVBoxLayout()
719 self._layout.addWidget(self._listSelection)
720 self._layout.addWidget(self._itemView)
721 self._widget = QtGui.QWidget()
722 self._widget.setLayout(self._layout)
724 self._populate_items()
731 self._itemView.setEnabled(True)
734 self._itemView.setEnabled(False)
737 self._itemView.clear()
742 def _populate_items(self):
743 self._errorLog.push_message("Not supported")
745 @QtCore.pyqtSlot(str)
746 @misc_utils.log_exception(_moduleLogger)
747 def _on_filter_changed(self, newItem):
748 self._selectedFilter = str(newItem)
751 @misc_utils.log_exception(_moduleLogger)
752 def _on_contacts_updated(self):
753 self._populate_items()
755 @QtCore.pyqtSlot(QtCore.QModelIndex)
756 @misc_utils.log_exception(_moduleLogger)
757 def _on_row_activated(self, index):
758 rowIndex = index.row()
759 #self._session.draft.add_contact(number, details)
762 class MainWindow(object):
776 assert len(_TAB_TITLES) == MAX_TABS
784 assert len(_TAB_CLASS) == MAX_TABS
786 def __init__(self, parent, app):
787 self._fsContactsPath = os.path.join(constants._data_path_, "contacts")
789 self._session = session.Session()
790 self._session.error.connect(self._on_session_error)
791 self._session.loggedIn.connect(self._on_login)
792 self._session.loggedOut.connect(self._on_logout)
794 self._credentialsDialog = None
796 self._errorLog = qui_utils.QErrorLog()
797 self._errorDisplay = qui_utils.ErrorDisplay(self._errorLog)
799 self._tabsContents = [
800 DelayedWidget(self._app)
801 for i in xrange(self.MAX_TABS)
803 for tab in self._tabsContents:
806 self._tabWidget = QtGui.QTabWidget()
807 if qui_utils.screen_orientation() == QtCore.Qt.Vertical:
808 self._tabWidget.setTabPosition(QtGui.QTabWidget.South)
810 self._tabWidget.setTabPosition(QtGui.QTabWidget.West)
811 for tabIndex, tabTitle in enumerate(self._TAB_TITLES):
812 self._tabWidget.addTab(self._tabsContents[tabIndex].toplevel, tabTitle)
813 self._tabWidget.currentChanged.connect(self._on_tab_changed)
815 self._layout = QtGui.QVBoxLayout()
816 self._layout.addWidget(self._errorDisplay.toplevel)
817 self._layout.addWidget(self._tabWidget)
819 centralWidget = QtGui.QWidget()
820 centralWidget.setLayout(self._layout)
822 self._window = QtGui.QMainWindow(parent)
823 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
824 qui_utils.set_autorient(self._window, True)
825 qui_utils.set_stackable(self._window, True)
826 self._window.setWindowTitle("%s" % constants.__pretty_app_name__)
827 self._window.setCentralWidget(centralWidget)
829 self._loginTabAction = QtGui.QAction(None)
830 self._loginTabAction.setText("Login")
831 self._loginTabAction.triggered.connect(self._on_login_requested)
833 self._importTabAction = QtGui.QAction(None)
834 self._importTabAction.setText("Import")
835 self._importTabAction.triggered.connect(self._on_import)
837 self._refreshTabAction = QtGui.QAction(None)
838 self._refreshTabAction.setText("Refresh")
839 self._refreshTabAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
840 self._refreshTabAction.triggered.connect(self._on_refresh)
842 self._closeWindowAction = QtGui.QAction(None)
843 self._closeWindowAction.setText("Close")
844 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
845 self._closeWindowAction.triggered.connect(self._on_close_window)
848 fileMenu = self._window.menuBar().addMenu("&File")
849 fileMenu.addAction(self._loginTabAction)
850 fileMenu.addAction(self._refreshTabAction)
852 toolsMenu = self._window.menuBar().addMenu("&Tools")
853 toolsMenu.addAction(self._importTabAction)
855 self._window.addAction(self._closeWindowAction)
856 self._window.addAction(self._app.quitAction)
857 self._window.addAction(self._app.fullscreenAction)
859 fileMenu = self._window.menuBar().addMenu("&File")
860 fileMenu.addAction(self._loginTabAction)
861 fileMenu.addAction(self._refreshTabAction)
862 fileMenu.addAction(self._closeWindowAction)
863 fileMenu.addAction(self._app.quitAction)
865 viewMenu = self._window.menuBar().addMenu("&View")
866 viewMenu.addAction(self._app.fullscreenAction)
868 toolsMenu = self._window.menuBar().addMenu("&Tools")
869 toolsMenu.addAction(self._importTabAction)
871 self._window.addAction(self._app.logAction)
873 self._initialize_tab(self._tabWidget.currentIndex())
874 self.set_fullscreen(self._app.fullscreenAction.isChecked())
881 def walk_children(self):
886 for child in self.walk_children():
890 for child in self.walk_children():
895 for child in self.walk_children():
896 child.window.destroyed.disconnect(self._on_child_close)
900 def set_fullscreen(self, isFullscreen):
902 self._window.showFullScreen()
904 self._window.showNormal()
905 for child in self.walk_children():
906 child.set_fullscreen(isFullscreen)
908 def _initialize_tab(self, index):
909 assert index < self.MAX_TABS
910 if not self._tabsContents[index].has_child():
911 self._tabsContents[index].set_child(
912 self._TAB_CLASS[index](self._app, self._session, self._errorLog)
915 @QtCore.pyqtSlot(str)
916 @misc_utils.log_exception(_moduleLogger)
917 def _on_session_error(self, message):
918 self._errorLog.push_message(message)
921 @misc_utils.log_exception(_moduleLogger)
923 for tab in self._tabsContents:
927 @misc_utils.log_exception(_moduleLogger)
928 def _on_logout(self):
929 for tab in self._tabsContents:
933 @QtCore.pyqtSlot(bool)
934 @misc_utils.log_exception(_moduleLogger)
935 def _on_login_requested(self, checked = True):
936 if self._credentialsDialog is None:
937 self._credentialsDialog = CredentialsDialog()
938 username, password = self._credentialsDialog.run("", "", self.window)
939 self._session.login(username, password)
941 @QtCore.pyqtSlot(int)
942 @misc_utils.log_exception(_moduleLogger)
943 def _on_tab_changed(self, index):
944 self._initialize_tab(index)
947 @QtCore.pyqtSlot(bool)
948 @misc_utils.log_exception(_moduleLogger)
949 def _on_refresh(self, checked = True):
950 index = self._tabWidget.currentIndex()
951 self._tabsContents[index].refresh()
954 @QtCore.pyqtSlot(bool)
955 @misc_utils.log_exception(_moduleLogger)
956 def _on_import(self, checked = True):
957 csvName = QtGui.QFileDialog.getOpenFileName(self._window, caption="Import", filter="CSV Files (*.csv)")
960 shutil.copy2(csvName, self._fsContactsPath)
963 @QtCore.pyqtSlot(bool)
964 @misc_utils.log_exception(_moduleLogger)
965 def _on_close_window(self, checked = True):
970 app = QtGui.QApplication([])
971 handle = Dialcentral(app)
976 if __name__ == "__main__":
977 logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s'
978 logging.basicConfig(level=logging.DEBUG, format=logFormat)
980 os.makedirs(constants._data_path_)