class EkdSaveDialog(QDialog): ''' EkdSaveDialog : Classe représentant la boite de dialogue utiliser lors de l'enregistrement des modification sur un fichier donné attributs : suffix - Suffix utilisé en filtre filter - Filtre (déduit à partir du suffix) chemin - Chemin du dernier fichier enregistré multiple - va-t-on enregistrer plus d'un fichier ? (ex: extraction d'image d'une vidéo) méthodes : getFile - Invoque la boite de dialogue et retourne le fichier saisi ''' ### Pourquoi avoir réimplémenté cette classe au lieu de passer par ### QFileDialog ? ## Correction du bug de consommation mémoire ## ## Explication du problème : ## Par défaut un QFileDialog utilise un QFileSystemModel qui lui ## crée un QWatchFileSystem ## Hors QWatchFileSystem scan régulièrement les changement ## dans le répertoire courant ## Ce phénomène provoque une réaction en chaine : ## 1 - je choisi mon répertoire de destination d'enregistrement ## de mes images ## 2 - ffmpeg se lance ## 3 - ffmpeg crée un fichier dans l'arborescence ## 4 - QWatchFileSystem (est toujours dans le répertoire courant) ## détecte un changement ## et recharge l'ensemble du contenue du répertoire ## 5 - goto 3 jusqu'à plus de mémoire ou fin du process ffmpeg ## ## def __init__(self, parent, path = None, suffix = '', title = u"Sauver", multiple = False, mode = None): if type(suffix) == tuple or type(suffix) == list : sfilter="" for s in suffix : sfilter += "*"+s+" " self.filter=sfilter[:-1] # Si on a plusieur suffix, on prend le premier par défaut pour # la sauvegarde self.suffix = suffix[0] else : self.suffix = suffix self.filter = "*" + self.suffix QDialog.__init__(self, parent) self.setWindowTitle(title) self.multiple = multiple self.mode = mode if not path: if self.mode == "image" : path = EkdConfig.get("general", "image_output_path") elif self.mode == "video" : path = EkdConfig.get("general", "video_output_path") elif self.mode == "audio" : path = EkdConfig.get("general", "sound_output_path") else : path = unicode(QDir.homePath()) # Nom du répertoire courant self.location = QLabel("<b>%s</b>" % path) # Variable permettant de savoir à tout moment le répertoire courant. self.currentDir = path self.mkdirButton = QPushButton(u" Créer un répertoire") self.mkdirButton.setIcon(QIcon("Icones" + os.sep + "add_sub_task.png")) if int(EkdConfig.get("general", "show_hidden_files")) : #print "hidden shown" EkdPrint(u"hidden shown") shf = QDir.Hidden else : shf = QDir.Readable # Liste des fichiers self.dirList = QListView() sorting = QDir.DirsFirst if int(EkdConfig.get("general", "ignore_case")): sorting |= QDir.IgnoreCase self.sorting = sorting self.flags = QDir.Files | QDir.Readable | shf self.dirModel = QStandardItemModel() self.dirList.setModel(self.dirModel) self.updateDir(path) self.dirList.setWrapping(True) #panneau latéral self.dirTree = QTreeView() self.dirModelLight = QDirModel(QStringList(""), QDir.AllDirs | QDir.NoDotAndDotDot | shf, QDir.DirsFirst | QDir.Name | QDir.IgnoreCase) self.dirTree.setModel(self.dirModelLight) self.dirTree.setColumnHidden(1,True) self.dirTree.setColumnHidden(2,True) self.dirTree.setColumnHidden(3,True) self.dirTree.setMaximumWidth(200) self.dirTree.setMinimumWidth(150) self.dirTree.setCurrentIndex(self.dirModelLight.index(path)) self.dirTree.resizeColumnToContents(0) self.connect(self.dirTree, SIGNAL("pressed(QModelIndex)"), self.updateLatDir) self.connect(self.dirTree, SIGNAL("expanded(QModelIndex)"), self.treeMAJ) self.connect(self.dirTree, SIGNAL("collapsed(QModelIndex)"), self.treeMAJ) # Nom du fichier self.fileField = QLineEdit() # Nom du filtre self.filterField = QComboBox() self.filterField.addItems(QStringList(self.filter)) # Bouton de sauvegarde et d'annulation self.saveButton = QPushButton(_(u" Enregistrer")) self.saveButton.setIcon(QIcon("Icones" + os.sep + "action.png")) self.cancelButton = QPushButton(_(u" Annuler")) self.cancelButton.setIcon(QIcon("Icones" + os.sep + "annuler.png")) # Organisation des différents objets de la boite de dialogue self.layout = QHBoxLayout(self) self.layout.addWidget(self.dirTree) self.filelinelayout = QGridLayout() self.filelinelayout.addWidget(self.location, 0, 0, 1, 0) self.filelinelayout.addWidget(self.mkdirButton, 0, 2) self.filelinelayout.addWidget(self.dirList, 1, 0, 1, 0) self.filelinelayout.addWidget(QLabel(_("Nom de fichier : ")), 2, 0) self.filelinelayout.addWidget(self.fileField, 2, 1) self.filelinelayout.addWidget(self.saveButton, 2, 2) self.filelinelayout.addWidget(QLabel(_("Filtre extension : ")), 3, 0) self.filelinelayout.addWidget(self.filterField, 3, 1) self.filelinelayout.addWidget(self.cancelButton, 3, 2) self.layout.addLayout(self.filelinelayout) # Connexion des différents objets self.connect(self.dirList, SIGNAL("clicked(QModelIndex)"), self.updateFile) self.connect(self.saveButton, SIGNAL("clicked()"), self.accept) self.connect(self.cancelButton, SIGNAL("clicked()"), self.reject) self.connect(self.mkdirButton, SIGNAL("clicked()"), self.mkdir) self.connect(self.dirList, SIGNAL("indexesMoved (const QModelIndexList&)"), self.updateFile) self.connect(self.fileField, SIGNAL("textChanged (const QString&)"), self.activate) self.connect(self.fileField, SIGNAL("returnPressed()"), self.accept) # Taille minimum self.setMinimumSize(700, 480) # Par défaut, on désactive self.deactivate() # Completion des fichiers self.completion = QCompleter(self.dirModel, self.dirList) def updateLatDir(self, item) : """ Fonction permettant de naviguer dans la listes des répertoires """ self.updateDir(self.dirModelLight.filePath(item)) def treeMAJ(self, item) : self.dirTree.resizeColumnToContents(0) def activate(self, filename=None): """ Activation des boutton de sauvegarde """ self.dirList.clearSelection() if filename != "": self.saveButton.setEnabled(True) else: self.saveButton.setEnabled(False) def deactivate(self): """ Désactivation des boutton de sauvegarde """ self.saveButton.setEnabled(False) def updateDir(self, path = None): """ Fonction permettant de naviguer dans la listes des répertoires """ if path : self.currentDir = path self.location.setText("<b>%s</b>" % path) self.dirModel.clear() self.tmpdir = QDir() self.tmpdir.setPath(self.currentDir) self.tmpdir.setNameFilters(QStringList(self.filter)) # Une icône pour les images, un autre icône pour les vidéos, et # une pour audio if self.mode == "image" : icone = QIcon("Icones" + os.sep + "image_image.png") elif self.mode == "video" : icone = QIcon("Icones" + os.sep + "image_video.png") elif self.mode == "audio" : icone = QIcon("Icones" + os.sep + "image_audio.png") else: icone = QIcon("Icones" + os.sep + "image_default.png") for wlfile in self.tmpdir.entryList(QDir.Files): if self.mode == "image" : icone = QIcon(EkdPreview("Icones" + os.sep + "image_default.png").get_preview()) item = QStandardItem(icone, QString(wlfile)) item.setToolTip(wlfile) item.setData(QVariant(self.tmpdir.absolutePath() + os.sep + wlfile), Qt.UserRole + 2) item.setData(QVariant(wlfile), Qt.UserRole + 3) self.dirModel.appendRow(item) def updateFile(self, item): """ Fonction appelée par la listView lors d'un changement de repertoire""" # On récupère le QModel parent du QModelItem path = "%s" % item.data(Qt.UserRole + 2).toString() name = os.path.basename(path) self.fileField.selectAll() self.fileField.setFocus() self.fileField.setText(name) self.activate(name) def selectedFile(self): """ Renvoi le fichier selectionné pour la sauvegarde""" # Récupération du fichier selectionné fichier = self.fileField.text() output = "%s%s%s" % (self.currentDir, os.sep, fichier) info = QFileInfo(output) # Si l'utilisateur n'a pas spécifié de suffix, on l'ajoute if info.suffix() != self.suffix[1:]: output = "%s%s" % (output, self.suffix) return output def mkdir(self): """ Crée un répertoire dans le répertoire courant """ (dirname, ok) = QInputDialog.getText(self, _(u"Nouveau répertoire"), _(u"Nom du répertoire"), QLineEdit.Normal, _(u"Nouveau répertoire")) if ok : try : os.mkdir("%s%s%s" % (self.currentDir, os.sep,dirname)) #print u"Création de : %s%s%s" % (self.currentDir, os.sep, dirname) EkdPrint(u"Création de : %s%s%s" % (self.currentDir, os.sep, dirname)) self.updateDir() self.dirModelLight.refresh() except Exception, e: #print _(u"Impossible de crée un nouveau répertoire : %s" % e) EkdPrint(_(u"Impossible de crée un nouveau répertoire : %s" % e))
class DirectoryWidget(RWidget): def __init__(self, parent, base="."): RWidget.__init__(self, parent) self.base = base self.model = QFileSystemModel() self.model.setRootPath(QDir.rootPath()) self.proxyModel = FileSystemProxyModel() self.proxyModel.setDynamicSortFilter(True) self.proxyModel.setFilterKeyColumn(0) self.proxyModel.setSourceModel(self.model) self.listView = QListView(self) self.listView.setModel(self.proxyModel) index = self.model.index(QDir.currentPath()) self.listView.setRootIndex(self.proxyModel.mapFromSource(index)) self.listView.setContextMenuPolicy(Qt.CustomContextMenu) self.lineEdit = QLineEdit(self) filterLineEdit = QLineEdit() filterLabel = QLabel("Filter:") self.connect(filterLineEdit, SIGNAL("textChanged(QString)"), self.proxyModel.setFilterWildcard) self.actions = [] self.upAction = QAction("&Up", self) self.upAction.setStatusTip("Move to parent directory") self.upAction.setToolTip("Move to parent directory") self.upAction.setIcon(QIcon(":go-up")) self.upAction.setEnabled(True) self.actions.append(self.upAction) self.newAction = QAction("&New Directory", self) self.newAction.setStatusTip("Create new directory") self.newAction.setToolTip("Create new directory") self.newAction.setIcon(QIcon(":folder-new")) self.newAction.setEnabled(True) self.actions.append(self.newAction) self.synchAction = QAction("&Synch", self) self.synchAction.setStatusTip("Synch with current working directory") self.synchAction.setToolTip("Synch with current working directory") self.synchAction.setIcon(QIcon(":view-refresh")) self.synchAction.setEnabled(True) self.actions.append(self.synchAction) self.rmAction = QAction("&Delete", self) self.rmAction.setStatusTip("Delete selected item") self.rmAction.setToolTip("delete selected item") self.rmAction.setIcon(QIcon(":edit-delete")) self.rmAction.setEnabled(True) self.actions.append(self.rmAction) self.openAction = QAction("&Open", self) self.openAction.setStatusTip("Open selected R script") self.openAction.setToolTip("Open selected R script") self.openAction.setIcon(QIcon(":document-open")) self.openAction.setEnabled(True) self.actions.append(self.openAction) self.loadAction = QAction("&Load", self) self.loadAction.setStatusTip("Load selected R data") self.loadAction.setToolTip("Load selected R data") self.loadAction.setIcon(QIcon(":document-open")) self.loadAction.setEnabled(True) self.actions.append(self.loadAction) self.setAction = QAction("Set as ¤t", self) self.setAction.setStatusTip("Set folder as R working directory") self.setAction.setToolTip("Set folder as R working directory") self.setAction.setIcon(QIcon(":folder-home")) self.setAction.setEnabled(True) self.actions.append(self.setAction) self.loadExternal = QAction("Open &Externally", self) self.loadExternal.setStatusTip("Load file in external application") self.loadExternal.setToolTip("Load file in external application") self.loadExternal.setIcon(QIcon(":folder-system")) self.loadExternal.setEnabled(True) self.actions.append(self.loadExternal) self.rootChanged() hiddenAction = QAction("Toggle hidden files", self) hiddenAction.setStatusTip("Show/hide hidden files and folders") hiddenAction.setToolTip("Show/hide hidden files and folders") hiddenAction.setIcon(QIcon(":stock_keyring")) hiddenAction.setCheckable(True) self.connect(self.newAction, SIGNAL("triggered()"), self.newFolder) self.connect(self.upAction, SIGNAL("triggered()"), self.upFolder) self.connect(self.synchAction, SIGNAL("triggered()"), self.synchFolder) self.connect(self.rmAction, SIGNAL("triggered()"), self.rmItem) self.connect(self.openAction, SIGNAL("triggered()"), self.openItem) self.connect(self.loadAction, SIGNAL("triggered()"), self.loadItem) self.connect(self.loadExternal, SIGNAL("triggered()"), self.externalItem) self.connect(self.setAction, SIGNAL("triggered()"), self.setFolder) self.connect(hiddenAction, SIGNAL("toggled(bool)"), self.toggleHidden) self.connect(self.listView, SIGNAL("activated(QModelIndex)"), self.cdFolder) self.connect(self.listView, SIGNAL("customContextMenuRequested(QPoint)"), self.customContext) self.connect(self.lineEdit, SIGNAL("returnPressed()"), self.gotoFolder) upButton = QToolButton() upButton.setDefaultAction(self.upAction) upButton.setAutoRaise(True) newButton = QToolButton() newButton.setDefaultAction(self.newAction) newButton.setAutoRaise(True) synchButton = QToolButton() synchButton.setDefaultAction(self.synchAction) synchButton.setAutoRaise(True) setButton = QToolButton() setButton.setDefaultAction(self.setAction) setButton.setAutoRaise(True) hiddenButton = QToolButton() hiddenButton.setDefaultAction(hiddenAction) hiddenButton.setAutoRaise(True) hbox = QHBoxLayout() hbox.addWidget(upButton) hbox.addWidget(synchButton) hbox.addWidget(newButton) hbox.addWidget(setButton) hbox.addWidget(hiddenButton) vbox = QVBoxLayout(self) vbox.addLayout(hbox) vbox.addWidget(self.lineEdit) vbox.addWidget(self.listView) vbox.addWidget(filterLabel) vbox.addWidget(filterLineEdit) def toggleHidden(self, toggled): base = QDir.AllDirs|QDir.AllEntries|QDir.NoDotAndDotDot if toggled: self.model.setFilter(base|QDir.Hidden) else: self.model.setFilter(base) def gotoFolder(self): text = self.lineEdit.text() self.listView.setRootIndex(self.proxyModel.mapFromSource(self.model.index(text, 0))) def rootChanged(self): index1 = self.listView.rootIndex() index2 = self.proxyModel.mapToSource(index1) self.lineEdit.setText(self.model.filePath(index2)) self.listView.setCurrentIndex(index1) def customContext(self, pos): index = self.listView.indexAt(pos) index = self.proxyModel.mapToSource(index) if not index.isValid(): self.rmAction.setEnabled(False) self.openAction.setEnabled(False) self.loadAction.setEnabled(False) elif not self.model.isDir(index): info = self.model.fileInfo(index) suffix = info.suffix() if suffix in ("Rd","Rdata","RData"): self.loadAction.setEnabled(True) self.openAction.setEnabled(False) self.loadExternal.setEnabled(False) elif suffix in ("txt","csv","R","r"): self.openAction.setEnabled(True) self.loadAction.setEnabled(False) self.loadExternal.setEnabled(True) else: self.loadAction.setEnabled(False) self.openAction.setEnabled(False) self.loadExternal.setEnabled(True) menu = QMenu(self) for action in self.actions: menu.addAction(action) menu.exec_(self.listView.mapToGlobal(pos)) def openItem(self): index = self.listView.currentIndex() index = self.proxyModel.mapToSource(index) self.emit(SIGNAL("openFileRequest(QString)"), self.model.filePath(index)) def loadItem(self): index = self.listView.currentIndex() index = self.proxyModel.mapToSource(index) self.emit(SIGNAL("loadFileRequest(QString)"), self.model.filePath(index)) def externalItem(self): index = self.listView.currentIndex() index = self.proxyModel.mapToSource(index) QDesktopServices.openUrl(QUrl(self.model.filePath(index))) def newFolder(self): text, ok = QInputDialog.getText(self, "New Folder", "Folder name:", QLineEdit.Normal, "new_folder") if ok: index = self.listView.rootIndex() index = self.proxyModel.mapToSource(index) self.model.mkdir(index, text) def setFolder(self): index = self.listView.currentIndex() index = self.proxyModel.mapToSource(index) commands = "setwd('%s')" % self.model.filePath(index) self.emitCommands(commands) def rmItem(self): index = self.listView.currentIndex() if index == self.listView.rootIndex(): return yes = QMessageBox.question(self, "manageR Warning", "Are you sure you want to delete '%s'?" % self.model.fileName(index), QMessageBox.Yes|QMessageBox.Cancel) if not yes == QMessageBox.Yes: return index = self.proxyModel.mapToSource(index) if self.model.isDir(index): result = self.model.rmdir(index) else: result = self.model.remove(index) if not result: QMessageBox.warning(self, "manageR Error", "Unable to delete %s!" % self.model.fileName(index)) def upFolder(self): index = self.listView.rootIndex() index = self.proxyModel.parent(index) self.listView.setRootIndex(index) self.rootChanged() def cdFolder(self): indexes = self.listView.selectedIndexes() if len(indexes) < 1: return index = indexes[0] if self.model.isDir(self.proxyModel.mapToSource(index)): self.listView.setRootIndex(index) self.rootChanged() self.listView.clearSelection() def synchFolder(self): text = robjects.r.getwd()[0] index = self.model.index(text, 0) index = self.proxyModel.mapFromSource(index) self.listView.setRootIndex(index) self.rootChanged()