Exemple #1
0
    def __init__(self, parent=None):
        super(MainWindow, self).__init__()
        self.conf = configure()
        self.setFixedSize(600, 500)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        #将进度初始化为零
        self.ui.progressBar.setValue(0)

        #从配置文件中读取源文件和目标文件路径并进行显示
        self.read_config_file()

        #菜单栏
        self.ui.file_open.triggered.connect(self.on_file_open_clicked)
        self.ui.file_save.triggered.connect(self.on_file_save_clicked)
        self.ui.file_set.triggered.connect(self.on_file_set_cliked)
        self.ui.file_quit.triggered.connect(self.close)
        self.ui.help_help.triggered.connect(self.on_help_help_clicked)
        self.ui.help_abut.triggered.connect(self.on_help_about_clicked)

        #按钮
        self.ui.set_src_button.clicked.connect(self.set_src_button_cliked)
        self.ui.set_des_button.clicked.connect(self.set_dst_button_cliked)
        self.ui.open_dst_button.clicked.connect(self.open_dst_button_cliked)
        self.ui.del_old_button.clicked.connect(self.del_old_button_clicked)
        self.ui.start_button.clicked.connect(self.start_button_cliked)
Exemple #2
0
	def __init__(self, parent=None):
		QWidget.__init__(self, parent)
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)
		self.create_index("songs/*.txt")
		self.ui.gvVistaPrevia.setViewport(QGLWidget())
		self.font = QFont()
		self.text = QString("Trigales Worship")
		self.bgcolor = QColor("yellow")
		self.pic = QPixmap("fondo.jpg")
#		self.pic = self.pic.scaled(266, 200, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
		self.createScene()
#		self.mostrar_letra()
		### Conexiones
		self.connect(self.ui.btnAgregar, SIGNAL("clicked()"), self.agregar_a_la_lista)
		self.connect(self.ui.btnQuitar, SIGNAL("clicked()"), self.quitar_de_la_lista)
		self.connect(self.ui.listLista, SIGNAL("itemSelectionChanged()"), self.mostrar_letra)
#		self.connect(self.ui.fontTipoDeLetra, SIGNAL("currentFontChanged(const QFont &)"), self.setFont)
#		self.connect(self.ui.btnTipoLetra, SIGNAL("clicked()"), self.setFont)
		self.connect(self.ui.btnComenzar, SIGNAL("clicked()"), self.comenzarPresentacion)
		self.connect(self.ui.btnSiguiente,  SIGNAL("clicked()"),  self.siguiente)
		self.connect(self.ui.btnAnterior,   SIGNAL("clicked()"),  self.anterior)
		self.connect(self.ui.btnInicio, SIGNAL("clicked()"),  self.inicio)
		self.connect(self.ui.btnFin, SIGNAL("clicked()"),  self.fin)
Exemple #3
0
 def __init__(self, parent, locale="fr_FR"):
     """
     Le constructeur
     @param parent un QWidget
     @param locale la langue de l'application
     """
     QMainWindow.__init__(self)
     QWidget.__init__(self, parent)
     self.locale=locale
     from Ui_mainWindow  import Ui_MainWindow
     self.ui = Ui_MainWindow()
     self.ui.setupUi(self)
     QIcon.setThemeName("Tango")
     icon=self.setThemedIcon(self.ui.fromButton,"back")
     self.copyfromIcon=icon
     self.movefromIcon=QIcon.fromTheme("movefrom",QIcon("/usr/share/scolasync/images/movefrom.png"))
     self.setThemedIcon(self.ui.toButton,"forward")
     self.setThemedIcon(self.ui.delButton,"edit-clear")
     self.setThemedIcon(self.ui.umountButton,"top")
     self.setThemedIcon(self.ui.redoButton,"go-jump")
     self.setThemedIcon(self.ui.namesButton,"gtk-find")
     self.setThemedIcon(self.ui.forceCheckButton,"multimedia-player")
     self.setThemedIcon(self.ui.preferenceButton,"package_settings")
     self.setThemedIcon(self.ui.helpButton,"info")
     # crée le dialogue des nouveaux noms
     self.namesFullIcon=QIcon.fromTheme("gtk-find-and-replace.svg")
     self.namesEmptyIcon=QIcon.fromTheme("gtk-find")
     self.namesFullTip=QApplication.translate("MainWindow", "<br />Des noms sont disponibles pour renommer les prochains baladeurs que vous brancherez", None)
     self.namesEmptyTip=QApplication.translate("MainWindow", "<br />Cliquez sur ce bouton pour préparer une liste de noms afin de renommer les prochains baladeurs que vous brancherez", None)
     self.namesDialog=choixEleves.choixElevesDialog(parent =self)
     self.recentConnect="" # chemin dbus pour un baladeur récemment connecté
     # initialise deux icônes
     self.initRedoStuff()
     # initialise le tableau
     self.t=self.ui.tableView
     self.proxy=QSortFilterProxyModel()
     self.proxy.setSourceModel(self.t.model())
     self.applyPreferences()
     self.updateButtons()
     self.setAvailableNames(False)
     self.operations=[] # liste des opérations précédemment "réussies"
     self.oldThreads=set() # threads lancés éventuellement encore vivants
     self.ui.helpButton.clicked.connect(self.help)
     self.ui.umountButton.clicked.connect(self.umount)
     self.ui.toButton.clicked.connect(self.copyTo)
     self.ui.fromButton.clicked.connect(self.copyFrom)
     self.ui.delButton.clicked.connect(self.delFiles)
     self.ui.redoButton.clicked.connect(self.redoCmd)
     self.ui.namesButton.clicked.connect(self.namesCmd)
     self.ui.preferenceButton.clicked.connect(self.preference)
     self.ui.tableView.doubleClicked.connect(self.tableClicked)
     self.checkAllSignal.connect(self.checkAll)
     self.checkToggleSignal.connect(self.checkToggle)
     self.checkNoneSignal.connect(self.checkNone)
     self.shouldNameDrive.connect(self.namingADrive)
     ## accrochage d'une fonction de rappel pour les disque ajoutés
     qApp.available.addHook('object-added',   self.cbAdded())
     qApp.available.addHook('object-removed', self.cbRemoved())
     self.pushCmdSignal.connect(self.pushCmd)
     self.popCmdSignal.connect(self.popCmd)
     return
Exemple #4
0
class mainWindow(QMainWindow):
    """
    defines the main window of the application.
    """
    ############# custom signals ########################
    checkAllSignal=pyqtSignal()
    checkToggleSignal=pyqtSignal()
    checkNoneSignal=pyqtSignal()
    shouldNameDrive=pyqtSignal()
    pushCmdSignal=pyqtSignal(str, str)
    popCmdSignal=pyqtSignal(str, str)
    
    def __init__(self, parent, locale="fr_FR"):
        """
        Le constructeur
        @param parent un QWidget
        @param locale la langue de l'application
        """
        QMainWindow.__init__(self)
        QWidget.__init__(self, parent)
        self.locale=locale
        from Ui_mainWindow  import Ui_MainWindow
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        QIcon.setThemeName("Tango")
        icon=self.setThemedIcon(self.ui.fromButton,"back")
        self.copyfromIcon=icon
        self.movefromIcon=QIcon.fromTheme("movefrom",QIcon("/usr/share/scolasync/images/movefrom.png"))
        self.setThemedIcon(self.ui.toButton,"forward")
        self.setThemedIcon(self.ui.delButton,"edit-clear")
        self.setThemedIcon(self.ui.umountButton,"top")
        self.setThemedIcon(self.ui.redoButton,"go-jump")
        self.setThemedIcon(self.ui.namesButton,"gtk-find")
        self.setThemedIcon(self.ui.forceCheckButton,"multimedia-player")
        self.setThemedIcon(self.ui.preferenceButton,"package_settings")
        self.setThemedIcon(self.ui.helpButton,"info")
        # crée le dialogue des nouveaux noms
        self.namesFullIcon=QIcon.fromTheme("gtk-find-and-replace.svg")
        self.namesEmptyIcon=QIcon.fromTheme("gtk-find")
        self.namesFullTip=QApplication.translate("MainWindow", "<br />Des noms sont disponibles pour renommer les prochains baladeurs que vous brancherez", None)
        self.namesEmptyTip=QApplication.translate("MainWindow", "<br />Cliquez sur ce bouton pour préparer une liste de noms afin de renommer les prochains baladeurs que vous brancherez", None)
        self.namesDialog=choixEleves.choixElevesDialog(parent =self)
        self.recentConnect="" # chemin dbus pour un baladeur récemment connecté
        # initialise deux icônes
        self.initRedoStuff()
        # initialise le tableau
        self.t=self.ui.tableView
        self.proxy=QSortFilterProxyModel()
        self.proxy.setSourceModel(self.t.model())
        self.applyPreferences()
        self.updateButtons()
        self.setAvailableNames(False)
        self.operations=[] # liste des opérations précédemment "réussies"
        self.oldThreads=set() # threads lancés éventuellement encore vivants
        self.ui.helpButton.clicked.connect(self.help)
        self.ui.umountButton.clicked.connect(self.umount)
        self.ui.toButton.clicked.connect(self.copyTo)
        self.ui.fromButton.clicked.connect(self.copyFrom)
        self.ui.delButton.clicked.connect(self.delFiles)
        self.ui.redoButton.clicked.connect(self.redoCmd)
        self.ui.namesButton.clicked.connect(self.namesCmd)
        self.ui.preferenceButton.clicked.connect(self.preference)
        self.ui.tableView.doubleClicked.connect(self.tableClicked)
        self.checkAllSignal.connect(self.checkAll)
        self.checkToggleSignal.connect(self.checkToggle)
        self.checkNoneSignal.connect(self.checkNone)
        self.shouldNameDrive.connect(self.namingADrive)
        ## accrochage d'une fonction de rappel pour les disque ajoutés
        qApp.available.addHook('object-added',   self.cbAdded())
        qApp.available.addHook('object-removed', self.cbRemoved())
        self.pushCmdSignal.connect(self.pushCmd)
        self.popCmdSignal.connect(self.popCmd)
        return

    def setThemedIcon(self, button, name, default=None):
        """
        Associe une icone à un bouton, dans le thème courant
        @param button le bouton à décorer
        @param name le nom de l'icone
        @param default un fichier PNG ; si rien n'est donné, il aura
        comme valeur par défaut "images/icons32/"+name+".png"
        @return l'objet de type QIcon qui a été associé au bouton
        """
        icon=QIcon()
        try:
            icon.addPixmap(QIcon.fromTheme(name).pixmap(32))
        except:
            icon.addPixmap("images/icons32/"+name+".png")
        button.setIcon(icon)
        return button.icon()
    
    def pushCmd(self,owner,cmd):
        """
        fonction de rappel déclenchée par les threads (au commencement)
        @param owner le propriétaire du baladeur associé au thread
        @param cmd la commande shell effectuée sur ce baladeur
        """
        global activeThreads, pastCommands, lastCommand
        if owner in activeThreads:
            activeThreads[owner].append(cmd)
        else:
            activeThreads[owner]=[cmd]
        self.tm.updateOwnerColumn()
        self.updateButtons()

    def popCmd(self,owner, cmd):
        """
        fonction de rappel déclenchée par les threads (à la fin)
        @param owner le propriétaire du baladeur associé au thread
        @param cmd la commande shell effectuée sur ce baladeur
        """
        global activeThreads, pastCommands, lastCommand
        if owner in activeThreads:
            cmd0=activeThreads[owner].pop()
            if cmd0 in cmd:
                msg=cmd.replace(cmd0,"")+"\n"
                logFile=open(os.path.expanduser(logFileName),"a")
                logFile.write(msg)
                logFile.close()
            else:
                raise Exception(("mismatched commands\n%s\n%s" %(cmd,cmd0)))
            if len(activeThreads[owner])==0:
                activeThreads.pop(owner)
        else:
            raise Exception("End of command without a begin.")
        self.tm.updateOwnerColumn()
        if len(activeThreads)==0 :
            self.updateButtons()
        
    def checkModify(self, boolFunc):
        """
        @param boolfunc une fonction pour décider du futur état de la coche
        étant donné l'état antérieur
        Modifie les coches des baladeurs
        """
        model=self.tm
        index0=model.createIndex(0,0)
        index1=model.createIndex(len(model.donnees)-1,0)
        srange=QItemSelectionRange(index0,index1)
        for i in srange.indexes():
            checked=bool(i.model().data(i,Qt.DisplayRole))
            model.setData(i, boolFunc(checked),Qt.EditRole)

    def checkAll(self):
        """
        Coche tous les baladeurs
        """
        self.checkModify(lambda x: True)
        
    def checkToggle(self):
        """
        Inverse la coche des baladeurs
        """
        self.checkModify(lambda x: not x)
       
    def checkNone(self):
        """
        Décoche tous les baladeurs
        """
        self.checkModify(lambda x: False)

    def namingADrive(self):
        """
        Gère un dialogue pour renommer un baladeur désigné par
        self.recentConnect
        """
        if self.availableNames:
            if self.recentConnect not in qApp.available.targets:
                return
            disk=qApp.available.targets[self.recentConnect]
            hint=db.readStudent(disk.serial, disk.uuid, ownedUsbDisk.tattooInDir(disk.mp))
            if hint != None:
                oldName=hint
            else:
                oldName=""
            d=nameAdrive.nameAdriveDialog(self, oldName=oldName,
                                          nameList=self.namesDialog.itemStrings(),
                                          driveIdent=(stickId, uuid, tattoo))
            d.show()
            result=d.exec_()
        return

    def cbAdded(self):
        """
        Renvoie une fonction de rappel pour l'abonnement aux évènements de l'arrière-boutique.
        Il s'agit de la fonction pour les disques branchés
        """
        def _cbAdded(man, obj):
            if qApp.available.modified:
                path=safePath(obj)
                self.recentConnect=str(path)
                delai=0.5 # petit délai pour que targets soit à jour
                QTimer.singleShot(delai, self.deviceAdded) 
                qApp.available.modified=False
        return _cbAdded
    
    def cbRemoved(self):
        """
        Renvoie une fonction de rappel pour l'abonnement aux évènements de l'arrière-boutique.
        Il s'agit de la fonction pour les disques débranchés
        """
        def _cbRemoved(man, obj):
            if qApp.available.modified:
                path=safePath(obj)
                if path in qApp.available.targets:
                    self.recentDisConnect=path
                    delai=0.5 # petit délai pour que targets soit à jour
                    QTimer.singleShot(delai, self.deviceRemoved)
                qApp.available.modified=False
        return _cbRemoved
    
    def deviceAdded(self):
        """
        Fonction de rappel pour un medium ajouté ; se base sur la valeur de self.recentConnect
        """
        if self.recentConnect not in qApp.available.targets:
            return
        disk=qApp.available.targets[self.recentConnect]
        if disk.parent: # c'est une partition
            QTimer.singleShot(0, self.namingADrive)
            self.findAllDisks()
            
    def deviceRemoved(self):
        """
        fonction de rappel pour un medium retiré ; se base sur la valeur de self.recentDisConnect
        """
        self.findAllDisks()
        
    def initRedoStuff(self):
        """
        Initialise des données pour le bouton central (refaire/stopper)
        """
        # réserve les icônes
        self.iconRedo = QIcon()
        self.iconRedo.addPixmap(QIcon.fromTheme("go-jump").pixmap(32), QIcon.Normal, QIcon.Off)
        self.iconStop = QIcon()
        self.iconStop.addPixmap(QIcon.fromTheme("stop").pixmap(32), QIcon.Normal, QIcon.Off)
        # réserve les phrases d'aide
        self.redoToolTip=QApplication.translate("MainWindow", "Refaire à nouveau", None)
        self.redoStatusTip=QApplication.translate("MainWindow", "Refaire à nouveau la dernière opération réussie, avec les baladeurs connectés plus récemment", None)
        self.stopToolTip=QApplication.translate("MainWindow", "Arrêter les opérations en cours", None)
        self.stopStatusTip=QApplication.translate("MainWindow", "Essaie d'arrêter les opérations en cours. À faire seulement si celles-ci durent trop longtemps", None)

    def applyPreferences(self):
        """
        Applique les préférences et les options de ligne de commande
        """
        prefs=db.readPrefs()
        self.schoolFile=prefs["schoolFile"]
        self.workdir=prefs["workdir"]
        self.manFileLocation=prefs["manfile"]
        self.mv=prefs["mv"]
        self.header=ownedUsbDisk.uDisk2.headers()
        self.findAllDisks()
        return

    def findAllDisks(self, other=None):
        """
        Initialisation du catalogue des disques USB connectés, et
        maintenance de l'interface graphique.
        @param other un catalogue déjà tout prêt de disques (None par défaut)
        """
        if other:
            qApp.available=other
        else:
            qApp.available=ownedUsbDisk.Available(access="firstFat")
        self.connectTableModel(qApp.available)
        connectedCount=int(qApp.available)
        self.ui.lcdNumber.display(connectedCount)
        self.t.resizeColumnsToContents()
        self.updateButtons()
        return
        
    def changeWd(self, newDir):
        """
        change le répertoire par défaut contenant les fichiers de travail
        @param newDir le nouveau nom de répertoire
        """
        self.workdir=newDir
        db.setWd(newDir)

    def tableClicked(self, idx):
        """
        fonction de rappel pour un double clic sur un élément de la table
        @param idx un QModelIndex
        """
        c=idx.column()
        mappedIdx=self.proxy.mapFromSource(idx)
        r=mappedIdx.row()
        h=self.header[c]
        if c==0:
            self.manageCheckBoxes()
            pass
        elif c==1:
            # case du propriétaire
            self.editOwner(mappedIdx)
        elif "mp" in h:
            cmd="xdg-open '%s'" %idx.data()
            subprocess.call(cmd, shell=True)
        elif "capacity" in h:
            mount=idx.model().partition(idx).mountPoint()
            dev,total,used,remain,pcent,path = self.diskSizeData(mount)
            pcent=int(pcent[:-1])
            w=diskFull.mainWindow(self,pcent,title=path, total=total, used=used)
            w.show()
        else:
            QMessageBox.warning(None,
                                QApplication.translate("Dialog","Double-clic non pris en compte",None),
                                QApplication.translate("Dialog","pas d'action pour l'attribut {a}",None).format(a=h))

    def manageCheckBoxes(self):
        """
        ouvre un dialogue pour permettre de gérer les cases à cocher globalement
        """
        cbDialog=checkBoxDialog.CheckBoxDialog(self)
        cbDialog.exec_()
        
    def diskSizeData(self, rowOrDev):
        """
        @param rowOrDev a row number in the tableView, or a device string
        @return a tuple dev,total,used,remain,pcent,path for the
        disk in the given row of the tableView
        (the tuple comes from the command df)
        """
        if type(rowOrDev)==type(0):
            path=qApp.available[rowOrDev][self.header.index("1mp")]
        else:
            path=rowOrDev
        cmd ="df '%s'" %path
        dfOutput=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0]
        dfOutput=str(dfOutput.split(b"\n")[-2])
        m = re.match("(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+).*", dfOutput).groups()
        return m


    def diskFromOwner(self,student):
        """
        trouve le disque qui correspond à un propriétaire, ou alors
        renvoie le premier disque inconnu.
        @param student le propriétaire du disque
        @return le disque correspondant à l'étudiant
        """
        defaultDisk=None
        for d in ownedUsbDisk.Available(access="firstFat"):
            s=db.readStudent(d.stickid, d.uuid, d.tattoo())
            if s==student :
                return d
            elif s==None and defaultDisk==None : 
                # premier disque inconnu
                defaultDisk=d
        return defaultDisk

        
    def editOwner(self, idx):
        """
        Édition du propriétaire d'une clé.
        @param idx un QModelIndex qui pointe sur le propriétaire d'une clé
        """
        student="%s" %self.tm.data(idx,Qt.DisplayRole).value()
        # on fait une modification dans la base de donnée des propriétaires de clés
        ownedUsbDisk.editRecord(self.diskFromOwner(student), hint=student)
        # après quoi on relit brutalement toute la list des clés connectées
        self.findAllDisks()

    def setAvailableNames(self, available):
        """
        Met à jour l'icône qui reflète la disponibilité de noms pour
        renommer automatiquement des baladeurs
        @param available vrai s'il y a des noms disponibles pour
        renommer des baladeurs.
        """
        self.availableNames=available
        if available:
            icon=self.namesFullIcon
            msg=self.namesFullTip
        else:
            icon=self.namesEmptyIcon
            msg=self.namesEmptyTip
        self.ui.namesButton.setIcon(icon)
        self.ui.namesButton.setToolTip(msg)
        self.ui.namesButton.setStatusTip(msg.replace("<br />",""))
        
    def updateButtons(self):
        """
        Désactive ou active les flèches selon que l'option correspondante
        est possible ou non. Pour les flèches : ça aurait du sens de préparer
        une opération de copie avant même de brancher des clés, donc on les
        active. Par contre démonter les clés quand elles sont absentes ça
        n'a pas d'utilité.
        Change l'icône du dialogue des noms selon qu'il reste ou non des
        noms disponibles dans le dialogue des noms.
        """
        global activeThreads, lastCommand
        active = len(qApp.available)>0
        for button in (self.ui.toButton,
                       self.ui.fromButton,
                       self.ui.delButton,
                       self.ui.umountButton):
            button.setEnabled(active)
        #modifie l'icone copyfrom/movefrom
        if self.mv:
            self.ui.fromButton.setIcon(self.movefromIcon)
        else:
            self.ui.fromButton.setIcon(self.copyfromIcon)
        # l'état du redoButton dépend de plusieurs facteurs
        # si un thread au moins est en cours, on y affiche un STOP actif
        # sinon on y met l'icône de lastCommand, et celle-ci sera active
        # seulement s'il y a une commande déjà validée
        if len(activeThreads) > 0:
            self.ui.redoButton.setIcon(self.iconStop)
            self.ui.redoButton.setToolTip(self.stopToolTip)
            self.ui.redoButton.setStatusTip(self.stopStatusTip)
            self.ui.redoButton.setEnabled(True)
        else:
            self.oldThreads=set() # vide l'ensemble puisque tout est fini
            self.ui.redoButton.setIcon(self.iconRedo)
            self.ui.redoButton.setToolTip(self.redoToolTip)
            self.ui.redoButton.setStatusTip(self.redoStatusTip)
            self.ui.redoButton.setEnabled(lastCommand!=None)
        l=self.namesDialog.ui.listWidget.findItems("*",Qt.MatchWildcard)
        if len(l)>0:
            self.ui.namesButton.setIcon(self.namesFullIcon)
        else:
            self.ui.namesButton.setIcon(self.namesEmptyIcon)

    def preference(self):
        """
        lance le dialogue des préférences
        """
        pref=preferences.preferenceWindow()
        pref.setValues(db.readPrefs())
        pref.show()
        pref.exec_()
        if pref.result()==QDialog.Accepted:
            db.writePrefs(pref.values())
            # on applique les préférences tout de suite sans redémarrer
            self.applyPreferences()

    def delFiles(self):
        """
        Lance l'action de supprimer des fichiers ou des répertoires dans les clés USB
        """
        titre1=QApplication.translate("Dialog","Choix de fichiers à supprimer",None)
        titre2=QApplication.translate("Dialog","Choix de fichiers à supprimer (jokers autorisés)",None)
        d=chooseInSticks.chooseDialog(self, titre1, titre2)
        ok = d.exec_()
        if ok:
            pathList=d.pathList()
            buttons=QMessageBox.Ok|QMessageBox.Cancel
            defaultButton=QMessageBox.Cancel
            reply=QMessageBox.warning(
                None,
                QApplication.translate("Dialog","Vous allez effacer plusieurs baladeurs",None),
                QApplication.translate("Dialog","Etes-vous certain de vouloir effacer : "+"\n".join(pathList),None),
                buttons, defaultButton)
            if reply == QMessageBox.Ok:
                cmd="usbThread.threadDeleteInUSB(p,{paths},subdir='Travail', logfile='{log}', parent=self)".format(paths=pathList,log=logFileName)
                for p in qApp.available:
                    if not p.selected: continue # pas les médias désélectionnés
                    registerCmd(cmd,p)
                    t=eval(cmd)
                    t.setDaemon(True)
                    t.start()
                    self.oldThreads.add(t)
            return True
        else:
            msgBox=QMessageBox.warning(
                None,
                QApplication.translate("Dialog","Aucun fichier sélectionné",None),
                QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None))
            return True

    def copyTo(self):
        """
        Lance l'action de copier vers les clés USB
        """
        d=copyToDialog1.copyToDialog1(parent=self, workdir=self.workdir)
        d.exec_()
        if d.ok==True:
            cmd="usbThread.threadCopyToUSB(p,{selected},subdir='{subdir}', logfile='{logfile}', parent=self)".format(selected=list(d.selectedList()), subdir=self.workdir, logfile=logFileName)
            ## !!!!!!!!!!!!!!!!! itérations dans qApp.available à revoir !
            for p in qApp.available:
                if not p.selected: continue # pas les médias désélectionnés
                registerCmd(cmd,p)
                t=eval(cmd)
                t.setDaemon(True)
                t.start()
                self.oldThreads.add(t)
            return True
        else:
            msgBox=QMessageBox.warning(
                None,
                QApplication.translate("Dialog","Aucun fichier sélectionné",None),
                QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None))
            return True

    def copyFrom(self):
        """
        Lance l'action de copier depuis les clés USB
        """
        titre1=QApplication.translate("Dialog","Choix de fichiers à copier",None)
        titre2=QApplication.translate("Dialog", "Choix de fichiers à copier depuis les baladeurs", None)
        okPrompt=QApplication.translate("Dialog", "Choix de la destination ...", None)
        d=chooseInSticks.chooseDialog(self, title1=titre1, title2=titre2, okPrompt=okPrompt)
        d.exec_()
        if not d.ok :
            msgBox=QMessageBox.warning(None,
                                       QApplication.translate("Dialog","Aucun fichier sélectionné",None),
                                       QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None))
            return True
        # bon, alors c'est OK pour le choix des fichiers à envoyer
        pathList=d.pathList()
        mp=d.selectedDiskMountPoint()
        initialPath=os.path.expanduser("~")
        destDir = QFileDialog.getExistingDirectory(
            None,
            QApplication.translate("Dialog","Choisir un répertoire de destination",None),
            initialPath)
        if destDir and len(destDir)>0 :
            if self.mv:
                cmd="""usbThread.threadMoveFromUSB(
                           p,{paths},subdir=self.workdir,
                           rootPath='{mp}', dest='{dest}', logfile='{log}',
                           parent=self)""".format(paths=pathList, mp=mp, dest=destDir, log=logFileName)
            else:
                cmd="""usbThread.threadCopyFromUSB(
                           p,{paths},subdir=self.workdir,
                           rootPath='{mp}', dest='{dest}', logfile='{log}',
                           parent=self)""".format(paths=pathList, mp=mp, dest=destDir, log=logFileName)
                
            for p in qApp.available:
                if not p.selected: continue # pas les médias désélectionnés
                # on devrait vérifier s'il y a des données à copier
                # et s'il n'y en a pas, ajouter des lignes au journal
                # mais on va laisser faire ça dans le thread
                # inconvénient : ça crée quelquefois des sous-répertoires
                # vides inutiles dans le répertoire de destination.
                registerCmd(cmd,p)
                t=eval(cmd)
                t.setDaemon(True)
                t.start()
                self.oldThreads.add(t)
            # on ouvre un gestionnaire de fichiers pour voir le résultat
            buttons=QMessageBox.Ok|QMessageBox.Cancel
            defaultButton=QMessageBox.Cancel
            if QMessageBox.question(
                None,
                QApplication.translate("Dialog","Voir les copies",None),
                QApplication.translate("Dialog","Voulez-vous voir les fichiers copiés ?",None),
                buttons, defaultButton)==QMessageBox.Ok:
                subprocess.call("xdg-open '%s'" %destDir,shell=True)
            return True
        else:
            msgBox=QMessageBox.warning(
                None,
                QApplication.translate("Dialog","Destination manquante",None),
                QApplication.translate("Dialog","Veuillez choisir une destination pour la copie des fichiers",None))
            return True

    def redoCmd(self):
        """
        Relance la dernière commande, mais en l'appliquant seulement aux
        baladeurs nouvellement branchés.
        """
        global lastCommand, pastCommands, activeThreads
        if len(activeThreads)>0:
            for thread in self.oldThreads:
                if thread.isAlive():
                    try:
                        thread._Thread__stop()
                        print (str(thread.getName()) + ' is terminated')
                    except:
                        print (str(thread.getName()) + ' could not be terminated')
        else:
            if lastCommand==None:
                return
            if QMessageBox.question(
                None,
                QApplication.translate("Dialog","Réitérer la dernière commande",None),
                QApplication.translate("Dialog","La dernière commande était<br>{cmd}<br>Voulez-vous la relancer avec les nouveaux baladeurs ?",None).format(cmd=lastCommand))==QMessageBox.Cancel:
                return
            for p in qApp.available:
                if p.owner in pastCommands[lastCommand] : continue
                exec(compile(lastCommand,'<string>','exec'))
                t.setDaemon(True)
                t.start()
                self.oldThreads.add(t)
                pastCommands[lastCommand].append(p.owner)

    def namesCmd(self):
        """
        montre le dialogue de choix de nouveaux noms à partir d'un
        fichier administratif.
        """
        self.namesDialog.show()

    def help(self):
        """
        Affiche le widget d'aide
        """
        w=help.helpWindow(self)
        w.show()
        w.exec_()

    def umount(self):
        """
        Démonte et détache les clés USB affichées
        """
        buttons=QMessageBox.Ok|QMessageBox.Cancel
        defaultButton=QMessageBox.Cancel
        button=QMessageBox.question (
            self,
            QApplication.translate("Main","Démontage des baladeurs",None),
            QApplication.translate("Main","Êtes-vous sûr de vouloir démonter tous les baladeurs cochés de la liste ?",None),
            buttons,defaultButton)
        if button!=QMessageBox.Ok:
            return
        for d in qApp.available.disks_ud():
            for partition in qApp.available.parts_ud(d.path):
                if partition.mp:
                    cmd="umount {0}".format(partition.mp)
                    subprocess.call(cmd, shell=True)
            cmd= "udisks --detach {0}".format(d.devStuff)
            subprocess.call(cmd, shell=True)
        self.findAllDisks()     # remet à jour le compte de disques
        self.operations=[] # remet à zéro la liste des opérations
                

    def connectTableModel(self, data):
        """
        Connecte le modèle de table à la table
        @param data les données de la table
        """
        self.visibleheader=[]
        for h in self.header:
            if h in ownedUsbDisk.uDisk2._itemNames:
                self.visibleheader.append(self.tr(ownedUsbDisk.uDisk2._itemNames[h]))
            else:
                self.visibleheader.append(h)
        self.tm=usbTableModel(self, self.visibleheader, data)
        self.t.setModel(self.tm)
        self.t.setItemDelegateForColumn(0, CheckBoxDelegate(self))
        self.t.setItemDelegateForColumn(1, UsbDiskDelegate(self))
        self.t.setItemDelegateForColumn(3, DiskSizeDelegate(self))
        self.proxy.setSourceModel(self.t.model())

        
    def sameDiskData(self, one, two):
        """
        @return True si les ensembles de uniqueId de one et two sont identiques
        """
        return len(one.targets) == len(two.targets) and \
            set([p.uniqueId() for p in one]) == set([p.uniqueId() for p in two])
Exemple #5
0
class MiClase(QMainWindow):
	
	def __init__(self, parent=None):
		QWidget.__init__(self, parent)
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)
		self.create_index("songs/*.txt")
		self.ui.gvVistaPrevia.setViewport(QGLWidget())
		self.font = QFont()
		self.text = QString("Trigales Worship")
		self.bgcolor = QColor("yellow")
		self.pic = QPixmap("fondo.jpg")
#		self.pic = self.pic.scaled(266, 200, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
		self.createScene()
#		self.mostrar_letra()
		### Conexiones
		self.connect(self.ui.btnAgregar, SIGNAL("clicked()"), self.agregar_a_la_lista)
		self.connect(self.ui.btnQuitar, SIGNAL("clicked()"), self.quitar_de_la_lista)
		self.connect(self.ui.listLista, SIGNAL("itemSelectionChanged()"), self.mostrar_letra)
#		self.connect(self.ui.fontTipoDeLetra, SIGNAL("currentFontChanged(const QFont &)"), self.setFont)
#		self.connect(self.ui.btnTipoLetra, SIGNAL("clicked()"), self.setFont)
		self.connect(self.ui.btnComenzar, SIGNAL("clicked()"), self.comenzarPresentacion)
		self.connect(self.ui.btnSiguiente,  SIGNAL("clicked()"),  self.siguiente)
		self.connect(self.ui.btnAnterior,   SIGNAL("clicked()"),  self.anterior)
		self.connect(self.ui.btnInicio, SIGNAL("clicked()"),  self.inicio)
		self.connect(self.ui.btnFin, SIGNAL("clicked()"),  self.fin)
#		self.connect(self.ui.btnColor, SIGNAL("clicked()"),  self.setColor)

	def create_index(self, ruta):
		c = ColectionParser(ruta)
		self.indice = c.crear_indice()
		for i in self.indice.keys():
		  self.ui.listColeccion.addItem(QString(i))
		self.ui.listColeccion.sortItems()

	def agregar_a_la_lista(self):
		self.ui.listLista.addItem(self.ui.listColeccion.currentItem().text())

	def quitar_de_la_lista(self):
		self.ui.listLista.takeItem(self.ui.listLista.row(self.ui.listLista.currentItem()))

	def mostrar_letra(self):
		titulo = unicode(self.ui.listColeccion.currentItem().text())
		s = SongParser(self.indice[titulo])
		parrafos = s.crear_parrafos()
		self.ui.listControles.clear()
		for parrafo in parrafos:
			self.ui.listControles.addItem(parrafo)

	def createScene(self):
		print "redibujando"
		scene = QGraphicsScene()
		self.ui.gvVistaPrevia.setScene(scene)
		#scene.addPixmap(self.pic)
		scene.setFont(self.font)
#		scene.addText(self.text, self.font)
		scene.addText(self.text)
		scene.setBackgroundBrush(self.bgcolor)
		self.scene = scene
		#self.ui.gvVistaPrevia.fitInView(scene.sceneRect());
		#self.ui.gvVistaPrevia.alignment()
		self.ui.gvVistaPrevia.update()

	def setFont(self):
#		self.font = QFont(font)
		self.font, ok = QFontDialog.getFont()
		if ok:
			self.createScene()

	def setText(self, text):
		self.text = QString(text)
		self.createScene()

	def setPic(self, pic):
		self.pic = QPixmap(pic)
		self.createScene()

	def setPic(self, color):
		self.bgcolor = QColor(color)
		self.createScene()

	def comenzarPresentacion(self):
		salida = Salida()
		salida.show()

	def siguiente(self):
		fila = self.ui.listControles.currentRow()
		if fila == -1:
			self.ui.listControles.setCurrentRow(0)
		elif fila == self.ui.listControles.count() - 1:
			print "ultima fila"
		else:
			self.ui.listControles.setCurrentRow(fila + 1)

	def anterior(self):
		fila = self.ui.listControles.currentRow()
		if fila == -1:
			self.ui.listControles.setCurrentRow(self.ui.listControles.count() - 1)
		elif fila == 0:
			print "primera fila"
		else:
			self.ui.listControles.setCurrentRow(fila - 1)

	def inicio(self):
		self.ui.listControles.setCurrentRow(0)

	def fin(self):
		self.ui.listControles.setCurrentRow(self.ui.listControles.count() - 1)

	def setColor(self):
		color = QColorDialog.getColor()
		if color.isValid():
			self.bgcolor = color
			self.createScene()
Exemple #6
0
class MainWindow(QMainWindow, Ui_MainWindow):
    project_name = None
    src_folder_path = None
    dst_folder_path = None

    def __init__(self, parent=None):
        super(MainWindow, self).__init__()
        self.conf = configure()
        self.setFixedSize(600, 500)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        #将进度初始化为零
        self.ui.progressBar.setValue(0)

        #从配置文件中读取源文件和目标文件路径并进行显示
        self.read_config_file()

        #菜单栏
        self.ui.file_open.triggered.connect(self.on_file_open_clicked)
        self.ui.file_save.triggered.connect(self.on_file_save_clicked)
        self.ui.file_set.triggered.connect(self.on_file_set_cliked)
        self.ui.file_quit.triggered.connect(self.close)
        self.ui.help_help.triggered.connect(self.on_help_help_clicked)
        self.ui.help_abut.triggered.connect(self.on_help_about_clicked)

        #按钮
        self.ui.set_src_button.clicked.connect(self.set_src_button_cliked)
        self.ui.set_des_button.clicked.connect(self.set_dst_button_cliked)
        self.ui.open_dst_button.clicked.connect(self.open_dst_button_cliked)
        self.ui.del_old_button.clicked.connect(self.del_old_button_clicked)
        self.ui.start_button.clicked.connect(self.start_button_cliked)

    # '打开配置'菜单
    def on_file_open_clicked(self):
        self.output_log('打开配置文件')
        pass

    # '保存配置'菜单
    def on_file_save_clicked(self):
        self.output_log('保存配置文件')
        pass

    # '设置' 菜单
    def on_file_set_cliked(self):
        self.project_name, ok = QInputDialog.getText(self, '设置', '请输入项目名称:')
        if ok:
            #修改项目名称显示
            self.ui.project_name_label.setText(self.project_name)
            self.output_log('设置项目名称成功')
            # 写入配置文件
            try:
                self.conf.set_project_name(self.project_name)
            except Exception as e:
                logging.debug(e)

    # 'help' 菜单
    def on_help_help_clicked(self):
        QMessageBox.about(
            self, '使用说明', '本软件可以将源文件夹中的最新文件夹复制到目标文件夹,'
            '一般用于一键下载项目中的最新升级包至本地目录')

    #‘关于’菜单
    def on_help_about_clicked(self):
        QMessageBox.about(self, '关于', 'version:0.1' '\n' 'author: yasin')

    #‘设置源文件夹’按钮
    def set_src_button_cliked(self):
        # self.output_log('click set src folder button')
        self.src_folder_path_get = QFileDialog.getExistingDirectory(
            self, '选取文件夹', '/home')
        if self.src_folder_path_get:
            self.src_folder_path = self.src_folder_path_get
            self.output_log('设置源文件成功')
            src_str_prefix = '源文件夹: '
            src_str = src_str_prefix + self.src_folder_path
            self.ui.src_floder_label.setText(src_str)
            # 写入配置文件
            try:
                self.conf.set_src_folder_path(self.src_folder_path)
            except Exception as e:
                logging.debug(e)
        else:
            self.output_log('未设置源文件夹')

    #‘设置目标文件夹’按钮
    def set_dst_button_cliked(self):
        #self.output_log('click set dest folder button')
        self.dst_folder_path_get = QFileDialog.getExistingDirectory(
            self, '选取文件夹', '/home')
        if self.dst_folder_path_get:
            self.dst_folder_path = self.dst_folder_path_get
            self.output_log('设置目标文件成功')
            dst_str_prefix = '目标文件夹: '
            dst_str = dst_str_prefix + self.dst_folder_path
            self.ui.dst_floder_label.setText(dst_str)
            #写入配置文件
            try:
                self.conf.set_dst_folder_path(self.dst_folder_path)
            except Exception as e:
                logging.debug(e)
        else:
            self.output_log('未设置目标文件夹')

    # ‘打开目标文件夹按钮
    def open_dst_button_cliked(self):
        abs_path = os.path.abspath(self.dst_folder_path)
        open_dst_cmd = 'explorer.exe ' + abs_path
        logging.debug(open_dst_cmd)
        try:
            subprocess.Popen(open_dst_cmd)
        except Exception as e:
            logging.error(e)

    # '删除旧文件'按钮
    def del_old_button_clicked(self):
        self.output_log('点击删除旧文件按钮')
        reply = QMessageBox.warning(
            self, '警告', '您的操作会删除目标文件夹内所有旧目录和文件,只保留最新的一个文件或文件夹,'
            '请确认是否删除?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if (reply == QMessageBox.Yes):
            self.output_log('确认删除旧文件')
            try:
                if (delete_all_old(self.dst_folder_path)):
                    self.output_log('删除成功!')
            except Exception as e:
                logging.error(e)
        else:
            pass

    #‘开始’按钮
    def start_button_cliked(self):
        #logging.debug('start button cliked')
        if (self.src_folder_path == None or self.src_folder_path == ''):
            self.output_log('源文件未设置!')
            # TODO
        elif (self.dst_folder_path == None or self.dst_folder_path == ''):
            self.output_log('目标文件夹未设置!')
            #TODO
        else:
            # 把按钮禁用掉
            self.ui.start_button.setDisabled(True)
            # 新建对象,传入参数
            self.start_thread = function(self.src_folder_path,
                                         self.dst_folder_path)
            # 连接子线程的进度信号和槽函数
            self.start_thread.progress_signal.connect(self.show_progress)
            # 连接子进程的结束信号和槽函数
            self.start_thread.finish_signal.connect(self.start_copy_end)
            try:
                self.start_thread.start()
            except Exception as e:
                logging.debug(e)

    #进度信号槽函数
    def show_progress(self, progress):
        self.ui.progressBar.setValue(progress)

    #start 按钮结束
    def start_copy_end(self, result):
        logging.debug('receive end signal')
        logging.debug(result)
        if (result == 'dir_name_repetition'):
            try:
                reply = QMessageBox.question(
                    self, '警告', '目标文件夹已有源文件内的最新目录,您的操作会删除该最新目录,请确认是否删除?',
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if (reply == QMessageBox.Yes):
                    self.output_log('确认删除')
                    real_dir_name = os.path.split(
                        get_latest_object(self.src_folder_path))[1]
                    logging.debug(real_dir_name)
                    dir_to_be_deleted = os.path.join(self.dst_folder_path,
                                                     real_dir_name)
                    logging.debug('dir to be deleted is %s' %
                                  dir_to_be_deleted)
                    #删除目标文件夹中重复最新文件夹
                    try:
                        shutil.rmtree(dir_to_be_deleted)
                        self.output_log('删除成功')
                    except Exception as e:
                        self.output_log('删除失败')
                        logging.error(e)
                else:
                    self.output_log('取消删除')
            except Exception as e:
                logging.error(e)
        elif (result == 'end_with_success'):
            self.ui.progressBar.setValue(100)
            dir_name = os.path.split(get_latest_object(
                self.src_folder_path))[1]
            logging.debug(dir_name)
            dst_dir_abs = os.path.abspath(self.dst_folder_path)
            abs_dir_path = os.path.join(dst_dir_abs, dir_name)
            self.open_cmd = 'explorer.exe ' + abs_dir_path
            logging.debug(self.open_cmd)
            try:
                subprocess.Popen(self.open_cmd)
            except Exception as e:
                logging.error(e)

            self.output_log('下载完成!')

        # 恢复按钮
        self.ui.start_button.setDisabled(False)

    #输出log到GUI文本框
    def output_log(self, str):
        self.ui.textBrowser.append(str)

    def read_config_file(self):
        #读取项目名称
        self.project_name = self.conf.get_project_name()
        if (self.project_name != None):
            self.ui.project_name_label.setText(self.project_name)
        else:
            self.ui.project_name_label.setText('默认名称')
        #读取源文件夹路径
        src_str_prefix = '源文件夹: '
        self.src_folder_path = self.conf.get_src_folder_path()
        if (self.src_folder_path != None):
            src_str = src_str_prefix + self.src_folder_path
            self.ui.src_floder_label.setText(src_str)
        # 读取目标文件夹路径
        dst_str_prefix = '目标文件夹: '
        self.dst_folder_path = self.conf.get_dst_folder_path()
        if (self.dst_folder_path != None):
            dst_str = dst_str_prefix + self.dst_folder_path
            self.ui.dst_floder_label.setText(dst_str)
 def __init__(self, parent=None):
     super(MainWindow, self).__init__(parent)
     self.midiOutputDevicesDict = {}
     self.midiInputDevicesDict = {}
     self.onValue = 64
     self.offValue = 0
     #control change values
     self.dialDict = {'resonanceDial': 48, 'cutoffDial': 49, 'lfoRateDial': 50, 'lfoDepthDial': 51,
     'envAmountDial': 52, 'glideAmountDial' : 53, 'oscPWMDial': 54, 'oscDetuneDial' :55,
     'filterDecaySlider': 58, 'filterAttackSlider' :59, 'ampDecaySlider' :60, 'ampAttackSlider': 61}
     
     self.buttonDict = {'oscFMOn':[65, self.onValue], 'oscFMOff':[65, self.offValue], 'lfoRandomOn':[66, self.onValue],
                   'lfoRandomOff':[66, self.offValue], 'lfoSquareOn':[67, self.onValue], 
                   'lfoTriangleOn':[67, self.offValue], 'lpOn':[68, self.offValue],'hpOn':[68, self.onValue],
                   'distortionOn':[69, self.onValue], 'distortionOff':[69, self.offValue], 
                   'lfoOn':[70, self.onValue], 'lfoOff':[70, self.offValue], 'lfoOscOn':[71, self.onValue],
                   'lfoFilterOn':[71, self.offValue], 'antiAliasOn':[72, self.onValue], 'antiAliasOff':[72, self.offValue],
                   'oscBOctaveOn':[73, self.onValue], 'oscBOctaveOff':[73, self.offValue],'oscBOn':[74, self.onValue], 
                   'oscBOff':[74, self.offValue], 'oscBWaveSquare':[75, self.onValue], 'oscBWaveTri':[75, self.offValue], 
                   'envSustainOn':[76, self.onValue], 'envSustainOff':[76, self.offValue], 'oscANoiseOn':[77, self.onValue], 
                   'oscANoiseOff':[77, self.offValue],'pwmSweepOn':[78, self.onValue], 'pwmSweepOff':[78, self.offValue], 
                   'oscAPWMOn':[79, self.onValue], 'oscASawOn':[79, self.offValue]}        
     
     self.buttonBoxDict = {'oscAWaveBox':'oscAWaveGroup', 'oscANoiseBox':'oscANoiseGroup', 'oscBEnableBox':'oscBEnableGroup', 
                             'oscBWaveBox':'oscBWaveGroup', 'oscBOctaveBox':'oscBOctaveGroup', 'oscFMBox':'oscFMGroup',
                             'lfoEnableBox':'lfoEnableGroup', 'lfoDestBox':'lfoDestGroup', 'lfoWaveBox':'lfoWaveGroup', 
                             'lfoRandomBox':'lfoRandomGroup', 'filterModeBox':'filterModeGroup', 'distortionBox':'distortionGroup',
                             'antiAliasBox':'antiAliasGroup', 'envSustainBox':'envSustainGroup', 'pwmSweepBox':'pwmSweepGroup', 
                             'oscAWaveBox':'oscAWaveGroup'}
     
     self.ui = Ui_MainWindow()        
     self.ui.setupUi(self)
     self.windowHandler = MainWindowHandler(self.dialDict, self.buttonDict) 
     self.settings = QtCore.QSettings('MeeblipControl', 'MeeblipControl')        
     #connect signals and slots
     for dial, cc, in self.dialDict.iteritems():
         currentDial = getattr(self.ui, dial)
         currentDial.setRange(0,127)
         dialChanged = partial(self.windowHandler.dialChanged, mainWindowInstance=self, cc=cc)
         currentDial.valueChanged.connect(dialChanged)
         currentDial.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
         currentDial.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, widgetName=dial))
     for button, buttonList in self.buttonDict.iteritems():
         currentButton = getattr(self.ui, button)
         buttonFunc = partial(self.windowHandler.buttonChanged, mainWindowInstance=self, value=buttonList[1], cc=buttonList[0], button=currentButton)
         currentButton.toggled.connect(buttonFunc)
     for buttonGroupBox in self.buttonBoxDict.keys():
         currentGroup = getattr(self.ui, buttonGroupBox)
         currentGroup.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
         currentGroup.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, 
                                                                 widgetName=buttonGroupBox))
     
     class _MidiInput(QtCore.QThread):
         dataReceivedSignal = QtCore.pyqtSignal(int, int)
         midiExceptionSignal = QtCore.pyqtSignal(str)
         
         def __init__(self, mainWindow, mainWindowHandler, parent=None):
             super(_MidiInput, self).__init__(parent)
             self.mainWindow = mainWindow
             self.mainWindowHandler = mainWindowHandler
             
         def run(self):
             while True:
                 if self.mainWindowHandler.midiSelectedInputDevicesDict:
                     try:
                         self.mainWindowHandler.midiInputMutex.lock()
                         for inputDevice in self.mainWindowHandler.midiSelectedInputDevicesDict.values():
                                     if inputDevice.poll():
                                         data = inputDevice.read(1)
                                         channel = (data[0][0][0] & 0xF) + 1
                                         if channel == self.mainWindowHandler.midiInputChannel:
                                             status = data[0][0][0] & 0xF0
                                             cc = data[0][0][1]
                                             #if a CC message arrives and is mapped
                                             if status == 0xB0 and cc in self.mainWindowHandler.currentPatch.patchMIDIMapDict:  
                                                 value = data[0][0][2]
                                                 self.dataReceivedSignal.emit(cc, value)
                                             else:
                                                 if self.mainWindowHandler.midiSelectedOutputDevice:
                                                     self.mainWindowHandler.midiSelectedOutputDevice.write(data)                    
                     except midi.MidiException as e:
                         self.midiExceptionSignal.emit(unicode(e))
                     finally:
                         self.mainWindowHandler.midiInputMutex.unlock()
                 self.usleep(200) #don't hog the processor in the polling loop!
     #initialize MIDI, start listening for incoming MIDI data       
     midi.init()
     self.getMIDIDevices()
     self.midiInputThread = _MidiInput(self, self.windowHandler)
     self.midiInputThread.dataReceivedSignal.connect(self.midiInputCallback)         
     self.midiInputThread.midiExceptionSignal.connect(lambda e: QtGui.QMessageBox.warning(self, "MIDI Error", unicode(e)))
     self.midiInputThread.start()
     self.ui.action_Save.setEnabled(False)
     self.restoreSettings(self.windowHandler)
     self.windowHandler.new(self)
class MainWindow(QtGui.QMainWindow):    
    
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.midiOutputDevicesDict = {}
        self.midiInputDevicesDict = {}
        self.onValue = 64
        self.offValue = 0
        #control change values
        self.dialDict = {'resonanceDial': 48, 'cutoffDial': 49, 'lfoRateDial': 50, 'lfoDepthDial': 51,
        'envAmountDial': 52, 'glideAmountDial' : 53, 'oscPWMDial': 54, 'oscDetuneDial' :55,
        'filterDecaySlider': 58, 'filterAttackSlider' :59, 'ampDecaySlider' :60, 'ampAttackSlider': 61}
        
        self.buttonDict = {'oscFMOn':[65, self.onValue], 'oscFMOff':[65, self.offValue], 'lfoRandomOn':[66, self.onValue],
                      'lfoRandomOff':[66, self.offValue], 'lfoSquareOn':[67, self.onValue], 
                      'lfoTriangleOn':[67, self.offValue], 'lpOn':[68, self.offValue],'hpOn':[68, self.onValue],
                      'distortionOn':[69, self.onValue], 'distortionOff':[69, self.offValue], 
                      'lfoOn':[70, self.onValue], 'lfoOff':[70, self.offValue], 'lfoOscOn':[71, self.onValue],
                      'lfoFilterOn':[71, self.offValue], 'antiAliasOn':[72, self.onValue], 'antiAliasOff':[72, self.offValue],
                      'oscBOctaveOn':[73, self.onValue], 'oscBOctaveOff':[73, self.offValue],'oscBOn':[74, self.onValue], 
                      'oscBOff':[74, self.offValue], 'oscBWaveSquare':[75, self.onValue], 'oscBWaveTri':[75, self.offValue], 
                      'envSustainOn':[76, self.onValue], 'envSustainOff':[76, self.offValue], 'oscANoiseOn':[77, self.onValue], 
                      'oscANoiseOff':[77, self.offValue],'pwmSweepOn':[78, self.onValue], 'pwmSweepOff':[78, self.offValue], 
                      'oscAPWMOn':[79, self.onValue], 'oscASawOn':[79, self.offValue]}        
        
        self.buttonBoxDict = {'oscAWaveBox':'oscAWaveGroup', 'oscANoiseBox':'oscANoiseGroup', 'oscBEnableBox':'oscBEnableGroup', 
                                'oscBWaveBox':'oscBWaveGroup', 'oscBOctaveBox':'oscBOctaveGroup', 'oscFMBox':'oscFMGroup',
                                'lfoEnableBox':'lfoEnableGroup', 'lfoDestBox':'lfoDestGroup', 'lfoWaveBox':'lfoWaveGroup', 
                                'lfoRandomBox':'lfoRandomGroup', 'filterModeBox':'filterModeGroup', 'distortionBox':'distortionGroup',
                                'antiAliasBox':'antiAliasGroup', 'envSustainBox':'envSustainGroup', 'pwmSweepBox':'pwmSweepGroup', 
                                'oscAWaveBox':'oscAWaveGroup'}
        
        self.ui = Ui_MainWindow()        
        self.ui.setupUi(self)
        self.windowHandler = MainWindowHandler(self.dialDict, self.buttonDict) 
        self.settings = QtCore.QSettings('MeeblipControl', 'MeeblipControl')        
        #connect signals and slots
        for dial, cc, in self.dialDict.iteritems():
            currentDial = getattr(self.ui, dial)
            currentDial.setRange(0,127)
            dialChanged = partial(self.windowHandler.dialChanged, mainWindowInstance=self, cc=cc)
            currentDial.valueChanged.connect(dialChanged)
            currentDial.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            currentDial.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, widgetName=dial))
        for button, buttonList in self.buttonDict.iteritems():
            currentButton = getattr(self.ui, button)
            buttonFunc = partial(self.windowHandler.buttonChanged, mainWindowInstance=self, value=buttonList[1], cc=buttonList[0], button=currentButton)
            currentButton.toggled.connect(buttonFunc)
        for buttonGroupBox in self.buttonBoxDict.keys():
            currentGroup = getattr(self.ui, buttonGroupBox)
            currentGroup.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            currentGroup.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, 
                                                                    widgetName=buttonGroupBox))
        
        class _MidiInput(QtCore.QThread):
            dataReceivedSignal = QtCore.pyqtSignal(int, int)
            midiExceptionSignal = QtCore.pyqtSignal(str)
            
            def __init__(self, mainWindow, mainWindowHandler, parent=None):
                super(_MidiInput, self).__init__(parent)
                self.mainWindow = mainWindow
                self.mainWindowHandler = mainWindowHandler
                
            def run(self):
                while True:
                    if self.mainWindowHandler.midiSelectedInputDevicesDict:
                        try:
                            self.mainWindowHandler.midiInputMutex.lock()
                            for inputDevice in self.mainWindowHandler.midiSelectedInputDevicesDict.values():
                                        if inputDevice.poll():
                                            data = inputDevice.read(1)
                                            channel = (data[0][0][0] & 0xF) + 1
                                            if channel == self.mainWindowHandler.midiInputChannel:
                                                status = data[0][0][0] & 0xF0
                                                cc = data[0][0][1]
                                                #if a CC message arrives and is mapped
                                                if status == 0xB0 and cc in self.mainWindowHandler.currentPatch.patchMIDIMapDict:  
                                                    value = data[0][0][2]
                                                    self.dataReceivedSignal.emit(cc, value)
                                                else:
                                                    if self.mainWindowHandler.midiSelectedOutputDevice:
                                                        self.mainWindowHandler.midiSelectedOutputDevice.write(data)                    
                        except midi.MidiException as e:
                            self.midiExceptionSignal.emit(unicode(e))
                        finally:
                            self.mainWindowHandler.midiInputMutex.unlock()
                    self.usleep(200) #don't hog the processor in the polling loop!
        #initialize MIDI, start listening for incoming MIDI data       
        midi.init()
        self.getMIDIDevices()
        self.midiInputThread = _MidiInput(self, self.windowHandler)
        self.midiInputThread.dataReceivedSignal.connect(self.midiInputCallback)         
        self.midiInputThread.midiExceptionSignal.connect(lambda e: QtGui.QMessageBox.warning(self, "MIDI Error", unicode(e)))
        self.midiInputThread.start()
        self.ui.action_Save.setEnabled(False)
        self.restoreSettings(self.windowHandler)
        self.windowHandler.new(self)
        
    def midiInputCallback(self, cc, value):        
        widgetName = self.windowHandler.currentPatch.patchMIDIMapDict[cc]
        if widgetName in self.dialDict:
            getattr(self.ui, widgetName).setValue(value)
            self.windowHandler.dialChanged(value, self, self.dialDict[widgetName])
        elif widgetName in self.buttonBoxDict:
            buttonGroup = getattr(self.ui, self.buttonBoxDict[widgetName])
            checkedButton = buttonGroup.checkedButton()
            checkedButtonName = str(checkedButton.objectName())
            if value >= self.onValue and self.buttonDict[checkedButtonName][1] == self.offValue:
                for button in buttonGroup.buttons():
                    if button != checkedButton:
                        button.toggle()
            elif value < self.onValue and self.buttonDict[checkedButtonName][1] == self.onValue:
                for button in buttonGroup.buttons():
                    if button != checkedButton:
                        button.toggle()

    def getMIDIDevices(self):
        midiOutputDevices = []
        midiInputDevices = []
        for index in xrange(0, midi.get_count()):
            device = midi.get_device_info(index)
            deviceName = device[1]
            if device[3] == 1 and device[4] == 0: #if the device is an output and not opened
                setattr(self, deviceName, QtGui.QAction(QtGui.QIcon(''), deviceName, self))
                deviceWidget = getattr(self, deviceName)
                deviceWidget.setCheckable(True)
                midiOutputDevices.append(deviceWidget)
                self.midiOutputDevicesDict[deviceWidget] = index
            elif device[2] == 1 and device[4] == 0: #if devices is an input and not opened
                deviceName = device[1]
                setattr(self, deviceName, QtGui.QAction(QtGui.QIcon(''), deviceName, self))
                deviceWidget = getattr(self, deviceName)
                deviceWidget.setCheckable(True)
                midiInputDevices.append(deviceWidget)
                self.midiInputDevicesDict[deviceWidget] = index
                
        if midiOutputDevices:
            self.ui.midiOutputDevicesMenu = self.ui.menubar.addMenu("&Midi Output Device")
            self.ui.midiOutputDevicesMenu.addActions(midiOutputDevices)
        if midiInputDevices:
            self.ui.midiInputDevicesMenu = self.ui.menubar.addMenu("&Midi Input Devices")
            self.ui.midiInputDevicesMenu.addActions(midiInputDevices)
        
        for device in midiOutputDevices:
            outputFunction = partial(self.windowHandler.midiOutputSelect, mainWindowInstance=self, device=device)
            device.triggered.connect(outputFunction)

        for device in midiInputDevices:
            inputFunction = partial(self.windowHandler.midiInputSelect, mainWindowInstance=self, device=device)
            device.triggered.connect(inputFunction)
    
    def restoreSettings(self, mainWindowHandler):
        mainWindowHandler.midiInputChannel = self.settings.value('midiInputChannel').toInt()[0]
        if not mainWindowHandler.midiInputChannel:
            self.midiInputChannel = 1
        self.midiOutputChannel = self.settings.value('midiOutputChannel').toInt()[0]
        if not mainWindowHandler.midiOutputChannel:
            mainWindowHandler.midiOutputChannel = 1
        
        registryInputDeviceList = []    
        for inputDeviceHash in self.settings.value('midiInputDevices', []).toList():
            inputDeviceHash = str(inputDeviceHash.toString())
            for deviceWidget, index in self.midiInputDevicesDict.iteritems():
                deviceName = midi.get_device_info(index)[1]
                deviceHash = hashlib.md5(deviceName).hexdigest()
                if deviceHash == inputDeviceHash:
                    deviceWidget.setChecked(True)
                    self.windowHandler.midiInputSelect(self, deviceWidget)
                    registryInputDeviceList.append(deviceHash)            
        self.settings.setValue('midiInputDevices', registryInputDeviceList) 
        #update the registry so unplugged devices aren't 
        #reselected when plugged back in at some later time
        outputDeviceHash = str(self.settings.value('midiOutputDevice').toString())
        registryOutputDevice = None
        for deviceWidget, index in self.midiOutputDevicesDict.iteritems():
                deviceName = midi.get_device_info(index)[1]
                deviceHash = hashlib.md5(deviceName).hexdigest()
                if deviceHash == outputDeviceHash:
                    deviceWidget.setChecked(True)
                    self.windowHandler.midiOutputSelect(self, deviceWidget)
                    registryOutputDevice = deviceHash
        self.settings.setValue('midiOutputDevice', registryOutputDevice)
            
    @QtCore.pyqtSignature("")
    def on_action_MIDI_Channel_triggered(self):
        self.windowHandler.menuOptions(self)
        
    @QtCore.pyqtSignature("")
    def on_action_Save_as_triggered(self):
        self.windowHandler.saveAs(self)
    
    @QtCore.pyqtSignature("")
    def on_action_Load_triggered(self):
        self.windowHandler.load(self)
        
    @QtCore.pyqtSignature("")
    def on_action_Save_triggered(self):
        self.windowHandler.save(self)
            
    @QtCore.pyqtSignature("")
    def on_action_New_triggered(self):
        self.windowHandler.new(self)
    
    @QtCore.pyqtSignature("")
    def on_action_Export_patch_as_MIDI_triggered(self):
        self.windowHandler.midiExport(self)
    
    @QtCore.pyqtSignature("")
    def on_action_Import_MIDI_patch_triggered(self):
        self.windowHandler.midiImport(self)