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 __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)
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
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])
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()
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)