X-Git-Url: https://v.licheni.net/stack/code/dboxswitch.git/blobdiff_plain/4ccfdabe53cd795fe1e74b704c15da311986ecaf..957fc59fe85f6c3ad18fe279699db2e614a1d036:/dboxswitch/gui.py?ds=inline diff --git a/dboxswitch/gui.py b/dboxswitch/gui.py new file mode 100644 index 0000000..ae2c3c8 --- /dev/null +++ b/dboxswitch/gui.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- + +""" +Dboxswitch dropbox profile switcher + +license: Modified BSD License + +Copyright (c) 2012, +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +import sys +import os +from time import sleep +from PyQt4 import QtGui, QtCore +from PyQt4.QtCore import SIGNAL +from qmenutooltip import QMenuToolTip + +from apperror import AppError +from settings import appconf + +class Gui(QtGui.QDialog): + def __init__(self, prManager): + """ Gui init, an QApplication is created and stored here, also a main window and a trayIcon + are created. + Default actions are builded and profile manager stored from argument (instance of ProfHandler """ + + self.app = QtGui.QApplication(sys.argv) + for i in range(3): + if not QtGui.QSystemTrayIcon.isSystemTrayAvailable(): + sleep(4) + else: + break + #check if system tray is avaiable on the system + if not QtGui.QSystemTrayIcon.isSystemTrayAvailable(): + QtGui.QMessageBox.critical(None, "Systray", + "I couldn't detect any system tray on this system.") + sys.exit(1) + + QtGui.QApplication.setQuitOnLastWindowClosed(False) + + super(Gui, self).__init__() + + self.profiles = [] + + self.createActions() + self.createTrayIcon() + self.createMainDialog() + + self.trayIcon.show() + + self.profileManager = prManager + + def main(self): + """ Like Gtk application this main executes the app, thi is somewhat writed for + compatibility in porting the app """ + + sys.exit(self.app.exec_()) + + def closeEvent(self, event): + """ Handle application closing """ + + if self.trayIcon.isVisible(): + self.hide() + event.ignore() + + def createActions(self): + """ Create actions for the various components """ + + self.manageprofiles = QtGui.QAction("Manage &Profiles", self, + triggered=self.show) + self.quitAction = QtGui.QAction("&Quit", self, + triggered=QtGui.qApp.quit) + + #profile manager component + self.addProfileAction = QtGui.QAction(" Add &Profile ", self, + triggered=self.addProfile) + + def createMainDialog(self): + """ Build the main dialog layout """ + + self.setWindowTitle("Profile manager / Dboxswitch - dropbox profile switcher") + #add a default action to create a profile + self.addButton = QtGui.QPushButton(QtGui.QIcon(appconf.addpicon), + "Add Profile") + self.addButton.clicked.connect(self.addProfile) + self.addButton.setVisible(False) + + self.la = QtGui.QFormLayout() + + self.la.addRow(self.addButton) + self.la.sizePolicy = QtGui.QSizePolicy.Minimum + self.la.setVerticalSpacing(15) + self.la.setHorizontalSpacing(15) + self.formGroupBox = QtGui.QGroupBox("Manage profiles:") + self.formGroupBox.setLayout(self.la) + mainLayout = QtGui.QVBoxLayout() + mainLayout.addWidget(self.formGroupBox) + self.setLayout(mainLayout) + + + def show(self): + """ Show the main dialog for dealing with profiles """ + + profiles = self.profileManager.getProfilesList() + + for pr in profiles: + prname = self.profileManager.getBaseProfileName(pr) + delButton = QtGui.QToolButton() + label = QtGui.QLabel(prname) + self.la.addRow(label, delButton) + delAction = QtGui.QAction(QtGui.QIcon(appconf.delpicon),"Delete", self, + triggered=self.deleteProfileAction(prname, (label, delButton))) + delButton.setDefaultAction(delAction) + self.profiles.append((label, delButton)) + + #if profiles was empty + if len(profiles) == 0: + self.addButton.setVisible(True) + else: + self.addButton.setVisible(False) + + super(Gui, self).show() + + def hide(self): + """ Hides the main dialog """ + + #Hides all the profiles in the Dialog + #User can deal with profiles like removing the dir manually + + for ws in self.profiles: + for w in ws: + w.setVisible(False) + w.setParent(None) + super(Gui, self).hide() + + def createTrayIcon(self): + """ Builds a new tray icon with a context menu and an action for the profile manager menu """ + + #context menu build, right click + self.trayIconMenu = QtGui.QMenu(self) + self.trayIconMenu.addAction(self.manageprofiles) + self.trayIconMenu.addSeparator() + self.trayIconMenu.addAction(self.quitAction) + + #create the tray with an incon + self.trayIcon = QtGui.QSystemTrayIcon(QtGui.QIcon(appconf.icon), self.app) + #attach a context menu for the right click to the tray + self.trayIcon.setContextMenu(self.trayIconMenu) + #baloon on hover for the tray + self.trayIcon.setToolTip(appconf.appname+" "+appconf.appversion+"\nRight Click to manage profiles.") + #left click profiles show for the tray + self.trayIcon.activated.connect(self.showTrayProfiles) + + + def showTrayProfiles(self,reason): + """ Pops up a system tray profile Manager with a list of activable profiles and an + action to add a new One """ + + if reason in (QtGui.QSystemTrayIcon.Trigger, QtGui.QSystemTrayIcon.DoubleClick): + self.menuProfiles = QMenuToolTip() + self.menuProfiles.setTitle("Profiles") + QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_DontShowIconsInMenus, False) + + #Get profiles from the ProfHandler embedded in the gui + profiles = self.profileManager.getProfilesList() + + for prpath in profiles: + pr = self.profileManager.getBaseProfileName(prpath) + receiver = self.activateProfileAction(prpath) + menuItem_Profile = self.menuProfiles.addAction(pr) + if self.profileManager.isCurrentProfile(prpath): + menuItem_Profile.setIcon(QtGui.QIcon(appconf.cpicon)) + + #Using lambda function to pass additional arguments to the function, in this case the path of the profile + self.connect(menuItem_Profile, SIGNAL('triggered()'), receiver) + #set menu item ToolTip + menuItem_Profile.setToolTip("Activate profile: "+pr) + + self.menuProfiles.addAction(menuItem_Profile) + + self.menuProfiles.addSeparator() + #action and menu item for adding a New Profile + self.menuProfiles.addAction(self.addProfileAction) + + self.menuProfiles.activateWindow() + self.menuProfiles.popup(QtGui.QCursor.pos()) + + def activateProfileAction(self, pr): + """ Returns a callable to be passed as an action for the switching profile mechanism + to self.connect(menuItem_Profile... + It compiles a function with a profile name as argument and also handle the displaying of errors.""" + + def f(): + try: + self.profileManager.activateProfile(pr) + except AppError, e: + self.showError(str(e)) + return f + + def deleteProfileAction(self, pr, prWidgets): + """ Returns a callable to be passed as an action for the profile manager mechanism + to self.connect(menuItem_Profile... + It compiles a function with a profile name as argument and also handle the displaying of errors.""" + + def f(): + try: + self.profileManager.delProfile(pr) + for w in prWidgets: + w.setParent(None) + w.setVisible(False) + except AppError, e: + self.showError(str(e)) + return f + + + def addProfile(self): + """ Gui frontend to add a new Profile, it requests the user a profile name + through a QInputDialog and creates a new profile with the help of the ProfHandler embedded in the Gui """ + + self.setWindowTitle("Add New Profile - Dboxswitch - dropbox profile switcher") + self.resize(300, 100) + text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', + 'Enter profile name:') + if ok: + try: + self.profileManager.addProfile(unicode(text)) + except AppError, e: + self.showError(str(e)) + + def showError(self, err): + """ Display an error message through a QErrorMessage """ + + self.setWindowTitle("Error - Dboxswitch - dropbox profile switcher") + self.resize(200, 100) + err = QtGui.QErrorMessage.showMessage(QtGui.QErrorMessage.qtHandler(), "Error: "+err)