class Main(plugin.Plugin): " Main Class " def initialize(self, *args, **kwargs): " Init Main Class " super(Main, self).initialize(*args, **kwargs) if linux_distribution(): self.menu = QMenu('Open with') self.menu.aboutToShow.connect(self.build_submenu) self.ex_locator = self.locator.get_service('explorer') self.ex_locator.add_project_menu(self.menu, lang='all') def build_submenu(self): ''' build sub menu on the fly based on file path ''' self.menu.clear() if self.ex_locator.get_current_project_item().isFolder is not True: filenam = self.ex_locator.get_current_project_item().get_full_path() entry = xdg_query('default', guess_type(filenam, strict=False)[0]) if entry: app = entry.replace('.desktop', '') self.menu.addActions([ QAction(QIcon.fromTheme(app), app, self, triggered=lambda: Popen([app, filenam])), QAction(QIcon.fromTheme('folder-open'), 'File Manager', self, triggered=lambda: Popen(['xdg-open', path.dirname(filenam)]))]) self.menu.show()
class FlowWidget(QGraphicsView): def __init__(self, parent=None): QGraphicsView.__init__(self, parent) self.setAcceptDrops(True) self.setScene(FlowScene(self)) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) self.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.autoConnecter = ItemAutoConnecter(self.scene()) self.setupMenu() #self.scrollControl = ScrollControl( self ) #self.scrollControl.setScrollingEnabled( False ) def setupMenu(self): self.setContextMenuPolicy(Qt.CustomContextMenu) self.menu = QMenu(self) self.connect(self, SIGNAL('customContextMenuRequested( const QPoint& )'), self.slotContextMenu) def slotContextMenu(self, pos): self.menu.clear() item = self.itemAt(pos) if isinstance(item, FlowSceneGraphEdge): self.menu.addAction(item.delete_action) self.menu.addAction(self.autoConnecter.action()) self.autoConnecter.updateAction() self.menu.exec_(self.mapToGlobal(pos)) # Zoom by scroll event # def wheelEvent( self, event ): # factor = 1.2 # if event.delta() < 0: # factor = 1.0 / factor # self.scale( factor, factor ) # #self.updateZoomValue() # return QGraphicsView.wheelEvent( self, event ) def dragEnterEvent(self, event): if isinstance(event.source(), FlowItemTreeWidget): event.acceptProposedAction() def dragMoveEvent(self, event): pass def dropEvent(self, event): type_ = event.source().selectedIndexes()[0].data( Qt.UserRole).toPyObject() item = type_() item.setParent(self) item.setPos(self.mapToScene(event.pos())) self.scene().addItem(item)
class Main(plugin.Plugin): " Main Class " def initialize(self, *args, **kwargs): " Init Main Class " super(Main, self).initialize(*args, **kwargs) self.menu = QMenu('Convert Image') self.menu.aboutToShow.connect(self.build_submenu) self.ex_locator = self.locator.get_service('explorer') self.ex_locator.add_project_menu(self.menu, lang='all') def build_submenu(self): ''' build sub menu on the fly based on file path ''' self.menu.clear() if self.ex_locator.get_current_project_item().isFolder is not True: filenam = self.ex_locator.get_current_project_item().get_full_path() # pillow.readthedocs.org/en/latest/handbook/image-file-formats.html exten = ('bmp', 'cur', 'gbr', 'gif', 'ico', 'jfif', 'jpeg', 'pbm', 'pcx', 'pgm', 'png', 'ppm', 'psd', 'tga', 'tiff', 'xbm', 'xpm') conditional = path.splitext(filenam)[1][1:].strip().lower() in exten if conditional: self.menu.addActions([ QAction('{} to WEBP'.format(path.basename(filenam)[:50]), self, triggered=lambda: self.convert_img('WEBP')), QAction('{} to JPG'.format(path.basename(filenam)[:50]), self, triggered=lambda: self.convert_img('JPEG')), QAction('{} to PNG'.format(path.basename(filenam)[:50]), self, triggered=lambda: self.convert_img('PNG')), ]) self.menu.show() def convert_img(self, fmt): """ convert image to desired format """ filenam = self.ex_locator.get_current_project_item().get_full_path() im = Image.open(filenam).convert("RGB") im.save(path.splitext(filenam)[0] + ".{}".format(fmt.lower()), fmt)
class rot13box(QWidget): def __init__(self, parent): super(rot13box, self).__init__(parent) self.entry = None self.but = None self.buffer = None self.entry = QLineEdit(self) self.but = QPushButton("ROT13", self) if self.buffer is not None: self.encodeText(self.buffer) layout = QHBoxLayout(self) grabButton = QToolButton(parent) grabButton.setText("Msg ") # grabButton.setMinimumHeight(self.but.sizeHint().height()) self.msgMenu = QMenu(grabButton) self.msgMenu.aboutToShow.connect(self.updateMsgMenu) grabButton.setMenu(self.msgMenu) grabButton.setPopupMode(QToolButton.InstantPopup) # grabButton.clicked.connect(self.grabMessage) layout.addWidget(grabButton) layout.addWidget(self.entry) layout.addWidget(self.but) self.but.clicked.connect(self.enc) self.setMaximumHeight(self.sizeHint().height()) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) @loggingSlot() def updateMsgMenu(self): self.msgMenu.clear() messages = get_server().get_messages() with messages: for i in xrange(len(messages) - 1, len(messages) - 1 - min(10, len(messages)), -1): message = messages[i] self.msgMenu.addAction(message[2], partial(self.encodeText, message[2])) def grabMessage(self): self.encodeText(get_server().get_messages().getLatest()[2]) def encodeText(self, text): if self.entry is not None: self.entry.setText(text) self.enc() else: self.buffer = text @loggingSlot() def enc(self): rot13 = string.maketrans( u"ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", u"NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm", ) plain = self.entry.text() if plain: self.entry.setText(string.translate(str(plain.toUtf8()), rot13))
class FlowWidget( QGraphicsView ): def __init__( self, parent = None ): QGraphicsView.__init__( self, parent ) self.setAcceptDrops( True ) self.setScene( FlowScene( self ) ) self.setRenderHints( QPainter.Antialiasing | QPainter.SmoothPixmapTransform ) self.setAlignment( Qt.AlignLeft | Qt.AlignTop ) self.autoConnecter = ItemAutoConnecter( self.scene() ) self.setupMenu() #self.scrollControl = ScrollControl( self ) #self.scrollControl.setScrollingEnabled( False ) def setupMenu( self ): self.setContextMenuPolicy( Qt.CustomContextMenu ) self.menu = QMenu( self ) self.connect( self, SIGNAL( 'customContextMenuRequested( const QPoint& )' ), self.slotContextMenu ) def slotContextMenu( self, pos ): self.menu.clear() item = self.itemAt( pos ) if isinstance( item, FlowSceneGraphEdge ): self.menu.addAction( item.delete_action ) self.menu.addAction( self.autoConnecter.action() ) self.autoConnecter.updateAction() self.menu.exec_( self.mapToGlobal( pos ) ) # Zoom by scroll event # def wheelEvent( self, event ): # factor = 1.2 # if event.delta() < 0: # factor = 1.0 / factor # self.scale( factor, factor ) # #self.updateZoomValue() # return QGraphicsView.wheelEvent( self, event ) def dragEnterEvent( self, event ): if isinstance( event.source(), FlowItemTreeWidget ): event.acceptProposedAction() def dragMoveEvent( self, event ): pass def dropEvent( self, event ): type_ = event.source().selectedIndexes()[0].data( Qt.UserRole ).toPyObject() item = type_() item.setParent( self ) item.setPos( self.mapToScene( event.pos() ) ) self.scene().addItem( item )
def loadSwitcher(self): lmenu = QMenu() lmenu.clear() def loadAct(l): lmenu.addAction(l, lambda: self.langChangeRequest(l)) for x in self.kbList: loadAct(x) lmenu.addSeparator() lmenu.addAction("Configure", lambda: self.nxkbConfig.show()) lmenu.addAction("About", self.showAbout) lmenu.addAction("Quit", lambda: sys.exit(0)) self.setContextMenu(lmenu) self.setIcon(QIcon(self.kbMap[self.currentOpt]))
class OneColumnTree(QTreeWidget): def __init__(self, parent): QTreeWidget.__init__(self, parent) self.setItemsExpandable(True) self.setColumnCount(1) self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*,int)'), self.activated) # Setup context menu self.menu = QMenu(self) self.common_actions = self.setup_common_actions() def activated(self): raise NotImplementedError def set_title(self, title): self.setHeaderLabels([title]) def setup_common_actions(self): """Setup context menu common actions""" collapse_act = create_action(self, text=self.tr('Collapse all'), icon=get_icon('collapse.png'), triggered=self.collapseAll) expand_act = create_action(self, text=self.tr('Expand all'), icon=get_icon('expand.png'), triggered=self.expandAll) return [collapse_act, expand_act] def update_menu(self): self.menu.clear() actions = self.specific_actions() if actions: actions.append(None) actions += self.common_actions add_actions(self.menu, actions) def specific_actions(self): # Right here: add other actions if necessary # (reimplement this method) return [] def contextMenuEvent(self, event): """Override Qt method""" self.update_menu() self.menu.popup(event.globalPos())
class Main(plugin.Plugin): " Main Class " def initialize(self, *args, **kwargs): " Init Main Class " super(Main, self).initialize(*args, **kwargs) if linux_distribution(): self.menu = QMenu('Open with') self.menu.aboutToShow.connect(self.build_submenu) self.ex_locator = self.locator.get_service('explorer') self.ex_locator.add_project_menu(self.menu, lang='all') def build_submenu(self): ''' build sub menu on the fly based on file path ''' self.menu.clear() if self.ex_locator.get_current_project_item().isFolder is not True: filenam = self.ex_locator.get_current_project_item().get_full_path() entry = xdg_query('default', guess_type(filenam, strict=False)[0]) if entry: app = entry.replace('.desktop', '') actions = [ QAction(QIcon.fromTheme(app), app, self, triggered=lambda: Popen([app, filenam])), QAction(QIcon.fromTheme('folder-open'), 'File Manager', self, triggered=lambda: Popen(['xdg-open', path.dirname(filenam)]))] #QAction(QIcon.fromTheme('Sublime Text 2'), 'Sublime', # self, triggered=lambda: Popen(['sublime-text', filenam])), #QAction(QIcon.fromTheme('Sqliteman'), 'Sqliteman', # self, triggered=lambda: Popen(['sqliteman', filenam])), #QAction(QIcon.fromTheme('Google Chrome'), 'Google Chrome', # self, triggered=lambda: Popen(['google-chrome', filenam])) for key in usersettings.commands.keys(): action = QAction(QIcon.fromTheme(key), key, self, triggered=lambda: Popen([usersettings.commands[key], filenam])) actions.append(action) self.menu.addActions(actions) self.menu.show()
class NotificacionActualizacion(QSystemTrayIcon): """ System Tray Icon """ def __init__(self, parent=None): QSystemTrayIcon.__init__(self, parent) self.setIcon(QIcon(":image/edis")) self.menu = QMenu() self.setContextMenu(self.menu) exit_action = self.menu.addAction(self.tr("Close")) self.thread = Thread() # Conexiones self.connect(self.thread, SIGNAL("updateVersion(QString, QString, bool)"), self._show_tray) self.connect(exit_action, SIGNAL("triggered()"), self.hide) self.setToolTip(self.tr("Buscando actualizaciones...")) self.thread.start() def _show_tray(self, version, link, found): """ Muestra el system tray icon """ if found: self.menu.clear() self.setToolTip("") download_action = self.menu.addAction(self.tr("Descargar!")) exit_action = self.menu.addAction(self.tr("Cerrar notificación")) self.connect(download_action, SIGNAL("triggered()"), lambda: webbrowser.open_new(link)) self.connect(exit_action, SIGNAL("triggered()"), self.hide) self.showMessage(self.tr("Nueva versión disponible!"), self.tr("Está disponible una nueva versión de" " Edis\n" "versión: {0}.").format(version), QSystemTrayIcon.Information, 10000) else: self.hide()
class NotificacionActualizacion(QSystemTrayIcon): """ System Tray Icon """ def __init__(self, parent=None): QSystemTrayIcon.__init__(self, parent) self.setIcon(QIcon(":image/edis")) self.menu = QMenu() self.setContextMenu(self.menu) exit_action = self.menu.addAction(self.tr("Close")) self.thread = Thread() # Conexiones self.connect(self.thread, SIGNAL("updateVersion(QString, QString, bool)"), self._show_tray) self.connect(exit_action, SIGNAL("triggered()"), self.hide) self.setToolTip(self.tr("Buscando actualizaciones...")) self.thread.start() def _show_tray(self, version, link, found): """ Muestra el system tray icon """ if found: self.menu.clear() self.setToolTip("") download_action = self.menu.addAction(self.tr("Descargar!")) exit_action = self.menu.addAction(self.tr("Cerrar notificación")) self.connect(download_action, SIGNAL("triggered()"), lambda: webbrowser.open_new(link)) self.connect(exit_action, SIGNAL("triggered()"), self.hide) self.showMessage( self.tr("Nueva versión disponible!"), self.tr("Está disponible una nueva versión de" " Edis\n" "versión: {0}.").format(version), QSystemTrayIcon.Information, 10000) else: self.hide()
class Main(plugin.Plugin): " Main Class " def initialize(self, *args, **kwargs): " Init Main Class " super(Main, self).initialize(*args, **kwargs) self.menu1 = QMenu('Compress') self.menu1.aboutToShow.connect(self.build_submenu) self.ex_locator = self.locator.get_service('explorer') self.ex_locator.add_project_menu(self.menu1, lang='all') if not version_info[0] < 3: self.menu2 = QMenu('Extract') self.menu2.aboutToShow.connect(self.build_submenu) self.ex_locator.add_project_menu(self.menu2, lang='all') def build_submenu(self): ''' build sub menu on the fly based on file path ''' self.menu1.clear() if self.ex_locator.get_current_project_item().isFolder is True: folder = self.ex_locator.get_current_project_item().get_full_path() self.menu1.addActions([ QAction('To ZIP', self, triggered=lambda: make_archive(folder, 'zip', folder)), QAction('To TAR', self, triggered=lambda: make_archive(folder, 'tar', folder)), QAction('To BZ2', self, triggered=lambda: make_archive(folder, 'bztar', folder))]) self.menu1.show() elif not version_info[0] < 3: self.menu2.clear() filenam = self.ex_locator.get_current_project_item().get_full_path() exten = ('zip', 'tar', 'bz2', 'bztar', 'gztar') conditional = path.splitext(filenam)[1][1:].strip().lower() in exten if conditional: self.menu2.addAction(QAction('From {}'.format( path.splitext(filenam)[1].upper()), self, triggered=lambda: unpack_archive(filenam, path.dirname(filenam)))) self.menu2.show()
class DirView(QTreeView): """Base file/directory tree view""" def __init__(self, parent=None): super(DirView, self).__init__(parent) self.name_filters = None self.parent_widget = parent self.valid_types = None self.show_all = None self.menu = None self.common_actions = None self.__expanded_state = None self._to_be_loaded = None self.fsmodel = None self.setup_fs_model() self._scrollbar_positions = None #---- Model def setup_fs_model(self): """Setup filesystem model""" filters = QDir.AllDirs | QDir.Files | QDir.Drives | QDir.NoDotAndDotDot self.fsmodel = QFileSystemModel(self) self.fsmodel.setFilter(filters) self.fsmodel.setNameFilterDisables(False) def install_model(self): """Install filesystem model""" self.setModel(self.fsmodel) def setup_view(self): """Setup view""" self.install_model() self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'), lambda: self.resizeColumnToContents(0)) self.setAnimated(False) self.setSortingEnabled(True) self.sortByColumn(0, Qt.AscendingOrder) def set_name_filters(self, name_filters): """Set name filters""" self.name_filters = name_filters self.fsmodel.setNameFilters(name_filters) def set_show_all(self, state): """Toggle 'show all files' state""" if state: self.fsmodel.setNameFilters([]) else: self.fsmodel.setNameFilters(self.name_filters) def get_filename(self, index): """Return filename associated with *index*""" if index: return osp.normpath(unicode(self.fsmodel.filePath(index))) def get_index(self, filename): """Return index associated with filename""" return self.fsmodel.index(filename) def get_selected_filenames(self): """Return selected filenames""" if self.selectionMode() == self.ExtendedSelection: return [self.get_filename(idx) for idx in self.selectedIndexes()] else: return [self.get_filename(self.currentIndex())] def get_dirname(self, index): """Return dirname associated with *index*""" fname = self.get_filename(index) if fname: if osp.isdir(fname): return fname else: return osp.dirname(fname) #---- Tree view widget def setup(self, name_filters=['*.py', '*.pyw'], valid_types=('.py', '.pyw'), show_all=False): """Setup tree widget""" self.setup_view() self.set_name_filters(name_filters) self.valid_types = valid_types self.show_all = show_all # Setup context menu self.menu = QMenu(self) self.common_actions = self.setup_common_actions() #---- Context menu def setup_common_actions(self): """Setup context menu common actions""" # Filters filters_action = create_action(self, _("Edit filename filters..."), None, get_icon('filter.png'), triggered=self.edit_filter) # Show all files all_action = create_action(self, _("Show all files"), toggled=self.toggle_all) all_action.setChecked(self.show_all) self.toggle_all(self.show_all) return [filters_action, all_action] def edit_filter(self): """Edit name filters""" filters, valid = QInputDialog.getText(self, _('Edit filename filters'), _('Name filters:'), QLineEdit.Normal, ", ".join(self.name_filters)) if valid: filters = [f.strip() for f in unicode(filters).split(',')] self.parent_widget.sig_option_changed.emit('name_filters', filters) self.set_name_filters(filters) def toggle_all(self, checked): """Toggle all files mode""" self.parent_widget.sig_option_changed.emit('show_all', checked) self.show_all = checked self.set_show_all(checked) def create_file_new_actions(self, fnames): """Return actions for submenu 'New...'""" if not fnames: return [] new_file_act = create_action( self, _("File..."), icon='filenew.png', triggered=lambda: self.new_file(fnames[-1])) new_module_act = create_action( self, _("Module..."), icon='py.png', triggered=lambda: self.new_module(fnames[-1])) new_folder_act = create_action( self, _("Folder..."), icon='folder_new.png', triggered=lambda: self.new_folder(fnames[-1])) new_package_act = create_action( self, _("Package..."), icon=get_icon('package_collapsed.png'), triggered=lambda: self.new_package(fnames[-1])) return [ new_file_act, new_folder_act, None, new_module_act, new_package_act ] def create_file_import_actions(self, fnames): """Return actions for submenu 'Import...'""" return [] def create_file_manage_actions(self, fnames): """Return file management actions""" only_files = all([osp.isfile(_fn) for _fn in fnames]) only_modules = all([ osp.splitext(_fn)[1] in ('.py', '.pyw', '.ipy') for _fn in fnames ]) only_valid = all( [osp.splitext(_fn)[1] in self.valid_types for _fn in fnames]) run_action = create_action(self, _("Run"), icon="run_small.png", triggered=self.run) edit_action = create_action(self, _("Edit"), icon="edit.png", triggered=self.clicked) move_action = create_action(self, _("Move..."), icon="move.png", triggered=self.move) delete_action = create_action(self, _("Delete..."), icon="delete.png", triggered=self.delete) rename_action = create_action(self, _("Rename..."), icon="rename.png", triggered=self.rename) open_action = create_action(self, _("Open"), triggered=self.open) actions = [] if only_modules: actions.append(run_action) if only_valid and only_files: actions.append(edit_action) else: actions.append(open_action) actions += [delete_action, rename_action] basedir = fixpath(osp.dirname(fnames[0])) if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]): actions.append(move_action) actions += [None] # VCS support is quite limited for now, so we are enabling the VCS # related actions only when a single file/folder is selected: dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0]) if len(fnames) == 1 and vcs.is_vcs_repository(dirname): vcs_ci = create_action(self, _("Commit"), icon="vcs_commit.png", triggered=lambda fnames=[dirname]: self. vcs_command(fnames, tool='commit')) vcs_log = create_action(self, _("Browse repository"), icon="vcs_browse.png", triggered=lambda fnames=[dirname]: self. vcs_command(fnames, tool='browse')) actions += [None, vcs_ci, vcs_log] return actions def create_folder_manage_actions(self, fnames): """Return folder management actions""" actions = [] if os.name == 'nt': _title = _("Open command prompt here") else: _title = _("Open terminal here") action = create_action( self, _title, icon="cmdprompt.png", triggered=lambda fnames=fnames: self.open_terminal(fnames)) actions.append(action) _title = _("Open Python interpreter here") action = create_action( self, _title, icon="python.png", triggered=lambda fnames=fnames: self.open_interpreter(fnames)) actions.append(action) return actions def create_context_menu_actions(self): """Create context menu actions""" actions = [] fnames = self.get_selected_filenames() new_actions = self.create_file_new_actions(fnames) if len(new_actions) > 1: # Creating a submenu only if there is more than one entry new_act_menu = QMenu(_('New'), self) add_actions(new_act_menu, new_actions) actions.append(new_act_menu) else: actions += new_actions import_actions = self.create_file_import_actions(fnames) if len(import_actions) > 1: # Creating a submenu only if there is more than one entry import_act_menu = QMenu(_('Import'), self) add_actions(import_act_menu, import_actions) actions.append(import_act_menu) else: actions += import_actions if actions: actions.append(None) if fnames: actions += self.create_file_manage_actions(fnames) if actions: actions.append(None) if fnames and all([osp.isdir(_fn) for _fn in fnames]): actions += self.create_folder_manage_actions(fnames) if actions: actions.append(None) actions += self.common_actions return actions def update_menu(self): """Update context menu""" self.menu.clear() add_actions(self.menu, self.create_context_menu_actions()) #---- Events def viewportEvent(self, event): """Reimplement Qt method""" # Prevent Qt from crashing or showing warnings like: # "QSortFilterProxyModel: index from wrong model passed to # mapFromSource", probably due to the fact that the file system model # is being built. See Issue 1250. # # This workaround was inspired by the following KDE bug: # https://bugs.kde.org/show_bug.cgi?id=172198 # # Apparently, this is a bug from Qt itself. self.executeDelayedItemsLayout() return QTreeView.viewportEvent(self, event) def contextMenuEvent(self, event): """Override Qt method""" self.update_menu() self.menu.popup(event.globalPos()) def keyPressEvent(self, event): """Reimplement Qt method""" if event.key() in (Qt.Key_Enter, Qt.Key_Return): self.clicked() elif event.key() == Qt.Key_F2: self.rename() elif event.key() == Qt.Key_Delete: self.delete() else: QTreeView.keyPressEvent(self, event) def mouseDoubleClickEvent(self, event): """Reimplement Qt method""" QTreeView.mouseDoubleClickEvent(self, event) self.clicked() def clicked(self): """Selected item was double-clicked or enter/return was pressed""" fnames = self.get_selected_filenames() for fname in fnames: if osp.isdir(fname): self.directory_clicked(fname) else: self.open([fname]) def directory_clicked(self, dirname): """Directory was just clicked""" pass #---- Drag def dragEnterEvent(self, event): """Drag and Drop - Enter event""" event.setAccepted(event.mimeData().hasFormat("text/plain")) def dragMoveEvent(self, event): """Drag and Drop - Move event""" if (event.mimeData().hasFormat("text/plain")): event.setDropAction(Qt.MoveAction) event.accept() else: event.ignore() def startDrag(self, dropActions): """Reimplement Qt Method - handle drag event""" data = QMimeData() data.setUrls([QUrl(fname) for fname in self.get_selected_filenames()]) drag = QDrag(self) drag.setMimeData(data) drag.exec_() #---- File/Directory actions def open(self, fnames=None): """Open files with the appropriate application""" if fnames is None: fnames = self.get_selected_filenames() for fname in fnames: ext = osp.splitext(fname)[1] if osp.isfile(fname) and ext in self.valid_types: self.parent_widget.sig_open_file.emit(fname) else: self.open_outside_spyder([fname]) def open_outside_spyder(self, fnames): """Open file outside Spyder with the appropriate application If this does not work, opening unknown file in Spyder, as text file""" for path in sorted(fnames): path = file_uri(path) ok = programs.start_file(path) if not ok: self.parent_widget.emit(SIGNAL("edit(QString)"), path) def open_terminal(self, fnames): """Open terminal""" for path in sorted(fnames): self.parent_widget.emit(SIGNAL("open_terminal(QString)"), path) def open_interpreter(self, fnames): """Open interpreter""" for path in sorted(fnames): self.parent_widget.emit(SIGNAL("open_interpreter(QString)"), path) def run(self, fnames=None): """Run Python scripts""" if fnames is None: fnames = self.get_selected_filenames() for fname in fnames: self.parent_widget.emit(SIGNAL("run(QString)"), fname) def remove_tree(self, dirname): """Remove whole directory tree Reimplemented in project explorer widget""" shutil.rmtree(dirname, onerror=misc.onerror) def delete_file(self, fname, multiple, yes_to_all): """Delete file""" if multiple: buttons = QMessageBox.Yes|QMessageBox.YesAll| \ QMessageBox.No|QMessageBox.Cancel else: buttons = QMessageBox.Yes | QMessageBox.No if yes_to_all is None: answer = QMessageBox.warning( self, _("Delete"), _("Do you really want " "to delete <b>%s</b>?") % osp.basename(fname), buttons) if answer == QMessageBox.No: return yes_to_all elif answer == QMessageBox.Cancel: return False elif answer == QMessageBox.YesAll: yes_to_all = True try: if osp.isfile(fname): misc.remove_file(fname) self.parent_widget.emit(SIGNAL("removed(QString)"), fname) else: self.remove_tree(fname) self.parent_widget.emit(SIGNAL("removed_tree(QString)"), fname) return yes_to_all except EnvironmentError, error: action_str = _('delete') QMessageBox.critical( self, _("Project Explorer"), _("<b>Unable to %s <i>%s</i></b>" "<br><br>Error message:<br>%s") % (action_str, fname, unicode(error))) return False
class MainWindow(QMainWindow): groups = dict() typeQListWidgetHeader = 1000 showHostsInGroups = False currentGroupName = None # used to simple detect currently selected group to show menu def __init__(self): super(MainWindow, self).__init__() self.config = Config() self.db = Database(self.config.getConnectionString()) cryptoKey = self.getCryptoKey() self.hosts = Hosts(self.db, cryptoKey) # menu used for each host self.hostMenu = QMenu() self.editAction = QAction(QIcon(':/ico/edit.svg'), "Edit", self.hostMenu) self.editAction.triggered.connect(self.editHost) self.hostMenu.addAction(self.editAction) # menu used for headers of groups self.groupsHeaderMenu = QMenu() self.editGroupAction = QAction(QIcon(':/ico/edit.svg'), "Edit group", self.groupsHeaderMenu) self.editGroupAction.triggered.connect(self.editGroup) self.deleteGroupAction = QAction(QIcon(':/ico/remove.svg'), "Delete group", self.groupsHeaderMenu) self.deleteGroupAction.triggered.connect(self.deleteGroup) self.groupsHeaderMenu.addAction(self.editGroupAction) self.groupsHeaderMenu.addAction(self.deleteGroupAction) self.duplicateAction = QAction(QIcon(':/ico/copy.svg'), "Duplicate", self.hostMenu) self.duplicateAction.triggered.connect(self.duplicateHost) self.hostMenu.addAction(self.duplicateAction) # todo: confirm for delete action self.deleteAction = QAction(QIcon(':/ico/remove.svg'), "Delete", self.hostMenu) self.deleteAction.triggered.connect(self.deleteHost) self.hostMenu.addAction(self.deleteAction) self.connectFramelessMenu = actions.generateScreenChoseMenu(self.hostMenu, self.connectFrameless, ':/ico/frameless.svg', "Connect frameless") self.hostMenu.addMenu(self.connectFramelessMenu) self.assignGroupAction = QAction("Assign group", self.hostMenu) self.assignGroupAction.triggered.connect(self.assignGroup) self.hostMenu.addAction(self.assignGroupAction) # setup main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # when top level changed, we changing dock title bar self.dockWidgetTileBar = DockWidgetTitleBar() self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) self.ui.hostsDock.topLevelChanged.connect(self.dockLevelChanged) # set global menu self.globalMenu = QMenu() self.globalMenu.addAction(QIcon(':/ico/add.svg'), 'Add host', self.addHost) # groups menu self.groupsMenu = QMenu("Groups") self.groupsMenu.aboutToShow.connect(self.setGroupsMenu) self.globalMenu.addMenu(self.groupsMenu) # disable menu indicator self.ui.menu.setStyleSheet("QPushButton::menu-indicator {image: none;}") self.positionMenu = QMenu("Dock position") self.positionMenu.addAction("Left", lambda: self.setDockPosition(Qt.LeftDockWidgetArea)) self.positionMenu.addAction("Right", lambda: self.setDockPosition(Qt.RightDockWidgetArea)) self.positionMenu.addAction("Float", self.setDockFloat) self.globalMenu.addMenu(self.positionMenu) self.globalMenu.addAction('Change tray icon visibility', self.changeTrayIconVisibility) self.globalMenu.addAction('Settings', self.showSettings) self.globalMenu.addAction('Quit', self.close) self.ui.menu.setMenu(self.globalMenu) # set events on hosts list self.ui.hostsList.itemDoubleClicked.connect(self.slotConnectHost) self.ui.hostsList.itemClicked.connect(self.slotShowHost) self.ui.hostsList.customContextMenuRequested.connect(self.slotShowHostContextMenu) # set tab widget self.tabWidget = MyTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.tabWidget.customContextMenuRequested.connect(self.showCentralWidgetContextMenu) # set tray icon self.tray = QSystemTrayIcon(QIcon(":/ico/myrdp.svg")) self.tray.activated.connect(self.trayActivated) self.trayMenu = QMenu() self.trayMenu.addAction("Hide tray icon", self.changeTrayIconVisibility) self.connectHostMenuTray = ConnectHostMenu(self.hosts) self.connectHostMenuTray.triggered.connect(self.connectHostFromTrayMenu) self.trayMenu.addMenu(self.connectHostMenuTray) self.trayMenu.addAction("Quit", self.close) self.tray.setContextMenu(self.trayMenu) self.restoreSettings() # host list self.ui.filter.textChanged.connect(self.setHostList) self.setHostList() def getCryptoKey(self, passphrase=None): try: return self.config.getPrivateKey(passphrase) except ValueError: passwordDialog = PasswordDialog() retCode = passwordDialog.exec_() if retCode == QtGui.QDialog.Accepted: return self.getCryptoKey(passwordDialog.getPassword()) else: raise SystemError("Password required") def showSettings(self): settingsWidget = self.findChild(QWidget, "settings") if settingsWidget is None: self.settingsWidget = SettingsPage() self.settingsWidget.setObjectName("settings") self.tabWidget.insertTab(0, self.settingsWidget, QIcon(":/ico/settings.svg"), 'Settings') index = self.tabWidget.indexOf(self.settingsWidget) self.tabWidget.setCurrentIndex(index) def connectHostFromMenu(self, action): self.connectHost(unicode(action.text())) def connectHostFromTrayMenu(self, action): tabPage = self.connectHost(unicode(action.text())) if not self.isVisible(): self.tabWidget.setDetached(True, tabPage) def trayActivated(self, reason): if reason != QSystemTrayIcon.Trigger: return if self.isVisible(): self.hide() else: self.show() self.activateWindow() def changeTrayIconVisibility(self): if self.tray.isVisible(): self.tray.hide() if not self.isVisible(): self.show() else: self.tray.show() def refreshGroups(self): groupList = self.hosts.getGroupsList() for group in groupList: if group not in self.groups: # add new groups as visible self.groups[group] = True # remove not existing groups keysToDelete = set(self.groups.keys()) - set(groupList) for key in keysToDelete: self.groups.pop(key) def assignGroup(self): groups = self.hosts.getGroupsList() assignGroupDialog = AssignGroupDialog(groups) groupToAssign = assignGroupDialog.assign() if groupToAssign is not False: # None could be used to unassign the group groupToAssign = None if groupToAssign.isEmpty() else unicode(groupToAssign) for hostName in self.getSelectedHosts(): self.hosts.assignGroup(hostName, groupToAssign) self.db.tryCommit() self.setHostList() def setGroupsMenu(self): self.groupsMenu.clear() addGroupAction = self.groupsMenu.addAction('Add group') addGroupAction.triggered.connect(self.addGroup) deleteGroupAction = self.groupsMenu.addAction('Delete group') deleteGroupAction.triggered.connect(self.showDeleteGroupDialog) showHostsInGroupsAction = self.groupsMenu.addAction('Show host list in groups') showHostsInGroupsAction.triggered.connect(self.changeHostListView) showHostsInGroupsAction.setCheckable(True) showHostsInGroupsAction.setChecked(self.showHostsInGroups) self.groupsMenu.addSeparator() for group, checked in self.groups.items(): action = QAction(group, self.groupsMenu) action.setCheckable(True) action.setChecked(checked) action.triggered.connect(self.groupsVisibilityChanged) self.groupsMenu.addAction(action) def addGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.add() self._processHostSubmit(resp) def groupsVisibilityChanged(self, checked): currentGroup = unicode(self.sender().text()) self.groups[currentGroup] = checked self.setHostList() def setDockPosition(self, dockWidgetArea): if self.ui.hostsDock.isFloating(): self.ui.hostsDock.setFloating(False) self.addDockWidget(dockWidgetArea, self.ui.hostsDock) def setDockFloat(self): if self.ui.hostsDock.isFloating(): return # default title bar must be set before is float because sometimes window make strange crash self.ui.hostsDock.setTitleBarWidget(None) self.ui.hostsDock.setFloating(True) def dockLevelChanged(self, isFloating): if isFloating: # changing title bar widget if is not none, probably true will be only once on start with saved float state if self.ui.hostsDock.titleBarWidget(): self.ui.hostsDock.setTitleBarWidget(None) else: self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) def showFramelessWidget(self): self.t.show() self.t.setGeometry(self.frameGeometry()) def getCurrentHostListItemName(self): return self.ui.hostsList.currentItem().text() def getSelectedHosts(self): return [host.text() for host in self.ui.hostsList.selectedItems()] def findHostItemByName(self, name): result = self.ui.hostsList.findItems(name, Qt.MatchExactly) resultLen = len(result) if resultLen != 1: # should be only one host logger.error("Host not found. Got %d results" % resultLen) return result[0] def showCentralWidgetContextMenu(self, pos): menu = QMenu() title = self.ui.hostsDock.windowTitle() hostsDockAction = menu.addAction(title) hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.ui.hostsDock.isVisible()) hostsDockAction.triggered.connect(self.changeHostsDockWidgetVisibility) hostsDockAction = menu.addAction("Tray icon") hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.tray.isVisible()) hostsDockAction.triggered.connect(self.changeTrayIconVisibility) connectHostMenuTray = ConnectHostMenu(self.hosts, "Connect") connectHostMenuTray.triggered.connect(self.connectHostFromMenu) menu.addMenu(connectHostMenuTray) menu.exec_(self.tabWidget.mapToGlobal(pos)) def changeHostListView(self, checked): self.showHostsInGroups = checked self.setHostList() def changeHostsDockWidgetVisibility(self): isVisible = self.ui.hostsDock.isVisible() self.ui.hostsDock.setVisible(not isVisible) def isHostListHeader(self, item): if not item or item.type() == self.typeQListWidgetHeader: return True return False def slotShowHostContextMenu(self, pos): def changeMenusVisibility(isEnabled): self.connectFramelessMenu.setEnabled(isEnabled) self.editAction.setEnabled(isEnabled) self.duplicateAction.setEnabled(isEnabled) # ignore context menu for group headers item = self.ui.hostsList.itemAt(pos) if self.isHostListHeader(item): item = self.ui.hostsList.itemAt(pos) widgetItem = self.ui.hostsList.itemWidget(item) if widgetItem: self.currentGroupName = widgetItem.text() # yea I'm so dirty if self.currentGroupName != unassignedGroupName: self.groupsHeaderMenu.exec_(self.ui.hostsList.mapToGlobal(pos)) return if len(self.ui.hostsList.selectedItems()) == 1: # single menu changeMenusVisibility(True) else: changeMenusVisibility(False) self.hostMenu.exec_(self.ui.hostsList.mapToGlobal(pos)) def _processHostSubmit(self, resp): if resp["code"]: self.setHostList() hostName = resp.get("name") if hostName: hostItem = self.findHostItemByName(hostName) self.slotConnectHost(hostItem) def addHost(self): hostDialog = HostConfigDialog(self.hosts) self._processHostSubmit(hostDialog.add()) def editHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.edit(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def editGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.edit(self.currentGroupName) self._processHostSubmit(resp) def deleteGroup(self): retCode = self.showOkCancelMessageBox("Do you want to remove selected group? All assigned hosts " "to this group will be unassigned.", "Confirmation") if retCode == QMessageBox.Cancel: return self.hosts.deleteGroup(self.currentGroupName) self.setHostList() def showDeleteGroupDialog(self): deleteGroupDialog = DeleteGroupDialog(self.hosts) deleteGroupDialog.deleteGroup() self.setHostList() def duplicateHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.duplicate(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def deleteHost(self): retCode = self.showOkCancelMessageBox("Do you want to remove selected hosts?", "Confirmation") if retCode == QMessageBox.Cancel: return for host in self.getSelectedHosts(): self.hosts.delete(host) self.setHostList() def connectFrameless(self, screenIndex=None): self.connectHost(self.getCurrentHostListItemName(), frameless=True, screenIndex=screenIndex) # Fix to release keyboard from QX11EmbedContainer, when we leave widget through wm border def leaveEvent(self, event): keyG = QWidget.keyboardGrabber() if keyG is not None: keyG.releaseKeyboard() event.accept() # needed? def setHostList(self): """ set hosts list in list view """ self.ui.hostsList.clear() self.refreshGroups() hostFilter = self.ui.filter.text() if self.showHostsInGroups: self.showHostListInGroups(hostFilter) else: self.showHostList(hostFilter) def showHostList(self, hostFilter): groupFilter = [group for group, visibility in self.groups.items() if visibility] hosts = self.hosts.getHostsListByHostNameAndGroup(hostFilter, groupFilter) self.ui.hostsList.addItems(hosts) def showHostListInGroups(self, hostFilter): hosts = self.hosts.getGroupedHostNames(hostFilter) for group, hostsList in hosts.items(): if self.groups.get(group, True): if group is None: group = unassignedGroupName groupHeader = QtGui.QListWidgetItem(type=self.typeQListWidgetHeader) groupLabel = QtGui.QLabel(unicode(group)) groupLabel.setProperty('class', 'group-title') self.ui.hostsList.addItem(groupHeader) self.ui.hostsList.setItemWidget(groupHeader, groupLabel) self.ui.hostsList.addItems(hostsList) def slotShowHost(self, item): # on one click we activating tab and showing options self.tabWidget.activateTab(item) def slotConnectHost(self, item): if self.isHostListHeader(item): return self.connectHost(unicode(item.text())) def connectHost(self, hostId, frameless=False, screenIndex=None): hostId = unicode(hostId) # sometimes hostId comes as QString tabPage = self.tabWidget.createTab(hostId) tabPage.reconnectionNeeded.connect(self.connectHost) if frameless: self.tabWidget.detachFrameless(tabPage, screenIndex) try: execCmd, opts = self.getCmd(tabPage, hostId) except LookupError: logger.error(u"Host {} not found.".format(hostId)) return ProcessManager.start(hostId, tabPage, execCmd, opts) return tabPage def getCmd(self, tabPage, hostName): host = self.hosts.get(hostName) # set tabPage widget width, height = tabPage.setSizeAndGetCurrent() # 1et widget winId to embed rdesktop winId = tabPage.x11.winId() # set remote desktop client, at this time works only with freerdp remoteClientType, remoteClientOptions = self.config.getRdpClient() remoteClient = ClientFactory(remoteClientType, **remoteClientOptions) remoteClient.setWindowParameters(winId, width, height) remoteClient.setUserAndPassword(host.user, host.password) remoteClient.setAddress(host.address) return remoteClient.getComposedCommand() def saveSettings(self): self.config.setValue("geometry", self.saveGeometry()) self.config.setValue("windowState", self.saveState()) self.config.setValue('trayIconVisibility', self.tray.isVisible()) self.config.setValue('mainWindowVisibility', self.isVisible()) self.config.setValue('groups', self.groups) self.config.setValue('showHostsInGroups', self.showHostsInGroups) def restoreSettings(self): try: self.restoreGeometry(self.config.getValue("geometry").toByteArray()) self.restoreState(self.config.getValue("windowState").toByteArray()) except Exception: logger.debug("No settings to restore") # restore tray icon state trayIconVisibility = self.config.getValue('trayIconVisibility', "true").toBool() self.tray.setVisible(trayIconVisibility) self.showHostsInGroups = self.config.getValue('showHostsInGroups', 'false').toBool() if self.tray.isVisible(): mainWindowVisibility = self.config.getValue('mainWindowVisibility', "true").toBool() self.setVisible(mainWindowVisibility) else: # it tray icon is not visible, always show main window self.show() self.groups = {unicode(k): v for k, v in self.config.getValue('groups', {}).toPyObject().items()} def closeEvent(self, event): if not ProcessManager.hasActiveProcess: self.saveSettings() QCoreApplication.exit() return ret = self.showOkCancelMessageBox("Are you sure do you want to quit?", "Exit confirmation") if ret == QMessageBox.Cancel: event.ignore() return self.saveSettings() ProcessManager.killemall() event.accept() QCoreApplication.exit() def showOkCancelMessageBox(self, messageBoxText, windowTitle): msgBox = QMessageBox(self, text=messageBoxText) msgBox.setWindowTitle(windowTitle) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setIcon(QMessageBox.Question) return msgBox.exec_()
class LogSParserMain(QMainWindow): """ This is the main class in the application. It's responsible for displaying the log data in a tabular format as well as allowing the user to filter the logs displayed. """ per_column_filter_out_set_list = list() per_column_filter_in_set_list = list() header = list() table_conditional_formatting_config = None def __init__(self): QMainWindow.__init__(self) self.graph_window_dict = {} self.menuFilter = None self.proxy_model = None self.table_data = None self.user_interface = Ui_Siraj() self.user_interface.setupUi(self) self.user_interface.mnuActionOpen.triggered.connect( self.menu_open_file) self.user_interface.mnuActionLoadConfigs.triggered.connect( self.menu_load_configs) self.user_interface.mnuActionExit.triggered.connect(self.menu_exit) self.user_interface.mnuActionAbout.triggered.connect(self.menu_about) self.user_interface.centralwidget.setLayout( self.user_interface.verticalLayout) self.user_interface.dckSourceContents.setLayout( self.user_interface.lytSource) self.user_interface.tblLogData.doubleClicked.connect( self.cell_double_clicked) self.user_interface.tblLogData.clicked.connect(self.cell_left_clicked) self.user_interface.tblLogData.keyPressEvent = self.cell_key_pressed self.user_interface.tblLogData.setContextMenuPolicy( Qt.CustomContextMenu) self.user_interface.tblLogData.customContextMenuRequested.connect( self.cell_right_clicked) self.user_interface.txtSourceFile.setReadOnly(True) self.is_table_visible = True self.is_source_visible = True self.user_interface.tblLogData.resizeColumnsToContents() self.user_interface.tblLogData.resizeRowsToContents() self.setup_context_menu() self.setup_toolbars() self.clipboard = QApplication.clipboard() self.is_filtering_mode_out = True self.matched_row_list = [] self.search_criteria_updated = True self.case_sensitive_search_type = Qt.CaseInsensitive self.is_wrap_search = True self.is_match_whole_word = False self.graph_marker_list = [] self.user_interface.tblLogData.setAcceptDrops(False) self.setAcceptDrops(True) self.load_configuration_file() self.toggle_source_view() def setup_toolbars(self): source_toolbar = self.addToolBar('SourceToolbar') self.user_interface.tbrActionToggleSourceView = QAction('C/C++', self) self.user_interface.tbrActionToggleSourceView.triggered.connect( self.toggle_source_view) self.user_interface.tbrActionToggleSourceView.setToolTip( "Toggle source code view") self.user_interface.tbrActionToggleSourceView.setCheckable(True) self.user_interface.tbrActionToggleSourceView.setChecked(True) source_toolbar.addAction(self.user_interface.tbrActionToggleSourceView) search_toolbar = self.addToolBar("SearchToolbar") search_toolbar.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) self.ledSearchBox = QLineEdit() self.ledSearchBox.textChanged.connect(self.invalidate_search_criteria) self.ledSearchBox.keyPressEvent = self.search_box_key_pressed search_toolbar.addWidget(self.ledSearchBox) tbrActionPrevSearchMatch = QAction('<<', self) tbrActionPrevSearchMatch.triggered.connect( functools.partial(self.select_search_match, False)) tbrActionPrevSearchMatch.setToolTip("Go to previous search match") tbrActionNextSearchMatch = QAction('>>', self) tbrActionNextSearchMatch.triggered.connect( functools.partial(self.select_search_match, True)) tbrActionNextSearchMatch.setToolTip("Go to next search match") tbrActionIgnoreCase = QAction('Ignore Case', self) tbrActionIgnoreCase.setCheckable(True) tbrActionIgnoreCase.setChecked(True) tbrActionIgnoreCase.triggered.connect(self.set_search_case_sensitivity, tbrActionIgnoreCase.isChecked()) tbrActionIgnoreCase.setToolTip("Ignore case") tbrActionWrapSearch = QAction('Wrap Search', self) tbrActionWrapSearch.setCheckable(True) tbrActionWrapSearch.setChecked(True) tbrActionWrapSearch.triggered.connect(self.set_search_wrap, tbrActionWrapSearch.isChecked()) tbrActionWrapSearch.setToolTip("Wrap Search") tbrActionMatchWholeWord = QAction('Match Whole Word', self) tbrActionMatchWholeWord.setCheckable(True) tbrActionMatchWholeWord.setChecked(False) tbrActionMatchWholeWord.triggered.connect( self.set_match_whole_word, tbrActionMatchWholeWord.isChecked()) tbrActionMatchWholeWord.setToolTip("Match Whole Word") search_toolbar.addAction(tbrActionPrevSearchMatch) search_toolbar.addAction(tbrActionNextSearchMatch) search_toolbar.addAction(tbrActionIgnoreCase) search_toolbar.addAction(tbrActionMatchWholeWord) search_toolbar.addAction(tbrActionWrapSearch) def set_search_case_sensitivity(self, ignore_case): self.invalidate_search_criteria() if (ignore_case): self.case_sensitive_search_type = Qt.CaseInsensitive else: self.case_sensitive_search_type = Qt.CaseSensitive def set_search_wrap(self, wrap_search): self.invalidate_search_criteria() self.is_wrap_search = wrap_search def set_match_whole_word(self, match_whole_word): self.invalidate_search_criteria() self.is_match_whole_word = match_whole_word def invalidate_search_criteria(self): self.search_criteria_updated = True self.matched_row_list.clear() def get_matched_row_list(self, key_column, search_criteria, case_sensitivity): search_proxy = QSortFilterProxyModel() search_proxy.setSourceModel(self.user_interface.tblLogData.model()) search_proxy.setFilterCaseSensitivity(case_sensitivity) search_proxy.setFilterKeyColumn(key_column) if self.is_match_whole_word: search_criteria = r"\b{}\b".format(search_criteria) search_proxy.setFilterRegExp(search_criteria) matched_row_list = [] for proxy_row in range(search_proxy.rowCount()): match_index = search_proxy.mapToSource( search_proxy.index(proxy_row, key_column)) matched_row_list.append(match_index.row()) self.search_criteria_updated = False return matched_row_list def select_search_match(self, is_forward): selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 0): self.display_message_box( "No selection", "Please select a cell from the column you want to search", QMessageBox.Warning) else: index = self.get_selected_indexes()[0] row = index.row() column = index.column() search_criteria = self.ledSearchBox.text() if (self.search_criteria_updated): self.matched_row_list = self.get_matched_row_list( column, search_criteria, self.case_sensitive_search_type) if (len(self.matched_row_list) > 0): is_match_found = False if (is_forward): matched_row_index = bisect_left(self.matched_row_list, row) if ((matched_row_index < len(self.matched_row_list) - 1)): if (self.matched_row_list[matched_row_index] == row): matched_row_index += 1 is_match_found = True elif (self.is_wrap_search): matched_row_index = 0 is_match_found = True else: matched_row_index = bisect_right(self.matched_row_list, row) if (matched_row_index > 0): matched_row_index -= 1 if ((matched_row_index > 0)): if ((self.matched_row_list[matched_row_index] == row)): matched_row_index -= 1 is_match_found = True elif (self.is_wrap_search): matched_row_index = len(self.matched_row_list) - 1 is_match_found = True if (is_match_found): self.select_cell_by_row_and_column( self.matched_row_list[matched_row_index], column) else: self.display_message_box( "No match found", 'Search pattern "{}" was not found in column "{}"'.format( search_criteria, self.header[column]), QMessageBox.Warning) def reset_per_config_file_data(self): self.graph_window_dict.clear() self.reset_per_log_file_data() self.table_data = None self.table_model = None self.proxy_model = None def load_configuration_file(self, config_file_path="siraj_configs.json"): self.reset_per_config_file_data() self.config = LogSParserConfigs(config_file_path) self.log_file_full_path = self.config.get_config_item( "log_file_full_path") self.log_trace_regex_pattern = self.config.get_config_item( "log_row_pattern") self.time_stamp_column = self.config.get_config_item( "time_stamp_column_number_zero_based") self.user_data_column_zero_based = self.config.get_config_item( "user_data_column_zero_based") self.external_editor_configs = self.config.get_config_item( "external_editor_configs") cross_reference_configs = self.config.get_config_item( "source_cross_reference_configs") self.file_column = cross_reference_configs[ "file_column_number_zero_based"] self.file_column_pattern = cross_reference_configs[ "file_column_pattern"] self.line_column = cross_reference_configs[ "line_column_number_zero_based"] self.line_column_pattern = cross_reference_configs[ "line_column_pattern"] self.graph_configs = self.config.get_config_item("graph_configs") self.root_source_path_prefix = cross_reference_configs[ "root_source_path_prefix"] self.syntax_highlighting_style = cross_reference_configs[ "pygments_syntax_highlighting_style"] self.table_conditional_formatting_config = self.config.get_config_item( "table_conditional_formatting_configs") self.load_log_file(self.log_file_full_path) def load_graphs(self, graph_configs, table_data): pg.setConfigOption('background', QColor("white")) pg.setConfigOption('foreground', QColor("black")) pg.setConfigOptions(antialias=True) window_dict = graph_configs["window_dict"] series_list = [] for window_name in window_dict: window_handle = pg.GraphicsWindow(title=window_name) self.graph_window_dict[window_name] = window_handle window_handle.show() plot_dict = window_dict[window_name]["plot_dict"] first_plot_name_in_the_window = "" for plot_name in plot_dict: plot_row = plot_dict[plot_name]["row"] # plot_column = plot_dict[plot_name]["column"] # plot_row_span = plot_dict[plot_name]["row_span"] # plot_column_span = plot_dict[plot_name]["column_span"] plot_handle = window_handle.addPlot( name=plot_name, title=plot_name, row=plot_row, col=1, #plot_column, rowspan=1, #plot_row_span, colspan=1) #plot_column_span) plot_handle.addLegend() if first_plot_name_in_the_window == "": first_plot_name_in_the_window = plot_name plot_handle.setXLink(first_plot_name_in_the_window) marker = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen(width=1, color=QColor("red"))) plot_handle.addItem(marker, ignoreBounds=True) self.graph_marker_list.append(marker) plot_handle.scene().sigMouseClicked.connect( functools.partial(self.graph_mouse_clicked, plot_handle)) plot_handle.scene().setClickRadius(50) series_dict = plot_dict[plot_name]["series_dict"] for series_name in series_dict: series_symbol = series_dict[series_name]["symbol"] series_color = series_dict[series_name]["color"] series_pattern = series_dict[series_name]["pattern"] series_list.append( (series_name, series_symbol, series_color, series_pattern, [], [], plot_handle)) for row_number, row_data in enumerate(table_data): for (series_name, series_symbol, series_color, series_pattern, x_point_list, y_point_list, plot_handle) in series_list: cell_to_match = row_data[self.user_data_column_zero_based] m = re.search(series_pattern, cell_to_match) if m is not None: x_point_list.append(row_number) y_point_list.append(int(m.group(1))) for (series_name, series_symbol, series_color, series_pattern, x_point_list, y_point_list, plot_handle) in series_list: plot_handle.plot(x_point_list, y_point_list, pen=pg.mkPen(width=1, color=QColor(series_color)), symbol=series_symbol, symbolPen='w', symbolBrush=QColor(series_color), name=series_name) # graphs = list(sorted(graph_configs.keys(), key=lambda k: graph_configs[k]["index"])) # graph_data = [([], [],) for _ in graphs] # # self.graph_marker_list = [] # # for row_number, row_data in enumerate(table_data): # for graph_number, graph_name in enumerate(graphs): # cell_to_match = row_data[graph_configs[graph_name]["column"]] # m = re.search(graph_configs[graph_name]["pattern"], cell_to_match) # if (m is not None): # graph_data[graph_number][0].append(row_number) # X-Axis value # graph_data[graph_number][1].append(int(m.group(1))) # Y-Axis value # # for graph in graphs: # window = None # wnd = graph_configs[graph]["window"] # if (wnd in self.graph_window_dict): # window = self.graph_window_dict[wnd] # window.clear() # # is_new_window = False # first_plot_name = None # for graph_number, graph in enumerate(graphs): # window = None # wnd = graph_configs[graph]["window"] # if (wnd in self.graph_window_dict): # window = self.graph_window_dict[wnd] # is_new_window = False # else: # is_new_window = True # window = pg.GraphicsWindow(title=wnd) # # self.graph_window_dict[wnd] = window # # p = window.addPlot(name=graph, title=graph) # # p.plot(graph_data[graph_number][0], # graph_data[graph_number][1], # pen=pg.mkPen(width=1, color=QColor(graph_configs[graph]["color"])), # symbol=graph_configs[graph]["symbol"], symbolPen='w', # symbolBrush=QColor(graph_configs[graph]["color"]), name=graph) # p.showGrid(x=True, y=True) # if first_plot_name == None: # first_plot_name = graph # p.setXLink(first_plot_name) # marker = pg.InfiniteLine(angle=90, movable=False) # p.addItem(marker, ignoreBounds=True) # self.graph_marker_list.append(marker) # p.scene().sigMouseClicked.connect(functools.partial(self.graph_mouse_clicked, p)) # # window.nextRow() def graph_mouse_clicked(self, plt, evt): point = plt.vb.mapSceneToView(evt.scenePos()) self.select_cell_by_row_and_column(int(round(point.x())), self.user_data_column_zero_based) self.update_graph_markers() def setup_context_menu(self): self.menuFilter = QMenu(self) self.hide_action = QAction('Hide selected values', self) self.show_only_action = QAction('Show only selected values', self) self.clear_all_filters_action = QAction('Clear all filters', self) self.copy_selection_action = QAction('Copy selection', self) self.unhide_menu = QMenu('Unhide item from selected column', self.menuFilter) self.hide_action.triggered.connect( self.hide_rows_based_on_selected_cells) self.show_only_action.triggered.connect( self.show_rows_based_on_selected_cells) self.clear_all_filters_action.triggered.connect(self.clear_all_filters) self.copy_selection_action.triggered.connect( self.prepare_clipboard_text) self.menuFilter.addAction(self.hide_action) self.menuFilter.addMenu(self.unhide_menu) self.menuFilter.addAction(self.show_only_action) self.menuFilter.addAction(self.clear_all_filters_action) self.menuFilter.addSeparator() self.menuFilter.addAction(self.copy_selection_action) self.hide_action.setShortcut('Ctrl+H') self.show_only_action.setShortcut('Ctrl+O') self.clear_all_filters_action.setShortcut('Ctrl+Del') self.copy_selection_action.setShortcut("Ctrl+C") def toggle_source_view(self): self.is_source_visible = not self.is_source_visible self.user_interface.tbrActionToggleSourceView.setChecked( self.is_source_visible) self.user_interface.dckSource.setVisible(self.is_source_visible) logging.info("Source view is now {}".format( "Visible" if self.is_source_visible else "Invisible")) def display_message_box(self, title, message, icon): """ Show the about box. """ message_box = QMessageBox(self) message_box.setWindowTitle(title) message_box.setTextFormat(Qt.RichText) message_box.setText(message) message_box.setIcon(icon) message_box.exec_() def menu_about(self): """ Show the about box. """ about_text = """ Copyright 2015 Mohamed Galal El-Din Ebrahim (<a href="mailto:[email protected]">[email protected]</a>) <br> <br> siraj is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License. <br> <br> siraj is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. <br> <br> You should have received a copy of the GNU General Public License along with siraj. If not, see <a href="http://www.gnu.org/licenses">http://www.gnu.org/licenses</a>. """ self.display_message_box("About", about_text, QMessageBox.Information) def menu_exit(self): """ Handles the exit menu clicked event. """ exit(0) def menu_open_file(self): """ Handles the open menu clicked event. """ self.log_file_full_path = QFileDialog.getOpenFileName( self, 'Open Log File', os.getcwd()) if (self.log_file_full_path != ''): self.load_log_file(self.log_file_full_path) def menu_load_configs(self): """ Loads a new configuration file. """ self.config_file_full_path = QFileDialog.getOpenFileName( self, 'Open Config File', os.getcwd()) if (self.config_file_full_path != ''): self.load_configuration_file(self.config_file_full_path) def reset_per_log_file_data(self): self.invalidate_search_criteria() def load_log_file(self, log_file_full_path): """ Loads the given log file into the table. """ self.reset_per_log_file_data() if (log_file_full_path == ""): pass elif (os.path.isfile(log_file_full_path)): with open(log_file_full_path, "r") as log_file_handle: log_file_content_lines = log_file_handle.read().splitlines() pattern = re.compile(self.log_trace_regex_pattern) self.table_data = [] most_recent_valid_table_entry = [] for line in log_file_content_lines: m = pattern.match(line) if (m is not None): most_recent_valid_table_entry = [ group.strip() for group in m.groups() ] self.table_data.append(list(most_recent_valid_table_entry)) else: if (self.user_data_column_zero_based != -1): temp_list = list(most_recent_valid_table_entry) temp_list[self.user_data_column_zero_based] = line self.table_data.append(temp_list) m = re.search(self.log_trace_regex_pattern, log_file_content_lines[1]) self.header = [ group_name for group_name in sorted(m.groupdict().keys(), key=lambda k: m.start(k)) ] self.table_model = MyTableModel( self.table_data, self.header, self.table_conditional_formatting_config, self) logging.info("Headers: %s", self.header) logging.info("%s has %d lines", self.log_file_full_path, len(self.table_data)) self.proxy_model = MySortFilterProxyModel(self) self.proxy_model.setSourceModel(self.table_model) self.user_interface.tblLogData.setModel(self.proxy_model) if (len(self.per_column_filter_out_set_list) == 0): self.per_column_filter_out_set_list = [ set() for column in range(len(self.table_data[0])) ] if (len(self.per_column_filter_in_set_list) == 0): self.per_column_filter_in_set_list = [ set() for column in range(len(self.table_data[0])) ] self.extract_column_dictionaries(self.header, self.table_data) self.load_graphs(self.graph_configs, self.table_data) self.setWindowTitle("Siraj | {}".format(log_file_full_path)) self.select_cell_by_row_and_column( 0, self.user_data_column_zero_based) else: self.display_message_box( "File not Found!", "File <b>`{}`</b> was not found. You can either: <br><br>1. Open a log file via the File menu. Or<br>2. Drag a log file from the system and drop it into the application" .format(log_file_full_path), QMessageBox.Critical) def extract_column_dictionaries(self, header_vector_list, data_matrix_list): """ This function extracts a dictionary of dictionaries The extracted is a dictionary of columns where key is the column name, and the data is another dictionary. The inner dictionary has a key equal to a specific cell value of the current column, and the value is a list of row number where this value appeared in. This will be used to provide quick navigation through the log. """ column_count = len(header_vector_list) self.columns_dict = {} for column, column_name in enumerate(header_vector_list): self.columns_dict[column] = {} for row, log in enumerate(data_matrix_list): for column, field in enumerate(log): if (log[column] not in self.columns_dict[column]): self.columns_dict[column][log[column]] = [] self.columns_dict[column][log[column]].append(row) def cell_left_clicked(self, index): """ Handles the event of clicking on a table cell. If the clicked column was the the column that contain the source file:line information from the log, the function also populate the the EditView with the source file contents with a marker highlighting the line. This is only done if the source view is visible. """ index = self.proxy_model.mapToSource(index) if (self.is_source_visible): logging.info("cell[%d][%d] = %s", index.row(), index.column(), index.data()) row = index.row() file_matcher = re.search(self.file_column_pattern, self.table_data[row][self.file_column]) line_matcher = re.search(self.line_column_pattern, self.table_data[row][self.line_column]) if ((file_matcher is not None) and (line_matcher is not None)): file = file_matcher.group(1) line = line_matcher.group(1) full_path = "{}{}".format(self.root_source_path_prefix, file.strip()) self.load_source_file(full_path, line) self.user_interface.tblLogData.setFocus() self.update_status_bar() self.update_graph_markers() def load_source_file(self, file, line): code = open(file).read() lexer = get_lexer_for_filename(file) formatter = HtmlFormatter(linenos=True, full=True, style=self.syntax_highlighting_style, hl_lines=[line]) result = highlight(code, lexer, formatter) self.user_interface.txtSourceFile.setHtml(result) text_block = self.user_interface.txtSourceFile.document( ).findBlockByLineNumber(int(line)) text_cursor = self.user_interface.txtSourceFile.textCursor() text_cursor.setPosition(text_block.position()) self.user_interface.txtSourceFile.setTextCursor(text_cursor) self.user_interface.txtSourceFile.ensureCursorVisible() def get_selected_indexes(self): """ Returns a list of the currently selected indexes mapped to the source numbering. mapToSource is needed to retrive the actual row number regardless of whether filtering is applied or not. """ return [ self.proxy_model.mapToSource(index) for index in self.user_interface.tblLogData.selectedIndexes() ] def update_status_bar(self): """ Updates the status bar with relevant information """ selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 1): selected_cell_index = selected_indexes[0] number_of_occurances = len(self.columns_dict[ selected_cell_index.column()][selected_cell_index.data()]) self.user_interface.statusbar.showMessage( '["{}"] occurred {} time(s) ~ {}%'.format( selected_cell_index.data(), number_of_occurances, number_of_occurances * 100 // len(self.table_data))) elif (len(selected_indexes) == 2): row_1 = selected_indexes[0].row() row_2 = selected_indexes[1].row() time_stamp1 = float(self.table_data[row_1][self.time_stamp_column]) time_stamp2 = float(self.table_data[row_2][self.time_stamp_column]) self.user_interface.statusbar.showMessage( "Time difference = {}".format(abs(time_stamp2 - time_stamp1))) else: self.user_interface.statusbar.showMessage("") def cell_right_clicked(self, point): """ Handle the event of right-clicking on a table cell. This function is responsible for showing the context menu for the user to choose from. """ index = self.proxy_model.mapToSource( self.user_interface.tblLogData.indexAt(point)) logging.debug("Cell[%d, %d] was right-clicked. Contents = %s", index.row(), index.column(), index.data()) self.right_clicked_cell_index = index self.populate_unhide_context_menu(index.column()) self.prepare_clipboard_text() self.menuFilter.popup(QCursor.pos()) def populate_unhide_context_menu(self, column): self.unhide_menu.clear() if (self.is_filtering_mode_out): filtered_out_set = self.per_column_filter_out_set_list[column] else: filtered_out_set = set(self.columns_dict[column].keys( )) - self.per_column_filter_in_set_list[column] if (len(filtered_out_set) > 0): self.unhide_menu.setEnabled(True) for filtered_string in filtered_out_set: temp_action = QAction(filtered_string, self.unhide_menu) temp_action.triggered.connect( functools.partial( self.unhide_selected_rows_only_based_on_column, self.right_clicked_cell_index.column(), filtered_string)) self.unhide_menu.addAction(temp_action) else: self.unhide_menu.setEnabled(False) def cell_double_clicked(self, index): """ Handles the event of double-clicking on a table cell. If the double clicked cell was at the column of file:line, the function triggers external text editor (currently this is gedit on Linux) and make it point on the corresponding line. """ index = self.proxy_model.mapToSource(index) logging.info("cell[%d][%d] = %s", index.row(), index.column(), index.data()) row = index.row() file_matcher = re.search(self.file_column_pattern, self.table_data[row][self.file_column]) line_matcher = re.search(self.line_column_pattern, self.table_data[row][self.line_column]) if ((file_matcher is not None) and (line_matcher is not None)): file = file_matcher.group(1) line = line_matcher.group(1) full_path = "{}{}".format(self.root_source_path_prefix, file.strip()) logging.info("Using external editor (gedit) to open %s at line %s", file, line) editor = self.external_editor_configs["editor"] editor_command_format = self.external_editor_configs[ "editor_command_format"] editor_command = editor_command_format.format( editor_executable=editor, line_number=line, file_name=full_path) call(editor_command, shell=True) self.user_interface.tblLogData.setFocus() self.update_status_bar() def search_box_key_pressed(self, q_key_event): key = q_key_event.key() if (key in [Qt.Key_Enter, Qt.Key_Return]): if (Qt.ShiftModifier == (int(q_key_event.modifiers()) & (Qt.ShiftModifier))): self.select_search_match(False) else: self.select_search_match(True) else: QLineEdit.keyPressEvent(self.ledSearchBox, q_key_event) def cell_key_pressed(self, q_key_event): """ Handles the event of pressing a keyboard key while on the table. """ logging.warning("A key was pressed!!!") key = q_key_event.key() logging.info("Key = {}".format(key)) if (Qt.ControlModifier == (int(q_key_event.modifiers()) & (Qt.ControlModifier))): if key == Qt.Key_Delete: logging.info( "Delete key pressed while in the table. Clear all filters") self.clear_all_filters() elif key == Qt.Key_H: self.hide_rows_based_on_selected_cells() elif key == Qt.Key_O: self.show_rows_based_on_selected_cells() elif key == Qt.Key_Up: # Jump to previous match selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 1): self.go_to_prev_match(selected_indexes[0]) elif key == Qt.Key_Down: # Jump to next match selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 1): self.go_to_next_match(selected_indexes[0]) elif key == Qt.Key_PageUp: selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 1): prev_bookmark_index = self.table_model.getPrevBookmarkIndex( selected_indexes[0]) if (prev_bookmark_index is not None): self.select_cell_by_index(prev_bookmark_index) elif key == Qt.Key_PageDown: selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 1): next_bookmark_index = self.table_model.getNextBookmarkIndex( selected_indexes[0]) if (next_bookmark_index is not None): self.select_cell_by_index(next_bookmark_index) elif key == Qt.Key_C: selected_indexes = self.get_selected_indexes() self.prepare_clipboard_text() elif key == Qt.Key_B: if (Qt.ShiftModifier == (int(q_key_event.modifiers()) & (Qt.ShiftModifier))): self.table_model.clearAllBookmarks() else: selected_indexes = self.get_selected_indexes() self.table_model.toggleBookmarks(selected_indexes) elif key == Qt.Key_Left: self.select_search_match(is_forward=False) elif key == Qt.Key_Right: self.select_search_match(is_forward=True) elif key == Qt.Key_Home: self.select_cell_by_row_and_column(0, 0) elif key == Qt.Key_End: self.select_cell_by_row_and_column( self.table_model.rowCount(None) - 1, 0) elif key == Qt.Key_F5: self.load_log_file(self.log_file_full_path) else: QTableView.keyPressEvent(self.user_interface.tblLogData, q_key_event) self.update_graph_markers() def update_graph_markers(self): selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 1): for marker in self.graph_marker_list: marker.setPos(selected_indexes[0].row()) def prepare_clipboard_text(self): """ Copy the cell content to the clipboard if a single cell is selected. Or Copy the whole rows if cells from multiple rows are selected. """ selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 0): clipboard_text = "" elif (len(selected_indexes) == 1): clipboard_text = self.user_interface.tblLogData.currentIndex( ).data() else: unique_rows_set = set( [index.row() for index in sorted(selected_indexes)]) row_text_list = [ str(row) + "," + ",".join([ self.proxy_model.index(row, column, QModelIndex()).data() for column in range(self.proxy_model.columnCount()) ]) for row in sorted(unique_rows_set) ] clipboard_text = "\n".join(row_text_list) self.clipboard.setText(clipboard_text) def get_index_by_row_and_column(self, row, column): """ Get the table index value by the given row and column """ index = self.table_model.createIndex(row, column) index = self.proxy_model.mapFromSource(index) return index def select_cell_by_row_and_column(self, row, column): """ Select the cell identified by the given row and column and scroll the table view to make that cell in the middle of the visible part of the table. """ self.user_interface.tblLogData.clearSelection() index = self.get_index_by_row_and_column(row, column) self.user_interface.tblLogData.setCurrentIndex(index) self.user_interface.tblLogData.scrollTo( index, hint=QAbstractItemView.PositionAtCenter) self.user_interface.tblLogData.setFocus() self.update_status_bar() def select_cell_by_index(self, index): """ Select a cell at the given index. """ self.user_interface.tblLogData.clearSelection() index = self.proxy_model.mapFromSource(index) self.user_interface.tblLogData.setCurrentIndex(index) self.user_interface.tblLogData.scrollTo( index, hint=QAbstractItemView.PositionAtCenter) self.user_interface.tblLogData.setFocus() self.update_status_bar() def go_to_prev_match(self, selected_cell): """ Go to the prev cell that matches the currently selected cell in the same column """ matches_list = self.columns_dict[selected_cell.column()][ selected_cell.data()] index = matches_list.index(selected_cell.row()) if (index > 0): new_row = matches_list[index - 1] self.select_cell_by_row_and_column(new_row, selected_cell.column()) def go_to_next_match(self, selected_cell): """ Go to the prev cell that matches the currently selected cell in the same column """ matches_list = self.columns_dict[selected_cell.column()][ selected_cell.data()] index = matches_list.index(selected_cell.row()) if (index < (len(matches_list) - 1)): new_row = matches_list[index + 1] self.select_cell_by_row_and_column(new_row, selected_cell.column()) def get_top_left_selected_row_index(self): """ This function return the top-left selected index from the selected list. It's used for example to anchor the table view around the top-left selected cell following any change in the visible cells due to filtering """ top_left_index = None selected_indexes = self.get_selected_indexes() if (len(selected_indexes) > 0): selected_indexes = self.get_selected_indexes() top_left_index = selected_indexes[0] row = top_left_index.row() column = top_left_index.column() for index in selected_indexes[1:]: if ((index.row() < row) and (index.column() < column)): row = index.row() column = index.column() top_left_index = index return top_left_index def clear_all_filters(self): """ Clears all the current filter and return the table to its initial view. """ top_selected_index = self.get_top_left_selected_row_index() self.per_column_filter_out_set_list = [ set() for column in range(len(self.table_data[0])) ] self.per_column_filter_in_set_list = [ set() for column in range(len(self.table_data[0])) ] self.apply_filter(is_filtering_mode_out=True) if (top_selected_index != None): self.select_cell_by_index(top_selected_index) self.update_status_bar() def hide_rows_based_on_selected_cells(self): """ Hides the selected rows and any other rows with matching data. """ selected_indexes = self.get_selected_indexes() for index in selected_indexes: column = index.column() self.per_column_filter_out_set_list[column].add(index.data()) new_selected_row = None min_selected_row = selected_indexes[0].row() max_selected_row = selected_indexes[-1].row() if (min_selected_row != 0): new_selected_row = min_selected_row - 1 elif (max_selected_row != self.table_model.columnCount(None)): new_selected_row = max_selected_row + 1 self.apply_filter(is_filtering_mode_out=True) self.select_cell_by_row_and_column(new_selected_row, selected_indexes[0].column()) self.update_status_bar() def show_rows_based_on_selected_cells(self): """ Shows the selected rows and any other rows with matching data only. """ selected_indexes = self.get_selected_indexes() self.per_column_filter_in_set_list = [ set() for column in range(len(self.table_data[0])) ] for index in selected_indexes: column = index.column() self.per_column_filter_in_set_list[column].add(index.data()) self.apply_filter(is_filtering_mode_out=False) self.update_status_bar() def unhide_selected_rows_only_based_on_column(self, filter_column, filtered_out_string): """ Unhides the selected rows and any other rows with matching data. The filtering works on one column only. """ top_selected_index = self.get_top_left_selected_row_index() if (self.is_filtering_mode_out): self.per_column_filter_out_set_list[filter_column].remove( filtered_out_string) else: self.per_column_filter_in_set_list[filter_column].add( filtered_out_string) logging.debug("Unhiding: %s", filtered_out_string) self.apply_filter(self.is_filtering_mode_out) if (top_selected_index != None): self.select_cell_by_index(top_selected_index) self.update_status_bar() def apply_filter(self, is_filtering_mode_out): """ Applies the filter based on the given mode. """ self.is_filtering_mode_out = is_filtering_mode_out if (is_filtering_mode_out): self.proxy_model.setFilterOutList( self.per_column_filter_out_set_list) else: self.proxy_model.setFilterInList( self.per_column_filter_in_set_list) # This is just to trigger the proxy model to apply the filter self.proxy_model.setFilterKeyColumn(0) def dragEnterEvent(self, q_drag_enter_event): if (q_drag_enter_event.mimeData().hasFormat("text/uri-list")): q_drag_enter_event.acceptProposedAction() def dropEvent(self, q_drop_event): url_list = q_drop_event.mimeData().urls() if (len(url_list) == 0): return log_file_list = [url.toLocalFile() for url in url_list] self.log_file_full_path = log_file_list[0] self.load_log_file(self.log_file_full_path) def closeEvent(self, event): app = QApplication([]) # app.closeAllWindows() app.deleteLater() app.closeAllWindows()
class ScriptAssistant: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor.""" self.iface = iface self.plugin_dir = __location__ self.image_dir = os.path.join(__location__, "images") # Initialise locale locale = QSettings().value("locale/userLocale")[0:2] locale_path = os.path.join(__location__, "i18n", "ScriptAssistant_{}.qm".format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > "4.3.3": QCoreApplication.installTranslator(self.translator) # Initialise plugin toolbar self.toolbar = self.iface.addToolBar(u"Script Assistant") self.toolbar.setObjectName(u"Script Assistant") self.test_script_menu = QMenu(self.toolbar) self.test_script_menu.aboutToShow.connect(self.update_test_script_menu) self.actions = [] self.test_actions = [] self.test_script_action = None # Initialise QGIS menu item self.menu = self.tr(u"&Script Assistant") # Initialise plugin dialog self.dlg_settings = SettingsDialog() self.test_cases = [] self.test_modules = [] self.aggregated_test_result = None def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. """ return QCoreApplication.translate("ScriptAssistant", message) def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" settings = QSettings( os.path.join(QgsApplication.qgisSettingsDirPath(), "scriptassistant", "config.ini"), QSettings.IniFormat, ) config_size = settings.value("script_assistant/size") if config_size is None: if gui.settings_manager.load_setting("current_configuration"): pass else: gui.settings_manager.save_setting("current_configuration", "Script Assistant") gui.settings_manager.save_setting("script_folder", "") gui.settings_manager.save_setting( "test_folder", os.path.join(__location__, "tests")) gui.settings_manager.save_setting("test_data_folder", "") gui.settings_manager.save_setting("no_reload", "N") gui.settings_manager.save_setting("current_test", "$ALL") settings.beginWriteArray("script_assistant") settings.setArrayIndex(0) settings.setValue("configuration", "Script Assistant") settings.setValue("script_folder", "") settings.setValue("test_data_folder", "") settings.setValue("test_folder", os.path.join(__location__, "tests")) settings.setValue("no_reload", "N") settings.endArray() self.create_reload_action() self.create_test_tool_button() self.create_add_test_data_action() self.create_settings_action() def create_reload_action(self): """ Creates the actions and tool button required for reloading scripts from a folder. """ script_folder = gui.settings_manager.load_setting("script_folder") # Reload self.reload_scripts_action = self.add_action( "reload_scripts.png", "Reload: {}".format(script_folder), self.reload_scripts) self.toolbar.addAction(self.reload_scripts_action) if not script_folder: self.reload_scripts_action.setEnabled(False) elif not os.path.isdir(script_folder): self.reload_scripts_action.setEnabled(False) self.iface.messageBar().pushMessage( self.tr("Invalid Script Folder"), self.tr("Please re-configure the script folder."), level=QgsMessageBar.CRITICAL, ) def create_test_tool_button(self): """ Creates the actions and tool button required for running tests within QGIS. """ self.create_test_script_menu() self.test_tool_button = self.create_tool_button(self.test_script_menu) self.test_tool_button.setDefaultAction(self.test_script_action) def create_add_test_data_action(self): """ Creates the actions and tool button required for adding test data. """ test_data_folder = gui.settings_manager.load_setting( "test_data_folder") # Reload self.add_test_data_action = self.add_action( "add_test_data.png", "Add Test Data From: {}".format(test_data_folder), self.add_test_data_to_map) self.toolbar.addAction(self.add_test_data_action) if not test_data_folder: self.add_test_data_action.setEnabled(False) elif not os.path.isdir(test_data_folder): self.add_test_data_action.setEnabled(False) self.iface.messageBar().pushMessage( self.tr("Invalid Test Data Folder"), self.tr("Please re-configure the test data folder."), level=QgsMessageBar.CRITICAL, ) def create_settings_action(self): """ Creates the actions and tool button required for running tests within QGIS. """ self.settings_action = self.add_action( "settings.png", "Open Script Assistant Settings", self.open_settings_dialog) self.toolbar.addAction(self.settings_action) def add_action(self, icon_filename, text, callback, test=False): """Creates an action with an icon, assigned to a QToolButton menu.""" icon_path = os.path.join(self.image_dir, icon_filename) icon = QIcon() icon.addFile(icon_path, QSize(8, 8)) action = QAction(icon, text, self.toolbar) action.triggered.connect(callback) if test is False: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) else: self.test_actions.append(action) return action def create_tool_button(self, tool_button_menu): """Creates an icon style menu.""" tool_button = QToolButton() tool_button.setMenu(tool_button_menu) # The first action created is the default try: tool_button.setDefaultAction(tool_button_menu.actions()[0]) except IndexError: pass tool_button.setPopupMode(QToolButton.MenuButtonPopup) self.toolbar.addWidget(tool_button) return tool_button @pyqtSlot() def reload_scripts(self): """ Copies and overwrites scripts from the configured folder to the QGIS scripts folder. """ folder_dir = gui.settings_manager.load_setting("script_folder") user_script_dir = os.path.join(QgsApplication.qgisSettingsDirPath(), "processing", "scripts") if folder_dir: for filename in os.listdir(folder_dir): if filename.endswith(".py") and not filename.startswith("_"): if self.is_processing_script( os.path.join(folder_dir, filename)): copy(os.path.join(folder_dir, filename), user_script_dir) plugins["processing"].toolbox.updateProvider("script") else: self.iface.messageBar().pushMessage( self.tr("No Script Folder Configured"), self.tr("Please configure script folder first."), level=QgsMessageBar.CRITICAL, ) @staticmethod def is_processing_script(filename): """ Find the first non-blank line of the python file and ensure that it contains ##formatting that looks like a processing script. """ with open(filename) as lines: line = lines.readline() while line != "": if line.startswith("##"): if line.startswith("## ") or line.startswith("###"): return False else: return True else: return False def update_test_script_menu(self): """ """ self.test_script_menu.clear() self.create_test_script_menu() self.test_tool_button.setDefaultAction(self.test_script_action) def create_test_script_menu(self): """ """ test_folder = gui.settings_manager.load_setting("test_folder") if test_folder: if not os.path.isdir(test_folder): self.iface.messageBar().pushMessage( self.tr("Invalid Test Folder"), self.tr("Please reconfigure the test folder."), level=QgsMessageBar.CRITICAL, ) self.test_actions = [] if self.test_script_action: self.iface.removePluginMenu(self.tr(u"&Script Assistant"), self.test_script_action) self.test_script_action = self.add_action( "test_scripts.png", "Test: {}".format( gui.settings_manager.load_setting("current_test")), partial(self.prepare_test, gui.settings_manager.load_setting("current_test"))) self.test_script_menu.addAction(self.test_script_action) self.test_all_action = self.add_action( "test_scripts.png", "all in: {}".format(test_folder), partial(self.prepare_test, "$ALL"), True) self.test_script_menu.addAction(self.test_all_action) if os.path.isdir(test_folder): self.test_cases = [] self.test_modules = [] self.update_unique_test_modules(test_folder) for test_module_name in self.test_modules: action = self.add_action( "test_scripts.png", test_module_name, partial(self.prepare_test, test_module_name), True) self.test_script_menu.addAction(action) if not gui.settings_manager.load_setting( "current_test") in self.test_modules: gui.settings_manager.save_setting("current_test", "$ALL") self.test_script_action.setText("Test: all") if not test_folder or not os.path.isdir(test_folder): self.test_script_action.setEnabled(False) self.test_all_action.setEnabled(False) def update_unique_test_modules(self, test_folder): """ Loops through all TestCase instances in a test folder to find unique test modules """ tests = unittest.TestLoader().discover(test_folder, pattern="test_*.py") self.update_all_test_cases(tests) all_test_modules = [] for t in self.test_cases: all_test_modules.append(type(t).__module__) unique_test_modules = list(set(all_test_modules)) self.test_modules = unique_test_modules self.test_modules.sort() def update_all_test_cases(self, test_suite): """ Loops through the test suites discovered using unittest.TestLoader().discover() to find all individual TestCase instances and return them in a list """ for test_or_suite in test_suite: if unittest.suite._isnotsuite(test_or_suite): # confirmed test self.test_cases.append(test_or_suite) else: # confirmed suite self.update_all_test_cases(test_or_suite) @pyqtSlot() def prepare_test(self, test_name): """Open the QGIS Python Console. Handle testing all tests.""" self.open_python_console() gui.settings_manager.save_setting("current_test", test_name) self.update_test_script_menu() if test_name: self.aggregated_test_result = unittest.TestResult() if test_name == "$ALL": self.add_test_data_action.setEnabled(False) test_folder = gui.settings_manager.load_setting("test_folder") self.update_unique_test_modules(test_folder) for test_module_name in self.test_modules: result = self.run_test(test_module_name) self.prepare_result(result) else: if not self.add_test_data_action.isEnabled(): test_data_folder = gui.settings_manager.load_setting( "test_data_folder") if os.path.isdir(test_data_folder): self.add_test_data_action.setEnabled(True) result = self.run_test(test_name) self.prepare_result(result) self.print_aggregated_result() else: # Ideally the button would be disabled, but that isn't possible # with QToolButton without odd workarounds self.iface.messageBar().pushMessage( self.tr("No Test Script Configured"), self.tr("Please configure a script to test first."), level=QgsMessageBar.CRITICAL, ) def prepare_result(self, result): """Extend aggregated TestResult""" if result: self.aggregated_test_result.errors.extend(result.errors) self.aggregated_test_result.failures.extend(result.failures) self.aggregated_test_result.expectedFailures.extend( result.expectedFailures) self.aggregated_test_result.unexpectedSuccesses.extend( result.unexpectedSuccesses) self.aggregated_test_result.skipped.extend(result.skipped) self.aggregated_test_result.testsRun += result.testsRun else: self.iface.messageBar().pushMessage( self.tr("No Test Summary"), self. tr("Test summary could not be provided to output as the run_tests method does not return a result." ), level=QgsMessageBar.WARNING, ) def print_aggregated_result(self): """Print a summary of all tests to the QGIS Python Console""" if self.aggregated_test_result.testsRun: print "" if self.aggregated_test_result.errors: print "ERRORS:\n" for error in self.aggregated_test_result.errors: print error[0] print "-" * len(error[0].__str__()) print "{0}\n".format(error[1]) if self.aggregated_test_result.failures: print "FAILURES:\n" for failure in self.aggregated_test_result.failures: print failure[0] print "-" * len(failure[0].__str__()) print "{0}\n".format(failure[1]) if self.aggregated_test_result.unexpectedSuccesses: print "UNEXPECTED SUCCESSES:\n" for unexpected in self.aggregated_test_result.unexpectedSuccesses: print unexpected print "" if self.aggregated_test_result.skipped: print "SKIPPED:\n" for skip in self.aggregated_test_result.skipped: print "{0} - {1}".format(skip[0], skip[1]) print "" successes = self.aggregated_test_result.testsRun - ( len(self.aggregated_test_result.errors) + len(self.aggregated_test_result.failures) + len(self.aggregated_test_result.expectedFailures) + len(self.aggregated_test_result.unexpectedSuccesses) + len(self.aggregated_test_result.skipped)) self.print_table_row("Successes", successes) self.print_table_row("Errors", len(self.aggregated_test_result.errors)) self.print_table_row("Failures", len(self.aggregated_test_result.failures)) self.print_table_row( "Expected Failures", len(self.aggregated_test_result.expectedFailures)) self.print_table_row( "Unexpected Successes", len(self.aggregated_test_result.unexpectedSuccesses)) self.print_table_row("Skipped", len(self.aggregated_test_result.skipped)) print """+===========================+============+ | Total | {total: >{fill}} | +---------------------------+------------+ """.format(total=self.aggregated_test_result.testsRun, fill='4') else: print "\nNo tests were run.\n" @staticmethod def print_table_row(result_type, count): if count: print """+---------------------------+------------+ | {result_type: <{text_fill}} | {count: >{count_fill}} |""".format( result_type=result_type, text_fill='25', count=count, count_fill='10') def open_python_console(self): """Ensures that the QGIS Python Console is visible to the user.""" pythonConsole = self.iface.mainWindow().findChild( QDockWidget, "PythonConsole") # If Python Console hasn't been opened before in this QGIS session # then pythonConsole will be a None type variable if pythonConsole is not None: if not pythonConsole.isVisible(): pythonConsole.setVisible(True) else: # This method causes the Python Dialog to close if it is open # so we only use it when we know that is is closed self.iface.actionShowPythonDialog().trigger() def run_test(self, test_name): """Import test scripts, run using run_tests method. Optionally reload and view depending on settings. """ module = import_module(test_name) # have to reload otherwise a QGIS restart is required after changes if gui.settings_manager.load_setting("no_reload") == "Y": pass else: reload(module) suite = unittest.TestLoader().loadTestsFromModule(module) result = unittest.TextTestRunner(verbosity=2, stream=sys.stdout).run(suite) return result @pyqtSlot() def add_test_data_to_map(self): """Adds test data referred to in the test script to the map. Must be .shp (shapefile). """ test_data_folder = gui.settings_manager.load_setting( "test_data_folder") test_folder = gui.settings_manager.load_setting("test_folder") current_test = gui.settings_manager.load_setting("current_test") if current_test == "$ALL": self.iface.messageBar().pushMessage( self.tr("Select a Single Test"), self.tr("Cannot add test data for all tests."), level=QgsMessageBar.WARNING, ) return test_file = open( os.path.join(test_folder, "test_{}.py".format(current_test)), "r") test_text = test_file.read() test_file.close() matches = re.findall(r"\/(.*).shp", test_text) for match in matches: self.iface.addVectorLayer( os.path.join(test_data_folder, "{}.shp".format(match)), match, "ogr") @pyqtSlot() def open_settings_dialog(self): """Open the settings dialog and show the current configuration.""" self.dlg_settings.show() self.dlg_settings.populate_config_combo() if gui.settings_manager.load_setting("current_configuration"): self.dlg_settings.show_last_configuration() self.dlg_settings.check_changes() result = self.dlg_settings.exec_() # On close if result or not result: # An asterisk in the window title indicates an unsaved configuration. if "*" in self.dlg_settings.windowTitle(): msg_confirm = QMessageBox() msg_confirm.setWindowTitle("Save") msg_confirm.setText( "Would you like to save this configuration?") msg_confirm.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_confirm.setDefaultButton(QMessageBox.Yes) msg_result = msg_confirm.exec_() if msg_result == QMessageBox.Yes: self.dlg_settings.save_configuration() else: msg_inform = QMessageBox() msg_inform.setWindowTitle("Info") msg_inform.setText("The configured settings will be used " "for the current session, but will not " "be saved for future sessions.") msg_inform.addButton(QPushButton("OK"), QMessageBox.AcceptRole) msg_inform.exec_() self.save_settings() def save_settings(self): """Save current settings to Project file and config.""" script_folder = self.dlg_settings.lne_script.text() gui.settings_manager.save_setting("script_folder", script_folder) if os.path.exists(script_folder): self.reload_scripts_action.setText( "Reload: {}".format(script_folder)) self.reload_scripts_action.setEnabled(True) else: self.reload_scripts_action.setText("Invalid Script Folder Path") self.reload_scripts_action.setEnabled(False) if script_folder != "": self.iface.messageBar().pushMessage( self.tr("Invalid Script Folder Path"), self.tr( "The configured script folder is not a valid path."), level=QgsMessageBar.CRITICAL, ) test_folder = self.dlg_settings.lne_test.text() gui.settings_manager.save_setting("test_folder", test_folder) if os.path.exists(test_folder): gui.settings_manager.save_setting("current_test", "$ALL") self.test_script_action.setEnabled(True) self.test_all_action.setEnabled(True) if test_folder not in sys.path: sys.path.append(test_folder) self.update_test_script_menu() else: self.test_script_action.setText("Invalid Test Script Path") self.test_script_action.setEnabled(False) self.test_all_action.setEnabled(False) self.add_test_data_action.setEnabled(False) if test_folder != "": self.iface.messageBar().pushMessage( self.tr("Invalid Test Script Path"), self. tr("The configured test script folder is not a valid path." ), level=QgsMessageBar.CRITICAL, ) test_data_folder = self.dlg_settings.lne_test_data.text() gui.settings_manager.save_setting("test_data_folder", test_data_folder) if os.path.exists(test_data_folder): self.add_test_data_action.setText( "Add Test Data From: {}".format(test_data_folder)) self.add_test_data_action.setEnabled(True) else: self.add_test_data_action.setText("Invalid Test Data Path") self.add_test_data_action.setEnabled(False) if test_data_folder != "": self.iface.messageBar().pushMessage( self.tr("Invalid Test Data Path"), self. tr("The configured test data folder is not a valid path."), level=QgsMessageBar.CRITICAL, ) if self.dlg_settings.chk_reload.isChecked(): gui.settings_manager.save_setting("no_reload", "Y") else: gui.settings_manager.save_setting("no_reload", "N") if self.dlg_settings.cmb_config.lineEdit().text(): gui.settings_manager.save_setting( "current_configuration", self.dlg_settings.cmb_config.lineEdit().text()) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu(self.tr(u"&Script Assistant"), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar
class ExplorerTreeWidget(DirView): def __init__(self, parent=None): DirView.__init__(self, parent) def setup(self, path=None, name_filters=['*.py', '*.pyw'], valid_types=('.py', '.pyw'), show_all=False): self.name_filters = name_filters self.valid_types = valid_types self.show_all = show_all self.refresh(path) # Enable drag events self.setDragEnabled(True) # Setup context menu self.menu = QMenu(self) self.common_actions = self.setup_common_actions() #---- Context menu def setup_common_actions(self): """Setup context menu common actions""" # Filters filters_action = create_action(self, translate('Explorer', "Edit filename filters..."), None, get_icon('filter.png'), triggered=self.edit_filter) # Show all files all_action = create_action(self, translate('Explorer', "Show all files"), toggled=self.toggle_all) all_action.setChecked(self.show_all) self.toggle_all(self.show_all) return [filters_action, all_action] def edit_filter(self): """Edit name filters""" filters, valid = QInputDialog.getText( self, translate('Explorer', 'Edit filename filters'), translate('Explorer', 'Name filters:'), QLineEdit.Normal, ", ".join(self.name_filters)) if valid: filters = [f.strip() for f in unicode(filters).split(',')] self.parent_widget.emit(SIGNAL('option_changed'), 'name_filters', filters) self.set_name_filters(filters) def toggle_all(self, checked): """Toggle all files mode""" self.parent_widget.emit(SIGNAL('option_changed'), 'show_all', checked) self.show_all = checked self.set_show_all(checked) def update_menu(self): """Update option menu""" self.menu.clear() actions = [] newdir_action = create_action(self, translate('Explorer', "New folder..."), icon="folder_new.png", triggered=self.new_folder) actions.append(newdir_action) newfile_action = create_action(self, translate('Explorer', "New file..."), icon="filenew.png", triggered=self.new_file) actions.append(newfile_action) fname = self.get_filename() if fname is not None: is_dir = osp.isdir(fname) ext = osp.splitext(fname)[1] run_action = create_action(self, translate('Explorer', "Run"), icon="run_small.png", triggered=self.run) edit_action = create_action(self, translate('Explorer', "Edit"), icon="edit.png", triggered=self.clicked) delete_action = create_action(self, translate('Explorer', "Delete..."), icon="delete.png", triggered=self.delete) rename_action = create_action(self, translate('Explorer', "Rename..."), icon="rename.png", triggered=self.rename) browse_action = create_action(self, translate('Explorer', "Browse"), icon=get_std_icon("CommandLink"), triggered=self.clicked) open_action = create_action(self, translate('Explorer', "Open"), triggered=self.startfile) if ext in ('.py', '.pyw'): actions.append(run_action) if ext in self.valid_types or os.name != 'nt': actions.append(browse_action if is_dir else edit_action) else: actions.append(open_action) actions += [delete_action, rename_action] if is_dir and os.name == 'nt': # Actions specific to Windows directories actions.append( create_action(self, translate('Explorer', "Open in Windows Explorer"), icon="magnifier.png", triggered=self.startfile)) if os.name == 'nt': actions.append( create_action( self, translate('Explorer', "Open command prompt here"), icon="cmdprompt.png", triggered=lambda cmd='cmd.exe': os.startfile(cmd))) if actions: actions.append(None) actions += self.common_actions add_actions(self.menu, actions) #---- Refreshing widget def refresh(self, new_path=None, force_current=False): """ Refresh widget force=False: won't refresh widget if path has not changed """ if new_path is None: new_path = os.getcwdu() self.set_folder(new_path, force_current=force_current) #---- Events def contextMenuEvent(self, event): """Override Qt method""" self.update_menu() self.menu.popup(event.globalPos()) def keyPressEvent(self, event): """Reimplement Qt method""" if event.key() in (Qt.Key_Enter, Qt.Key_Return): self.clicked() event.accept() elif event.key() == Qt.Key_F2: self.rename() event.accept() else: DirView.keyPressEvent(self, event) def mouseDoubleClickEvent(self, event): """Reimplement Qt method""" QTreeView.mouseDoubleClickEvent(self, event) self.clicked() #---- Drag def dragEnterEvent(self, event): """Drag and Drop - Enter event""" event.setAccepted(event.mimeData().hasFormat("text/plain")) def dragMoveEvent(self, event): """Drag and Drop - Move event""" if (event.mimeData().hasFormat("text/plain")): event.setDropAction(Qt.MoveAction) event.accept() else: event.ignore() def startDrag(self, dropActions): """Reimplement Qt Method - handle drag event""" mimeData = QMimeData() mimeData.setText(self.get_filename()) drag = QDrag(self) drag.setMimeData(mimeData) drag.exec_() #---- Files/Directories Actions def get_filename(self): """Return selected filename""" index = self.currentIndex() if index: return osp.normpath(unicode(self.model().filePath(index))) def get_dirname(self): """ Return selected directory path or selected filename's directory path """ fname = self.get_filename() if osp.isdir(fname): return fname else: return osp.dirname(fname) def clicked(self): """Selected item was double-clicked or enter/return was pressed""" fname = self.get_filename() if fname: if osp.isdir(fname): self.parent_widget.emit(SIGNAL("open_dir(QString)"), fname) self.refresh() else: self.open(fname) def open(self, fname): """Open filename with the appropriate application""" fname = unicode(fname) ext = osp.splitext(fname)[1] if ext in self.valid_types: self.parent_widget.emit(SIGNAL("open_file(QString)"), fname) else: self.startfile(fname) def startfile(self, fname=None): """Windows only: open file in the associated application""" if fname is None: fname = self.get_filename() emit = False if os.name == 'nt': try: os.startfile(fname) except WindowsError: emit = True else: emit = True if emit: self.parent_widget.emit(SIGNAL("edit(QString)"), fname) def run(self): """Run Python script""" self.parent_widget.emit(SIGNAL("run(QString)"), self.get_filename()) def delete(self): """Delete selected item""" fname = self.get_filename() if fname: answer = QMessageBox.warning(self, translate("Explorer", "Delete"), translate("Explorer", "Do you really want to delete <b>%1</b>?") \ .arg(osp.basename(fname)), QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.No: return try: if osp.isfile(fname): os.remove(fname) else: os.rmdir(fname) self.parent_widget.emit(SIGNAL("removed(QString)"), fname) except EnvironmentError, error: QMessageBox.critical(self, translate('Explorer', "Delete"), translate('Explorer', "<b>Unable to delete selected file</b>" "<br><br>Error message:<br>%1") \ .arg(str(error))) finally:
class Tabs(BaseTabs): """TabWidget with a context-menu""" def __init__(self, parent, actions=None): BaseTabs.__init__(self, parent, actions) tab_bar = TabBar(self, parent) self.connect(tab_bar, SIGNAL('move_tab(int,int)'), self.move_tab) self.connect(tab_bar, SIGNAL('move_tab(long,int,int)'), self.move_tab_from_another_tabwidget) self.setTabBar(tab_bar) self.index_history = [] self.connect(self, SIGNAL('currentChanged(int)'), self.__current_changed) tabsc = QShortcut(QKeySequence("Ctrl+Tab"), parent, self.tab_navigate) tabsc.setContext(Qt.WidgetWithChildrenShortcut) # Browsing tabs button browse_button = create_toolbutton(self, icon=get_icon("browse_tab.png"), tip=translate("Tabs", "Browse tabs")) self.browse_tabs_menu = QMenu(self) browse_button.setMenu(self.browse_tabs_menu) browse_button.setPopupMode(browse_button.InstantPopup) self.connect(self.browse_tabs_menu, SIGNAL("aboutToShow()"), self.update_browse_tabs_menu) self.setCornerWidget(browse_button) def update_browse_tabs_menu(self): """Update browse tabs menu""" self.browse_tabs_menu.clear() for index in range(self.count()): tab_action = create_action(self, self.tabText(index), icon=self.tabIcon(index), toggled=lambda state, index=index: self.setCurrentIndex(index), tip=self.tabToolTip(index)) tab_action.setChecked(index == self.currentIndex()) self.browse_tabs_menu.addAction(tab_action) def __current_changed(self, index): for _i in self.index_history[:]: if _i > self.count()-1: self.index_history.pop(self.index_history.index(_i)) while index in self.index_history: self.index_history.pop(self.index_history.index(index)) self.index_history.append(index) def tab_navigate(self): """Ctrl+Tab""" if len(self.index_history) > 1: last = len(self.index_history)-1 index = self.index_history.pop(last) self.index_history.insert(0, index) self.setCurrentIndex(self.index_history[last]) elif len(self.index_history) == 0 and self.count(): self.index_history = [self.currentIndex()] def move_tab(self, index_from, index_to): """Move tab inside a tabwidget""" self.emit(SIGNAL('move_data(int,int)'), index_from, index_to) tip, text = self.tabToolTip(index_from), self.tabText(index_from) icon, widget = self.tabIcon(index_from), self.widget(index_from) current_widget = self.currentWidget() self.removeTab(index_from) self.insertTab(index_to, widget, icon, text) self.setTabToolTip(index_to, tip) self.setCurrentWidget(current_widget) self.emit(SIGNAL('move_tab_finished()')) def move_tab_from_another_tabwidget(self, tabwidget_from, index_from, index_to): """Move tab from a tabwidget to another""" self.emit(SIGNAL('move_tab(long,long,int,int)'), tabwidget_from, id(self), index_from, index_to)
class MapToolIndentifyItems(QgsMapToolIdentify): def __init__(self, plugin): super(MapToolIndentifyItems, self).__init__(plugin.mapCanvas()) mToolName = self.tr('Identify feature') self._menu = QMenu(plugin.mapCanvas()) self._menu.hovered.connect(self._highlight) self._actions = [] self._highlights = [] self._plugin = plugin self._vertexMarker = QgsVertexMarker(plugin.mapCanvas()) self._vertexMarker.setIconType(QgsVertexMarker.ICON_CROSS) def collection(self): return self._plugin.project().collection('plan') def deactivate(self): self._reset() super(MapToolIndentifyItems, self).deactivate() def canvasPressEvent(self, e): self._reset() def canvasReleaseEvent(self, e): self._reset() if e.button() != Qt.LeftButton: return mapPoint = self.toMapCoordinates(e.pos()) self._vertexMarker.setCenter(mapPoint) layers = [ self.collection().layer('points'), self.collection().layer('lines'), self.collection().layer('polygons') ] results = self.identify(e.x(), e.y(), layers, QgsMapToolIdentify.TopDownAll) if (len(results) < 1): return # Build the set of unique items identified items = set() for result in results: feature = result.mFeature siteCode = feature.attribute('site') classCode = feature.attribute('class') itemId = feature.attribute('id') items.add(Item(siteCode, classCode, itemId)) action = QAction('Plan Items', self._menu) action.setData('top') self._menu.addAction(action) site = '' for item in sorted(items): if item.siteCode() != site: site = item.siteCode() self._menu.addSeparator() action = QAction('Site ' + site + ':', self._menu) action.setData('top') self._menu.addAction(action) action = IdentifyItemAction(item, self._plugin, self._menu) action.setData('top') action.zoomToItemSelected.connect(self._zoom) action.panToItemSelected.connect(self._pan) action.filterItemSelected.connect(self._filterItem) action.excludeFilterItemSelected.connect(self._excludeFilterItem) action.highlightItemSelected.connect(self._highlightItem) action.addHighlightItemSelected.connect(self._addHighlightItem) action.editItemSelected.connect(self._editInBuffers) action.deleteItemSelected.connect(self._delete) action.openDrawingsSelected.connect(self._openDrawings) self._actions.append(action) self._menu.addAction(action) self._menu.addSeparator() action = ClipboardAction('Map: ', mapPoint.toString(3), self._menu) action.setData('top') self._menu.addAction(action) if self._plugin.grid().mapTransformer is not None: localPoint = self._plugin.grid().mapTransformer.map(mapPoint) self._menu.addAction( ClipboardAction('Local: ', localPoint.toString(3), self._menu)) menuPos = QPoint(e.globalX() + 100, e.globalY() - 50) selected = self._menu.exec_(menuPos) self._reset(resetVertex=False) def keyPressEvent(self, e): if (e.key() == Qt.Key_Escape): self._reset() self.canvas().unsetMapTool(self) def _reset(self, resetVertex=True): self._menu.clear() del self._highlights[:] del self._actions[:] if resetVertex and self._vertexMarker: self._vertexMarker.setCenter(QgsPointV2()) def _highlight(self, item): if item.data() == 'top': del self._highlights[:] else: return if not isinstance(item, IdentifyItemAction): return request = item.item.featureRequest() for feature in self.collection().layer('polygons').getFeatures( request): self._addHighlight(self._plugin.mapCanvas(), feature.geometry(), self.collection().layer('polygons')) for feature in self.collection().layer('lines').getFeatures(request): self._addHighlight(self._plugin.mapCanvas(), feature.geometry(), self.collection().layer('lines')) for feature in self.collection().layer('points').getFeatures(request): self._addHighlight(self._plugin.mapCanvas(), feature.geometry(), self.collection().layer('points')) def _addHighlight(self, canvas, geometry, layer): hl = QgsHighlight(canvas, geometry, layer) color = QColor(QSettings().value('/Map/highlight/color', QGis.DEFAULT_HIGHLIGHT_COLOR.name(), str)) alpha = QSettings().value('/Map/highlight/colorAlpha', QGis.DEFAULT_HIGHLIGHT_COLOR.alpha(), int) buff = QSettings().value('/Map/highlight/buffer', QGis.DEFAULT_HIGHLIGHT_BUFFER_MM, float) minWidth = QSettings().value('/Map/highlight/minWidth', QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM, float) hl.setColor(color) color.setAlpha(alpha) hl.setFillColor(color) hl.setBuffer(buff) hl.setMinWidth(minWidth) self._highlights.append(hl) def _zoom(self, item): self.project().zoomToItem(item, highlight=True) def _pan(self, item): self.project().moveToItem(item, highlight=True) def _filterItem(self, item): self.project().filterItem(item) def _excludeFilterItem(self, item): self.project().excludeFilterItem(item) def _highlightItem(self, item): self.project().highlightItem(item) def _addHighlightItem(self, item): self.project().addHighlightItem(item) def _openDrawings(self, item): self.project().loadDrawing(item) def _editInBuffers(self, item): self.project().editInBuffers(item) def _delete(self, item): self.project().deleteItem(item)
class VentanaPrincipal(QtGui.QMainWindow, Ui_VentanaPrincipal): def __init__(self, parent=None): QtGui.QMainWindow.__init__(self, parent) self.setupUi(self) self.nuevo_proyecto = None self.nueva_clase = None self.acerca_de = None self.escoger_habilidades = None self.ingresar_frase = None self.editor = None self.programa = [] self.agregar_widget() self.ruta_proyecto = "" self.ruta_programa = "" self.ruta_objetos = "" self.ruta_propiedades = "" self.ruta_metodos = "" self.objetos = {} self.metodos = [] self.fondos = {} #self.connect(self.nuevo_proyecto, QtCore.SIGNAL("quit()"), self.cerrar_ventana_nuevo_proyecto) self.menAbrir.triggered.connect(self.abrir_proyecto) self.menNuevo.triggered.connect(self.crear_nuevo_proyecto) self.menEjecutar.triggered.connect(self.correr_programa) self.menAcercaDe.triggered.connect(self.abrir_acerca_de) self.menSalir.triggered.connect(self.salir) self.btnEjecutar.clicked.connect(self.correr_programa) self.btnGuardar.clicked.connect(self.guardar_programa) self.arbolActores.setContextMenuPolicy(Qt.CustomContextMenu) self.arbolActores.customContextMenuRequested.connect(self.abrir_menu_interno_actores) self.men_popup_actores = QMenu(self) self.arbolObjetos.setContextMenuPolicy(Qt.CustomContextMenu) self.arbolObjetos.customContextMenuRequested.connect(self.abrir_menu_interno_objetos) self.arbolMundo.setContextMenuPolicy(Qt.CustomContextMenu) self.arbolMundo.customContextMenuRequested.connect(self.abrir_menu_interno_mundo) self.menNuevaClase.triggered.connect(partial(self.abrir_ventana_nueva_clase,arbol=self.arbolActores)) #self.listaFondos.doubleClicked.connect(self.crear_fondo) self.arbolObjetos.clicked.connect(self.seleccionar_objeto) self.sbX.valueChanged.connect(self.cambiar_coord_x) self.sbY.valueChanged.connect(self.cambiar_coord_y) self.sbEscala.valueChanged.connect(self.cambiar_escala) self.sbRotacion.valueChanged.connect(self.cambiar_rotacion) def abrir_menu_interno_objetos(self, position): items = self.arbolObjetos.selectedItems() level = 0 if len(items) > 0: item = items[0] level = 1 menu = QMenu() if level == 1: nombre=str(self.arbolObjetos.currentItem().text(0)) objeto = self.objetos[nombre] self.obtener_metodos(nombre, objeto, menu, item) menu.exec_(self.arbolObjetos.viewport().mapToGlobal(position)) def abrir_menu_interno_actores(self, position): self.men_popup_actores.clear() items = self.arbolActores.selectedItems() level = 0 if len(items) > 0: item = items[0] level = 1 if level == 0: accion = self.men_popup_actores.addAction(self.tr("Agregar Clase")) accion.triggered[()].connect(partial(self.abrir_ventana_nueva_clase,arbol=self.arbolActores)) elif level == 1: accion = self.men_popup_actores.addAction(self.tr("Crear Objeto")) accion.triggered[()].connect(partial(self.crear_objeto,nombre_clase=str(item.text(0)))) accion = self.men_popup_actores.addAction(self.tr("Abrir Editor")) ruta_clase = os.path.join(self.ruta_proyecto,str(item.text(0))+".py") accion.triggered[()].connect(partial(self.abrir_editor,ruta=ruta_clase)) self.men_popup_actores.exec_(self.arbolActores.viewport().mapToGlobal(position)) def abrir_menu_interno_mundo(self, position): items = self.arbolMundo.selectedItems() level = 0 if len(items) > 0: item = items[0] level = 1 menu = QMenu() if level == 0: men_agregar_clase = menu.addAction(self.tr("Agregar Clase")) men_agregar_clase.triggered.connect(partial(self.abrir_ventana_nueva_clase,arbol=self.arbolMundo)) elif level == 1: men_agregar_clase = menu.addAction(self.tr("Crear Fondo")) men_agregar_clase.triggered.connect(partial(self.crear_objeto,nombre_clase=str(item.text(0)))) menu.exec_(self.arbolMundo.viewport().mapToGlobal(position)) def abrir_ventana_nuevo_objeto(self): self.ventana_nuevo_objeto = NuevoObjeto(self) if self.ventana_nuevo_objeto.exec_() == QtGui.QDialog.Accepted: nombre_objeto = self.ventana_nuevo_objeto.txtNombreObjeto.text() self.ventana_nuevo_objeto.deleteLater() return nombre_objeto def abrir_acerca_de(self): self.acerca_de = AcercaDe(self) self.acerca_de.exec_() self.acerca_de.deleteLater() def crear_objeto(self, nombre_clase): nombre_objeto = self.abrir_ventana_nuevo_objeto() objeto = self.instanciar_objeto(nombre_clase, nombre_objeto) self.agregar_objeto_interfaz_principal(nombre_objeto, objeto) def abrir_editor(self,ruta): self.editor=Editor(ruta) self.editor.show() def agregar_objeto_interfaz_principal(self,nombre_objeto,objeto): self.objetos[str(nombre_objeto)]=objeto self.agregar_elemento_arbol(nombre_objeto,self.arbolObjetos) self.taPrograma.append("Se creo el objeto: "+nombre_objeto) @staticmethod def obtener_modulo(nombre_clase): try: modulo = __import__(nombre_clase) except: modulo = None return modulo @staticmethod def obtener_clase(modulo,nombre_clase): return getattr(modulo, nombre_clase) def tipo_actor(self, clase): if clase=='Aceituna': objeto = self.pilas.actores.Aceituna() elif clase=='Mono': objeto = self.pilas.actores.Mono() elif clase == 'Banana': objeto = self.pilas.actores.Banana() elif clase == 'Bomba': objeto = self.pilas.actores.Bomba() elif clase == 'Nave': objeto = self.pilas.actores.Nave() elif clase == 'Pingu': objeto = self.pilas.actores.Pingu() elif clase == 'Cooperativista': objeto = self.pilas.actores.Cooperativista() elif clase == 'Estrella': objeto = self.pilas.actores.Estrella() elif clase == 'Moneda': objeto = self.pilas.actores.Moneda() elif clase == 'Ovni': objeto = self.pilas.actores.Ovni() elif clase == 'Pacman': objeto = self.pilas.actores.Pacman() elif clase == 'Piedra': objeto = self.pilas.actores.Piedra() elif clase == 'Zanahoria': objeto = self.pilas.actores.Zanahoria() objeto.aprender(self.pilas.habilidades.Arrastrable) return objeto def instanciar_objeto(self, nombre_clase, nombre_objeto): sys.path.insert(0, self.ruta_proyecto) try: modulo=self.obtener_modulo(nombre_clase) clase = self.obtener_clase(modulo, nombre_clase) objeto = clase(self.pilas) objeto.aprender(self.pilas.habilidades.Arrastrable) except: objeto=self.tipo_actor(nombre_clase) return objeto def abrir_ventana_nueva_clase(self,arbol): self.nueva_clase=NuevaClase(self.ruta_proyecto) self.nueva_clase.exec_() self.nueva_clase.deleteLater() self.agregar_elemento_arbol(self.nueva_clase.clase, arbol) @staticmethod def salir(): exit() def crear_nuevo_proyecto(self): self.nuevo_proyecto=NuevoProyecto(self) self.nuevo_proyecto.exec_() self.iniciar_proyecto(self.nuevo_proyecto.ruta) self.nuevo_proyecto.deleteLater() def iniciar_proyecto(self,ruta): ruta = str(ruta) if os.path.exists(ruta): os.chdir(ruta) self.ruta_proyecto = str(ruta) self.ruta_principal = os.path.join(self.ruta_proyecto,"principal.py") self.ruta_objetos = os.path.join(self.ruta_proyecto,"objetos") self.ruta_propiedades = os.path.join(self.ruta_proyecto,"propiedades") self.ruta_metodos = os.path.join(self.ruta_proyecto,"metodos") self.activar_controles() self.cargar_clases(ruta) self.cargar_objetos() self.cargar_propiedades() self.cargar_metodos() def abrir_proyecto(self): ruta = QtGui.QFileDialog.getExistingDirectory(self, 'Seleccionar carpeta') self.iniciar_proyecto(ruta) def activar_controles(self): self.btnGuardar.setEnabled(True) self.btnEjecutar.setEnabled(True) self.arbolActores.setEnabled(True) self.arbolMundo.setEnabled(True) self.arbolObjetos.setEnabled(True) self.taPrograma.setEnabled(True) self.menDeshacer.setEnabled(True) self.menRehacer.setEnabled(True) self.menNuevaClase.setEnabled(True) self.menEjecutar.setEnabled(True) def cargar_clases(self,ruta): self.arbolActores.clear() archivos = os.listdir(ruta) for archivo in archivos: if archivo.endswith(".py") and archivo<>'principal.py': self.agregar_elemento_arbol(archivo[0:len(archivo)-3], self.arbolActores) def cargar_objetos(self): ruta=self.ruta_objetos self.arbolObjetos.clear() try: archivo = open(ruta) for linea in archivo: cad = linea.rstrip('\n') palabra = cad.split('=') nombre_objeto = palabra[0] nombre_clase = palabra[1] objeto = self.instanciar_objeto(nombre_clase, nombre_objeto) self.agregar_objeto_interfaz_principal(nombre_objeto, objeto) archivo.close except IOError: archivo = open(ruta,"w") def cargar_propiedades(self): ruta = self.ruta_propiedades try: archivo = open(ruta) for linea in archivo: cad = linea.rstrip('\n') cadena = cad.split('=') cadena_propiedad = cadena[0].split('.') valor=cadena[1] objeto = cadena_propiedad[0] propiedad = cadena_propiedad[1] if propiedad == 'x': self.objetos[objeto].x = float(valor) elif propiedad == 'y': self.objetos[objeto].y = float(valor) elif propiedad == 'rotacion': self.objetos[objeto].rotacion = float(valor) elif propiedad == 'escala': self.objetos[objeto].escala = float(valor) archivo.close except IOError: archivo = open(ruta,"w") def cargar_metodos(self): ruta = self.ruta_metodos try: archivo = open(ruta) for linea in archivo: cad = linea.rstrip('\n') self.metodos.append(cad) archivo.close except IOError: archivo = open(ruta,"w") @staticmethod def escribir_importacion_clases(archivo, objeto): clase = objeto.__class__ modulo = clase.__module__ if modulo.find("pilasengine")<0: archivo.write("from "+modulo+" import " +clase.__name__+"\n") def guardar_programa(self): nombres_objetos = self.objetos.keys() objetos = self.objetos.values() archivo_programa=open(self.ruta_principal,'w') archivo_objetos=open(self.ruta_objetos,'w') archivo_atributos=open(self.ruta_propiedades,'w') archivo_metodos=open(self.ruta_metodos,'w') archivo_programa.write("import pilasengine\n") archivo_programa.write("pilas=pilasengine.iniciar()\n") for objeto in objetos: self.escribir_importacion_clases(archivo_programa,objeto) for nombre in nombres_objetos: clase = str(self.objetos[nombre].__class__.__name__) modulo = self.objetos[nombre].__module__ if modulo.find("pilasengine")>=0: linea_programa = nombre+" = pilas.actores."+clase+"()\n" else: linea_programa = nombre+" = "+clase+"(pilas)\n" archivo_programa.write(linea_programa) linea_objetos = nombre+"="+clase+"\n" archivo_objetos.write(linea_objetos) x,y = self.objetos[nombre].obtener_posicion() linea_atributos = nombre + ".x=" +str(x)+"\n" archivo_atributos.write(linea_atributos) archivo_programa.write(linea_atributos) linea_atributos = nombre + ".y=" +str(y)+"\n" archivo_atributos.write(linea_atributos) archivo_programa.write(linea_atributos) rotacion = self.objetos[nombre].obtener_rotacion() linea_atributos = nombre + ".rotacion=" +str(rotacion)+"\n" archivo_atributos.write(linea_atributos) archivo_programa.write(linea_atributos) escala = self.objetos[nombre].obtener_escala() linea_atributos = nombre + ".escala=" +str(escala)+"\n" archivo_atributos.write(linea_atributos) archivo_programa.write(linea_atributos) for metodo in self.metodos: archivo_programa.write(metodo+'\n') archivo_metodos.write(metodo+'\n') '''nombres_fondos = self.fondos.keys() for nomb_fondo in nombres_fondos: clase_fondo = str(self.fondos[nomb_fondo].__class__.__name__) linea_programa = nomb_fondo+" = pilas.fondos."+clase_fondo+"()\n" archivo_programa.write(linea_programa) linea_objetos = nomb_fondo+"="+clase_fondo+"\n" archivo_objetos.write(linea_objetos) archivo_objetos.close() for linea in self.programa: archivo_programa.write(linea)''' archivo_programa.write("pilas.ejecutar()\n") archivo_programa.close() def correr_programa(self): self.guardar_programa() comando_y_argumentos = ['python', self.ruta_principal] call(comando_y_argumentos) '''print self.ruta_principal os.system('python '+self.ruta_principal)''' def agregar_widget(self): self.pilas = pilasengine.iniciar() widget_de_pilas = self.pilas.obtener_widget() self.canvas.addWidget(widget_de_pilas) self.canvas.setCurrentWidget(widget_de_pilas) widget_de_pilas.show() @staticmethod def agregar_elemento_arbol(elemento,arbol): item = QtGui.QTreeWidgetItem(arbol) item.setText(0,elemento) def cerrar_ventana_nuevo_proyecto(self): QtGui.QMessageBox.about(self, "Error", "Debe registrar los datos completos"); def llenar_fondos(self): self.listaFondos.clear() fondos = ['Volley','Nubes','Pasto','Selva','Tarde','Espacio','Noche'] for fond in fondos: item = QListWidgetItem(fond) self.listaFondos.addItem(item) def tipo_fondo(self, objeto): actor = None if objeto=='Volley': actor = self.pilas.fondos.Volley() elif objeto=='Nubes': actor = self.pilas.fondos.Nubes() elif objeto=='Pasto': actor = self.pilas.fondos.Pasto() elif objeto=='Selva': actor = self.pilas.fondos.Selva() elif objeto=='Tarde': actor = self.pilas.fondos.Tarde() elif objeto=='Espacio': actor = self.pilas.fondos.Espacio() elif objeto=='Noche': actor = self.pilas.fondos.Noche() return actor def crear_fondo(self): objeto=str(self.listaFondos.currentItem().text()) fondo = self.tipo_fondo(objeto) cont = 1 nombre = objeto+str(cont) while self.fondos.has_key(nombre): cont = cont+1 nombre = objeto+str(cont) self.fondos[nombre]=fondo self.agregar_objeto_arbol(nombre) self.taPrograma.append("Se creo el objeto: "+nombre) def seleccionar_objeto(self): nombre=str(self.arbolObjetos.currentItem().text(0)) objeto = self.objetos[nombre] self.twCuerpo.setEnabled(True) self.sbX.setValue(objeto.x) self.sbY.setValue(objeto.y) self.sbEscala.setValue(objeto.escala) self.sbRotacion.setValue(objeto.rotacion) def obtener_metodos(self,nombre_objeto, objeto, menu, item): metodos = type(objeto).mro()[0].__dict__ for metodo in metodos.keys(): if metodo[0]<>'_' and metodo<>'iniciar': parametros = inspect.getargspec(metodos[metodo]) men_agregar_metodo = menu.addAction(self.tr(metodo)) men_agregar_metodo.triggered.connect(partial(self.llamar_metodo,nombre_metodo=str(metodo),nombre_objeto=nombre_objeto,parametros=parametros[0])) men_agregar_metodo_eliminar = menu.addAction('eliminar') men_agregar_metodo_eliminar.triggered.connect(partial(self.eliminar_objeto,nombre_objeto=nombre_objeto,item=item)) def eliminar_objeto(self,nombre_objeto,item): self.objetos[nombre_objeto].eliminar() del self.objetos[nombre_objeto] for metodo in self.metodos: if metodo.find(nombre_objeto)>=0: self.metodos.remove(metodo) self.arbolObjetos.clear() for objeto in self.objetos.keys(): self.agregar_elemento_arbol(objeto,self.arbolObjetos) self.taPrograma.append(u"Se eliminó el objeto: "+nombre_objeto) def llamar_metodo(self, nombre_metodo, nombre_objeto,parametros): cadena = nombre_objeto+'.'+nombre_metodo+'(' parametros_valor = [] if len(parametros)>1: for parametro in parametros: if parametro<>'self': valor_parametro, ok = QtGui.QInputDialog.getText(self, 'Ingrese Parámetro',str(parametro)) cad_param_valor = str(parametro)+'='+str(valor_parametro) cadena = cadena+cad_param_valor parametros_valor.append(cad_param_valor) cadena = cadena+')' self.metodos.append(cadena) self.taPrograma.append(u"Se llamó al método: "+nombre_metodo) def cambiar_coord_x(self): objeto=str(self.arbolObjetos.currentItem().text(0)) self.objetos[objeto].x = self.sbX.value() def cambiar_coord_y(self): objeto=str(self.arbolObjetos.currentItem().text(0)) self.objetos[objeto].y = self.sbY.value() def cambiar_escala(self): objeto=str(self.arbolObjetos.currentItem().text(0)) self.objetos[objeto].escala = self.sbEscala.value() def cambiar_rotacion(self): objeto=str(self.arbolObjetos.currentItem().text(0)) self.objetos[objeto].rotacion = self.sbRotacion.value() '''def seleccionar_metodo(self):
class ExplorerTreeWidget(DirView): def __init__(self, parent=None): DirView.__init__(self, parent) def setup(self, path=None, name_filters=['*.py', '*.pyw'], valid_types= ('.py', '.pyw'), show_all=False): self.name_filters = name_filters self.valid_types = valid_types self.show_all = show_all self.refresh(path) # Enable drag events self.setDragEnabled(True) # Setup context menu self.menu = QMenu(self) self.common_actions = self.setup_common_actions() #---- Context menu def setup_common_actions(self): """Setup context menu common actions""" # Filters filters_action = create_action(self, translate('Explorer', "Edit filename filters..."), None, get_icon('filter.png'), triggered=self.edit_filter) # Show all files all_action = create_action(self, translate('Explorer', "Show all files"), toggled=self.toggle_all) all_action.setChecked(self.show_all) self.toggle_all(self.show_all) return [filters_action, all_action] def edit_filter(self): """Edit name filters""" filters, valid = QInputDialog.getText(self, translate('Explorer', 'Edit filename filters'), translate('Explorer', 'Name filters:'), QLineEdit.Normal, ", ".join(self.name_filters)) if valid: filters = [f.strip() for f in unicode(filters).split(',')] self.parent_widget.emit(SIGNAL('option_changed'), 'name_filters', filters) self.set_name_filters(filters) def toggle_all(self, checked): """Toggle all files mode""" self.parent_widget.emit(SIGNAL('option_changed'), 'show_all', checked) self.show_all = checked self.set_show_all(checked) def update_menu(self): """Update option menu""" self.menu.clear() actions = [] newdir_action = create_action(self, translate('Explorer', "New folder..."), icon="folder_new.png", triggered=self.new_folder) actions.append(newdir_action) newfile_action = create_action(self, translate('Explorer', "New file..."), icon="filenew.png", triggered=self.new_file) actions.append(newfile_action) fname = self.get_filename() if fname is not None: is_dir = osp.isdir(fname) ext = osp.splitext(fname)[1] run_action = create_action(self, translate('Explorer', "Run"), icon="run_small.png", triggered=self.run) edit_action = create_action(self, translate('Explorer', "Edit"), icon="edit.png", triggered=self.clicked) delete_action = create_action(self, translate('Explorer', "Delete..."), icon="delete.png", triggered=self.delete) rename_action = create_action(self, translate('Explorer', "Rename..."), icon="rename.png", triggered=self.rename) browse_action = create_action(self, translate('Explorer', "Browse"), icon=get_std_icon("CommandLink"), triggered=self.clicked) open_action = create_action(self, translate('Explorer', "Open"), triggered=self.startfile) if ext in ('.py', '.pyw'): actions.append(run_action) if ext in self.valid_types or os.name != 'nt': actions.append(browse_action if is_dir else edit_action) else: actions.append(open_action) actions += [delete_action, rename_action] if is_dir and os.name == 'nt': # Actions specific to Windows directories actions.append( create_action(self, translate('Explorer', "Open in Windows Explorer"), icon="magnifier.png", triggered=self.startfile) ) if os.name == 'nt': actions.append( create_action(self, translate('Explorer', "Open command prompt here"), icon="cmdprompt.png", triggered=lambda cmd='cmd.exe': os.startfile(cmd)) ) if actions: actions.append(None) actions += self.common_actions add_actions(self.menu, actions) #---- Refreshing widget def refresh(self, new_path=None, force_current=False): """ Refresh widget force=False: won't refresh widget if path has not changed """ if new_path is None: new_path = os.getcwdu() self.set_folder(new_path, force_current=force_current) #---- Events def contextMenuEvent(self, event): """Override Qt method""" self.update_menu() self.menu.popup(event.globalPos()) def keyPressEvent(self, event): """Reimplement Qt method""" if event.key() in (Qt.Key_Enter, Qt.Key_Return): self.clicked() event.accept() elif event.key() == Qt.Key_F2: self.rename() event.accept() else: DirView.keyPressEvent(self, event) def mouseDoubleClickEvent(self, event): """Reimplement Qt method""" QTreeView.mouseDoubleClickEvent(self, event) self.clicked() #---- Drag def dragEnterEvent(self, event): """Drag and Drop - Enter event""" event.setAccepted(event.mimeData().hasFormat("text/plain")) def dragMoveEvent(self, event): """Drag and Drop - Move event""" if (event.mimeData().hasFormat("text/plain")): event.setDropAction(Qt.MoveAction) event.accept() else: event.ignore() def startDrag(self, dropActions): """Reimplement Qt Method - handle drag event""" mimeData = QMimeData() mimeData.setText(self.get_filename()) drag = QDrag(self) drag.setMimeData(mimeData) drag.exec_() #---- Files/Directories Actions def get_filename(self): """Return selected filename""" index = self.currentIndex() if index: return osp.normpath(unicode(self.model().filePath(index))) def get_dirname(self): """ Return selected directory path or selected filename's directory path """ fname = self.get_filename() if osp.isdir(fname): return fname else: return osp.dirname(fname) def clicked(self): """Selected item was double-clicked or enter/return was pressed""" fname = self.get_filename() if fname: if osp.isdir(fname): self.parent_widget.emit(SIGNAL("open_dir(QString)"), fname) self.refresh() else: self.open(fname) def open(self, fname): """Open filename with the appropriate application""" fname = unicode(fname) ext = osp.splitext(fname)[1] if ext in self.valid_types: self.parent_widget.emit(SIGNAL("open_file(QString)"), fname) else: self.startfile(fname) def startfile(self, fname=None): """Windows only: open file in the associated application""" if fname is None: fname = self.get_filename() emit = False if os.name == 'nt': try: os.startfile(fname) except WindowsError: emit = True else: emit = True if emit: self.parent_widget.emit(SIGNAL("edit(QString)"), fname) def run(self): """Run Python script""" self.parent_widget.emit(SIGNAL("run(QString)"), self.get_filename()) def delete(self): """Delete selected item""" fname = self.get_filename() if fname: answer = QMessageBox.warning(self, translate("Explorer", "Delete"), translate("Explorer", "Do you really want to delete <b>%1</b>?") \ .arg(osp.basename(fname)), QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.No: return try: if osp.isfile(fname): os.remove(fname) else: os.rmdir(fname) self.parent_widget.emit(SIGNAL("removed(QString)"), fname) except EnvironmentError, error: QMessageBox.critical(self, translate('Explorer', "Delete"), translate('Explorer', "<b>Unable to delete selected file</b>" "<br><br>Error message:<br>%1") \ .arg(str(error))) finally:
class projectInfoWidget(infoWidget): __master_url = "" __projectCached = None __mainLayout = None __projectInfo = None __projectInfoLayout = None __projectSettings = None #informacie o projekte __masterUrlLabel = None __masterUrlText = None __projectNameLabel = None __projectNameText = None __userNameLabel = None __userNameText = None __teamNameLabel = None __teamNameText = None __userTotalCreditLabel = None __userTotalCreditText = None __hostTotalCreditLabel = None __hostTotalCreditText = None __suspendedLabel = None __newTasksLabel = None __projectLinksButton = None __projectSettingsMenu = None def __init__(self, client, project, parent = None): infoWidget.__init__(self, client, parent) self.__mainLayout = QGridLayout() self.__mainLayout.setRowStretch(1, 1) self.setMainLayout(self.__mainLayout) self.__projectInfo = QGroupBox(self.tr("Project Info")); self.__projectInfoLayout = QGridLayout() self.__projectInfo.setLayout(self.__projectInfoLayout) self.__projectSettings = QHBoxLayout() self.__projectLinksButton = QToolButton() self.__projectLinksButton.setText(self.tr("Project Links")) self.__projectLinksButton.hide() self.__updateProjectButton = QPushButton(self.tr("Update")) self.__suspendProjectButton = QPushButton() self.__allowNewTasksButton = QPushButton() self.__resetProjectButton = QPushButton(self.tr("Reset project")) self.__detachProjectButton = QPushButton(self.tr("Detach project")) self.__resetProjectButton.setEnabled(False) self.__detachProjectButton.setEnabled(False) self.__updateProjectButton.hide() self.__suspendProjectButton.hide() self.__allowNewTasksButton.hide() self.__resetProjectButton.hide() self.__detachProjectButton.hide() self.connect(self.__updateProjectButton, SIGNAL('clicked()'), self.__updateProject) self.connect(self.__suspendProjectButton, SIGNAL('clicked()'), self.__suspendProject) self.connect(self.__allowNewTasksButton, SIGNAL('clicked()'), self.__allowNewTasksProject) self.__projectSettings.addWidget(self.__projectLinksButton) self.__projectSettings.addWidget(self.__updateProjectButton) self.__projectSettings.addWidget(self.__suspendProjectButton) self.__projectSettings.addWidget(self.__allowNewTasksButton) #self.__projectSettings.addStretch(1) self.__projectAdmin = QHBoxLayout() self.__projectAdmin.addWidget(self.__resetProjectButton) self.__projectAdmin.addWidget(self.__detachProjectButton) #self.__projectAdmin.addStretch(1) self.__mainLayout.addWidget(self.__projectInfo, 0, 0) self.__mainLayout.addLayout(self.__projectSettings, 2, 0) self.__mainLayout.addLayout(self.__projectAdmin, 3, 0) self.__masterUrlLabel = QLabel(self.tr("Master URL")) self.__projectNameLabel = QLabel(self.tr("Project Name")) self.__userNameLabel = QLabel(self.tr("User Name")) self.__teamNameLabel = QLabel(self.tr("Team Name")) self.__userTotalCreditLabel = QLabel(self.tr("Total User Credits")) self.__hostTotalCreditLabel = QLabel(self.tr("Total Host Credits")) self.__suspendedLabel = QLabel(self.tr("Suspended by user")) self.__newTasksLabel = QLabel(self.tr("Won't get new tasks")) self.__masterUrlText = QLabel() self.__projectNameText = QLabel() self.__userNameText = QLabel() self.__teamNameText = QLabel() self.__userTotalCreditText = QLabel() self.__hostTotalCreditText = QLabel() self.__masterUrlText.setTextFormat(Qt.PlainText) self.__projectNameText.setTextFormat(Qt.PlainText) self.__userNameText.setTextFormat(Qt.PlainText) self.__teamNameText.setTextFormat(Qt.PlainText) self.__userTotalCreditText.setTextFormat(Qt.PlainText) self.__hostTotalCreditText.setTextFormat(Qt.PlainText) self.__masterUrlLabel.hide() self.__projectNameLabel.hide() self.__userNameLabel.hide() self.__teamNameLabel.hide() self.__userTotalCreditLabel.hide() self.__hostTotalCreditLabel.hide() self.__masterUrlText.hide() self.__projectNameText.hide() self.__userNameText.hide() self.__teamNameText.hide() self.__userTotalCreditText.hide() self.__hostTotalCreditText.hide() self.__suspendedLabel.hide() self.__newTasksLabel.hide() self.__projectInfoLayout.addWidget(self.__masterUrlLabel, 0, 0) self.__projectInfoLayout.addWidget(self.__projectNameLabel, 1, 0) self.__projectInfoLayout.addWidget(self.__userNameLabel, 2, 0) self.__projectInfoLayout.addWidget(self.__teamNameLabel, 3, 0) self.__projectInfoLayout.addWidget(self.__userTotalCreditLabel, 4, 0) self.__projectInfoLayout.addWidget(self.__hostTotalCreditLabel, 5, 0) self.__projectInfoLayout.addWidget(self.__masterUrlText, 0, 1) self.__projectInfoLayout.addWidget(self.__projectNameText, 1, 1) self.__projectInfoLayout.addWidget(self.__userNameText, 2, 1) self.__projectInfoLayout.addWidget(self.__teamNameText, 3, 1) self.__projectInfoLayout.addWidget(self.__userTotalCreditText, 4, 1) self.__projectInfoLayout.addWidget(self.__hostTotalCreditText, 5, 1) self.__projectInfoLayout.addWidget(self.__suspendedLabel, 6, 0, 1, 2) self.__projectInfoLayout.addWidget(self.__newTasksLabel, 7, 0, 1, 2) self.__master_url = project.data(0, Qt.UserRole + 1).toString() self.__projectCached = None self.__projectSettingsMenu = QMenu() self.__projectLinksButton.setPopupMode(QToolButton.InstantPopup) self.__projectLinksButton.setMenu(self.__projectSettingsMenu) projects = client.projectState() if not projects is None: self.updateProjects(projects) self.connect(client, SIGNAL("projectState(PyQt_PyObject)"), self.updateProjects) self.connect(client, SIGNAL("projectUpdateRecv(PyQt_PyObject)"), self.__updateProjectRecv) self.connect(client, SIGNAL("projectSuspendRecv(PyQt_PyObject)"), self.__suspendProjectRecv) self.connect(client, SIGNAL("projectResumeRecv(PyQt_PyObject)"), self.__resumeProjectRecv) self.connect(client, SIGNAL("projectNomoreworkRecv(PyQt_PyObject)"), self.__nomoreworkProjectRecv) self.connect(client, SIGNAL("projectAllowmoreworkRecv(PyQt_PyObject)"), self.__allowmoreworkProjectRecv) def __updateProject(self): self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Updating project")) self.client().projectUpdate(self.__projectCached['master_url']) def __suspendProject(self): if self.__projectCached['suspended_via_gui']: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Resuming project")) self.client().projectResume(self.__projectCached['master_url']) else: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Suspending project")) self.client().projectSuspend(self.__projectCached['master_url']) def __allowNewTasksProject(self): if self.__projectCached['dont_request_more_work']: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Allowing new tasks")) self.client().projectAllowmorework(self.__projectCached['master_url']) else: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Disallowing new tasks")) self.client().projectNomorework(self.__projectCached['master_url']) def __updateProjectRecv(self, status): if status: self.emit(SIGNAL("showStatusBarMsg(QString)"), status) else: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Project updated")) self.client().getState() def __suspendProjectRecv(self, status): if status: self.emit(SIGNAL("showStatusBarMsg(QString)"), status) else: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Project suspended")) self.__projectCached['suspended_via_gui'] = 1 self.__suspendedLabel.show() self.__suspendProjectButton.setText(self.tr("Resume")) self.client().getState() def __resumeProjectRecv(self, status): if status: self.emit(SIGNAL("showStatusBarMsg(QString)"), status) else: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("Project restored")) self.__projectCached['suspended_via_gui'] = 0 self.__suspendedLabel.hide() self.__suspendProjectButton.setText(self.tr("Suspend")) self.client().getState() def __nomoreworkProjectRecv(self, status): if status: self.emit(SIGNAL("showStatusBarMsg(QString)"), status) else: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("New tasks disallowed")) self.__projectCached['dont_request_more_work'] = 1 self.__newTasksLabel.show() self.__allowNewTasksButton.setText(self.tr("Allow new tasks")) self.client().getState() def __allowmoreworkProjectRecv(self, status): if status: self.emit(SIGNAL("showStatusBarMsg(QString)"), status) else: self.emit(SIGNAL("showStatusBarMsg(QString)"), self.tr("New tasks allowed")) self.__projectCached['dont_request_more_work'] = 0 self.__newTasksLabel.hide() self.__allowNewTasksButton.setText(self.tr("No new tasks")) self.client().getState() def __changeLabels(self, project, key, label, text): try: inf = project[key] if type(inf) == type(u""): text.setText(inf) text.show() label.show() except KeyError: text.hide() label.hide() def updateProjects(self, projects): projects = projects['project'] project = None for proj in projects: if proj['master_url'] == self.__master_url: project = proj break # ak sme nenasli projekt if project is None: return if project != self.__projectCached: self.__projectCached = project try: self.setTitle(titleFrame(project['project_name'])) except KeyError: pass self.__changeLabels(project, 'master_url', self.__masterUrlLabel, self.__masterUrlText) self.__changeLabels(project, 'project_name', self.__projectNameLabel, self.__projectNameText) self.__changeLabels(project, 'user_name', self.__userNameLabel, self.__userNameText) self.__changeLabels(project, 'team_name', self.__teamNameLabel, self.__teamNameText) self.__changeLabels(project, 'user_total_credit', self.__userTotalCreditLabel, self.__userTotalCreditText) self.__changeLabels(project, 'host_total_credit', self.__hostTotalCreditLabel, self.__hostTotalCreditText) if project['suspended_via_gui']: self.__suspendProjectButton.setText(self.tr("Resume")) self.__suspendedLabel.show() else: self.__suspendProjectButton.setText(self.tr("Suspend")) self.__suspendedLabel.hide() if project['dont_request_more_work']: self.__allowNewTasksButton.setText(self.tr("Allow new tasks")) self.__newTasksLabel.show() else: self.__allowNewTasksButton.setText(self.tr("No new tasks")) self.__newTasksLabel.hide() self.__updateProjectButton.show() self.__suspendProjectButton.show() self.__allowNewTasksButton.show() self.__resetProjectButton.show() self.__detachProjectButton.show() try: self.__projectSettingsMenu.clear() guiUrls = project['gui_urls']['gui_url'] if type(guiUrls) == type({}): self.__projectSettingsMenu.addAction(urlAction(guiUrls['url'], guiUrls['name'], guiUrls['description'], self.__projectSettingsMenu)) else: for url in guiUrls: self.__projectSettingsMenu.addAction(urlAction(url['url'], url['name'], url['description'], self.__projectSettingsMenu)) try: ifTeamUrls = project['gui_urls']['ifteam']['gui_url'] self.__projectSettingsMenu.addSeparator() if type(ifTeamUrls) == type({}): self.__projectSettingsMenu.addAction(urlAction(ifTeamUrls['url'], ifTeamUrls['name'], ifTeamUrls['description'], self.__projectSettingsMenu)) else: for url in ifTeamUrls: self.__projectSettingsMenu.addAction(urlAction(url['url'], url['name'], url['description'], self.__projectSettingsMenu)) except KeyError, msg: pass self.__projectLinksButton.show() except KeyError: self.__projectLinksButton.hide()
class BrowserView(QWidget): """Luma LDAP Browser plugin """ # Custom signals used reloadSignal = QtCore.pyqtSignal(QtCore.QModelIndex) clearSignal = QtCore.pyqtSignal(QtCore.QModelIndex) __logger = logging.getLogger(__name__) def __init__(self, parent=None, configPrefix=None): """ :param configPrefix: defines the location of serverlist. :type configPrefix: string """ super(BrowserView, self).__init__(parent) self.__logger = logging.getLogger(__name__) self.setObjectName("PLUGIN_BROWSER") self.templateList = TemplateList() # The serverlist used self.serverList = ServerList(configPrefix) self.serversChangedMessage = QtGui.QErrorMessage() self.mainLayout = QtGui.QHBoxLayout(self) self.splitter = QtGui.QSplitter(self) # Create the model self.ldaptreemodel = LDAPTreeItemModel(self.serverList, self) self.ldaptreemodel.workingSignal.connect(self.setBusy) # Set up the entrylist (uses the model) self.__setupEntryList() # The editor for entries self.tabWidget = QtGui.QTabWidget(self) #self.tabWidget.setDocumentMode(True) self.tabWidget.setMovable(True) self.setMinimumWidth(200) self.tabWidget.setTabsClosable(True) self.tabWidget.tabCloseRequested.connect(self.tabCloseClicked) self.tabWidget.setUsesScrollButtons(True) sizePolicy = self.tabWidget.sizePolicy() sizePolicy.setHorizontalStretch(1) self.tabWidget.setSizePolicy(sizePolicy) # Remember and looks up open tabs self.openTabs = {} self.splitter.addWidget(self.entryList) self.splitter.addWidget(self.tabWidget) self.mainLayout.addWidget(self.splitter) # Used to signal the ldaptreemodel with a index # which needs processing (reloading, clearing) self.reloadSignal.connect(self.ldaptreemodel.reloadItem) self.clearSignal.connect(self.ldaptreemodel.clearItem) eventFilter = BrowserPluginEventFilter(self) self.installEventFilter(eventFilter) self.__createContextMenu() self.retranslateUi() self.progress = QMessageBox( 1, self.str_PLEASE_WAIT, self.str_PLEASE_WAIT_MSG, QMessageBox.Ignore, parent=self ) # For testing ONLY # AND ONLY ON SMALL LDAP-SERVERS SINCE IT LOADS BASICALLY ALL ENTIRES #import modeltest #self.modeltest = modeltest.ModelTest(self.ldaptreemodel, self); def setBusy(self, status): """ Helper-method. """ if status == True: self.progress.show() qApp.setOverrideCursor(Qt.WaitCursor) else: if not self.progress.isHidden(): self.progress.hide() qApp.restoreOverrideCursor() def __setupEntryList(self): # The view for server-content self.entryList = QtGui.QTreeView(self) self.entryList.setMinimumWidth(200) #self.entryList.setMaximumWidth(400) #self.entryList.setAlternatingRowColors(True) # Somewhat cool, but should be removed if deemed too taxing self.entryList.setAnimated(True) self.entryList.setUniformRowHeights(True) # MAJOR optimalization #self.entryList.setExpandsOnDoubleClick(False) self.entryList.setModel(self.ldaptreemodel) self.entryList.setMouseTracking(True) self.entryList.viewport().setMouseTracking(True) # For right-clicking in the tree self.entryList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.entryList.customContextMenuRequested.connect(self.rightClick) # When something is activated (doubleclick, <enter> etc.) self.entryList.activated.connect(self.viewItem) self.delegate = LoadingDelegate(self.entryList) self.entryList.setItemDelegate(self.delegate) self.entryList.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) def __createContextMenu(self): """Creates the context menu for the tree view. """ self.contextMenu = QMenu() self.contextMenuServerSettings = QAction(self) self.contextMenu.addAction(self.contextMenuServerSettings) self.contextMenu.addSeparator() self.contextMenuOpen = QAction(self) self.contextMenu.addAction(self.contextMenuOpen) self.contextMenuReload = QAction(self) self.contextMenu.addAction(self.contextMenuReload) self.contextMenuClear = QAction(self) self.contextMenu.addAction(self.contextMenuClear) self.contextMenuFilter = QAction(self) self.contextMenu.addAction(self.contextMenuFilter) self.contextMenuLimit = QAction(self) self.contextMenu.addAction(self.contextMenuLimit) self.contextMenu.addSeparator() self.contextMenuAdd = QMenu() self.contextMenu.addMenu(self.contextMenuAdd) self.contextMenuDelete = QMenu() self.contextMenu.addMenu(self.contextMenuDelete) self.contextMenuExport = QMenu() self.contextMenu.addMenu(self.contextMenuExport) # Connect the context menu actions to the correct slots self.contextMenuServerSettings.triggered.connect( self.editServerSettings) self.contextMenuOpen.triggered.connect(self.openChoosen) self.contextMenuReload.triggered.connect(self.reloadChoosen) self.contextMenuClear.triggered.connect(self.clearChoosen) self.contextMenuFilter.triggered.connect(self.filterChoosen) self.contextMenuLimit.triggered.connect(self.limitChoosen) def rightClick(self, point): """ Called when the view is right-clicked. Displays a context menu with possible actions. :param point: contains the global screen coordinates for the right-click that generated this call. :type potin: QPoint """ # This is a list of QModelIndex objects, which will be used by # the various context menu slots. # We therfore store it as a class member self.selection = self.entryList.selectedIndexes() openSupport = True reloadSupport = True clearSupport = True filterSupport = True limitSupport = True addSupport = True deleteSupport = True exportSupport = True editServerSupport = True # The number of selected items is used for naming of the actions # added to the submenues numselected = len(self.selection) # View disabled menu if nothing selected self.contextMenu.setEnabled(True) # Remember to enable if a selection if not numselected > 0: # If nothing is selected self.contextMenu.setEnabled(False) # Disable self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # Show return # Iterate through the list of selected indexes, and # validate what operations are supported. That is, # if one of the selected indexes do not support an # operation, we cannot allow to apply that operation # on the whole selection for index in self.selection: item = index.internalPointer() operations = item.getSupportedOperations() if not AbstractLDAPTreeItem.SUPPORT_OPEN & operations: openSupport = False if not AbstractLDAPTreeItem.SUPPORT_RELOAD & operations: reloadSupport = False if not AbstractLDAPTreeItem.SUPPORT_CLEAR & operations: clearSupport = False if not AbstractLDAPTreeItem.SUPPORT_FILTER & operations: filterSupport = False if not AbstractLDAPTreeItem.SUPPORT_LIMIT & operations: limitSupport = False if not AbstractLDAPTreeItem.SUPPORT_ADD & operations: addSupport = False if not AbstractLDAPTreeItem.SUPPORT_DELETE & operations: deleteSupport = False if not AbstractLDAPTreeItem.SUPPORT_EXPORT & operations: exportSupport = False if index.internalPointer().getParentServerItem() == None: editServerSupport = False # Now we just use the *Support variables to enable|disable # the context menu actions. self.contextMenuOpen.setEnabled(openSupport) self.contextMenuReload.setEnabled(reloadSupport) self.contextMenuClear.setEnabled(clearSupport) self.contextMenuFilter.setEnabled(filterSupport) self.contextMenuLimit.setEnabled(limitSupport) self.contextMenuServerSettings.setEnabled(editServerSupport) # For the submenues in the context menu, we add appropriate # actions, based on single|multi selection, or disable the menu # altogether if there is no support for the operation. if (limitSupport or filterSupport or openSupport) \ and not numselected == 1: self.contextMenuLimit.setEnabled(False) self.contextMenuFilter.setEnabled(False) self.contextMenuOpen.setEnabled(False) if addSupport and numselected == 1: self.contextMenuAdd.setEnabled(True) # template templateMenu = QMenu(self.str_TEMPLATE) self.contextMenuAdd.addMenu(templateMenu) index = self.selection[0] for template in self.templateList.getTable(): sO = index.internalPointer().smartObject() if template.server == sO.serverMeta.name: method = lambda name = template.templateName, i = index : self.addTemplateChoosen(name, i) templateMenu.addAction(template.templateName, method) else: self.contextMenuAdd.setEnabled(False) if numselected != 1: self.contextMenuServerSettings.setEnabled(False) if deleteSupport: self.contextMenuDelete.setEnabled(True) if numselected == 1: self.contextMenuDelete.addAction( self.str_ITEM, self.deleteSelection ) self.contextMenuDelete.addAction( self.str_SUBTREE_ONLY, self.deleteSubtree ) #self.contextMenuDelete.addAction( # self.str_SUBTREE_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.addAction( self.str_ITEMS, self.deleteSelection ) self.contextMenuDelete.addAction( self.str_SUBTREES, self.deleteSubtree ) #self.contextMenuDelete.addAction( # self.str_SUBTREES_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.setEnabled(False) if exportSupport: self.contextMenuExport.setEnabled(True) if numselected == 1: self.contextMenuExport.addAction( self.str_ITEM, self.exportItems ) self.contextMenuExport.addAction( self.str_SUBTREE, self.exportSubtrees ) self.contextMenuExport.addAction( self.str_SUBTREE_PARENTS, self.exportSubtreeWithParents ) else: self.contextMenuExport.addAction( self.str_ITEMS, self.exportItems ) self.contextMenuExport.addAction( self.str_SUBTREES, self.exportSubtrees ) self.contextMenuExport.addAction( self.str_SUBTREES_PARENTS, self.exportSubtreeWithParents ) else: self.contextMenuExport.setEnabled(False) # Finally we execute the context menu self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # We need to clear all the submenues after each right click # selection, if not; the submenu actions will be added and # thus duplicated for every selection the user makes. # FIXME: Find a better way of handling this issue. self.contextMenuAdd.clear() self.contextMenuDelete.clear() self.contextMenuExport.clear() """ Following methods are called from a context-menu. """ def openChoosen(self): if len(self.selection) == 1: self.viewItem(self.selection[0]) def reloadChoosen(self): for index in self.selection: self.reloadSignal.emit(index) def clearChoosen(self): for index in self.selection: self.clearSignal.emit(index) def limitChoosen(self): # Have the item set the limit for us, the reload for index in self.selection: ok = index.internalPointer().setLimit() if ok: self.reloadSignal.emit(index) def filterChoosen(self): # Have the item set the filter, then reload for index in self.selection: ok = index.internalPointer().setFilter() if ok: self.reloadSignal.emit(index) def addTemplateChoosen(self, templateName, index): serverMeta = index.internalPointer().smartObject().serverMeta baseDN = index.internalPointer().smartObject().getDN() template = self.templateList.getTemplateObject(templateName) smartO = template.getDataObject(serverMeta, baseDN) self.addNewEntry(index, smartO, template) def addNewEntry(self, parentIndex, defaultSmartObject=None, template=None): tmp = NewEntryDialog(parentIndex, defaultSmartObject, entryTemplate=template) if tmp.exec_(): ret = QMessageBox.question(self, QtCore.QCoreApplication.translate("BrowserView","Add"), QtCore.QCoreApplication.translate("BrowserView", "Do you want to reload to show the changes?"), QMessageBox.Yes|QMessageBox.No) if ret == QMessageBox.Yes: self.ldaptreemodel.reloadItem(self.selection[0]) """ Utility-methods """ def isOpen(self, smartObject): rep = self.getRepForSmartObject(smartObject) # The {}.has_key() method will be removed in the future version # of Python. Use the 'in' operation instead. [PEP8] #if self.openTabs.has_key(str(rep)): if str(rep) in self.openTabs: return True else: return False def getRepForSmartObject(self, smartObject): serverName = smartObject.getServerAlias() dn = smartObject.getDN() return (serverName, dn) def viewItem(self, index): """Opens items for viewing. """ item = index.internalPointer() supports = item.getSupportedOperations() # If we can't open this item, then don't if not supports & AbstractLDAPTreeItem.SUPPORT_OPEN: self.__logger.debug("Item didn't support open.") return smartObject = index.internalPointer().smartObject() rep = self.getRepForSmartObject(smartObject) # If the smartobject is already open, switch to it if self.isOpen(smartObject): x = self.openTabs[str(rep)] self.tabWidget.setCurrentWidget(x) return # Saves a representation of the opened entry to avoid opening duplicates # and open it x = AdvancedObjectWidget(QtCore.QPersistentModelIndex(index)) x.initModel(smartObject) self.openTabs[str(rep)] = x self.tabWidget.addTab(x, smartObject.getPrettyRDN()) self.tabWidget.setCurrentWidget(x) def deleteIndex(self, index): # Remember the smartObject for later sO = index.internalPointer().smartObject() # Try to delete (success, message) = self.ldaptreemodel.deleteItem(index) if success: # Close open edit-windows if any self.__closeTabIfOpen(sO) # Notify success return (True, message) else: # Notify fail return (False, message) def __closeTabIfOpen(self, sO): if self.isOpen(sO): rep = self.getRepForSmartObject(sO) x = self.openTabs.pop(str(rep)) i = self.tabWidget.indexOf(x) if i != -1: self.tabWidget.removeTab(i) def deleteSelection(self, subTree=False): """Slot for the context menu. Opens the DeleteDialog with the selected entries, giving the user the option to validate the selection before deleting. This is for deleting the item + possibly it's subtree. See deleteOnlySubtreeOfSelection() for only subtree. """ # Only a single item if len(self.selection) == 1 and not subTree: # Confirmation-message ok = QMessageBox.question( self, self.str_DELETE, self.str_REALLY_DELETE, QMessageBox.Yes | QMessageBox.No ) if ok == QMessageBox.No: return index = self.selection[0] (status, message) = self.deleteIndex(index) if not status: QMessageBox.critical( self, self.str_ERROR, self.str_ERROR_MSG.format( index.data().toPyObject(), message ) ) return # Make persistent indexes and list of smartObjects to be deleted persistenSelection = [] sOList = [] for x in self.selection: persistenSelection.append(QPersistentModelIndex(x)) sOList.append(x.internalPointer().smartObject()) # Create gui self.setBusy(True) deleteDialog = DeleteDialog(sOList, subTree) self.setBusy(False) status = deleteDialog.exec_() if status: # the dialog was not canceled if subTree: # Reload the items whos subtree was deleted for x in self.selection: self.ldaptreemodel.reloadItem(x) return # If all rows were removed successfully, just call # removeRows on all selected items (reloading all items of # the parent can be expensive) if deleteDialog.passedItemsWasDeleted: for x in persistenSelection: if x.isValid: i = x.sibling(x.row(), 0) # QModelIndex self.__closeTabIfOpen( i.internalPointer().smartObject()) self.ldaptreemodel.removeRow(x.row(), x.parent()) return # If not, call reload on the parent of all the items? else: tmp = QMessageBox.question( self, self.str_DELETION, self.str_DELETION_MSG, buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.Yes ) if tmp == QMessageBox.Yes: for x in persistenSelection: # index might not be valid if the parent was # reloaded by a previous item if x.isValid(): self.ldaptreemodel.reloadItem(x.parent()) return # Was cancelled so do nothing else: pass def deleteSubtree(self): self.deleteSelection(subTree=True) def exportItems(self): """Slot for the context menu. """ self.__exportSelection(scope=0) def exportSubtrees(self): """Slot for the context menu. """ self.__exportSelection(scope=1) def exportSubtreeWithParents(self): """Slot for the context menu. """ self.__exportSelection(scope=2) def __exportSelection(self, scope=0): """Slot for the context menu. Opens the ExportDialog with the selected entries, giving the user the option to validate the selection before exporting. :param scope: The scope selection. 0 = SCOPE_BASE -> Item(s), 1 = SCOPE_ONELEVEL -> Subtree(s); 2 = SCOPE_SUBTREE -> Subtree(s) with parent :type scope: int """ exportObjects = [] msg = '' self.setBusy(True) for index in self.selection: smartObject = index.internalPointer().smartObject() serverName = smartObject.getServerAlias() dn = smartObject.getDN() serverObject = self.serverList.getServerObject(serverName) con = LumaConnectionWrapper(serverObject, self) # For both subtree and subtree with parent, we fetch the # whole subtree including the parent, with a basic sync # search operation. Then, if only the subtree is to be # exported, we remove the smartObject(s) selected. if scope > 0: pass # Do a search on the whole subtree # 2 = ldap.SCOPE_SUBTREE #elif scope == 2: success, e = con.bindSync() if not success: self.__logger.error(str(e)) continue success, result, e = con.searchSync(base=dn, scope=2) if success: exportObjects.extend(result) else: self.__logger.error(str(e)) # If only the subtree is to be selected, we remove # the parent, which happens to be the smartObject(s) # initialy selected. if scope == 1: exportObjects.remove(smartObject) # For scope == 0 we need not do any LDAP search operation # because we already got what we need else: exportObjects.append(smartObject) # Initialize the export dialog # and give it the items for export dialog = ExportDialog(msg) dialog.setExportItems(exportObjects) self.setBusy(False) dialog.exec_() def editServerSettings(self): """Slot for the context menu. Opens the ServerDialog with the selected server. """ try: items = self.selection serverItem = items[0].internalPointer().getParentServerItem() serverName = serverItem.serverMeta.name serverDialog = ServerDialog(serverName) r = serverDialog.exec_() if r: self.serversChangedMessage.showMessage( self.str_SERVER_CHANGED_MSG ) except Exception, e: self.__logger.error(str(e)) QMessageBox.information( self, self.str_ERROR, self.str_SEE_LOG_DETAILS )
class Dummy: instance = None def __init__(self, iface): from createunbeffl import application as createunbeffl from importdyna import application as importdyna from exportdyna import application as exportdyna from linkflaechen import application as linkflaechen from tools import application as tools self.plugins = [ createunbeffl.CreateUnbefFl(iface), importdyna.ImportFromDyna(iface), exportdyna.ExportToKP(iface), linkflaechen.LinkFl(iface), tools.QKanTools(iface) ] Dummy.instance = self # Plugins self.instances = [] # QGIS self.iface = iface self.plugin_dir = os.path.dirname(__file__) self.actions = [] actions = self.iface.mainWindow().menuBar().actions() self.menu = None for menu in actions: if menu.text() == 'QKan': self.menu = menu.menu() self.menu_action = menu break self.toolbar = self.iface.addToolBar('QKan') self.toolbar.setObjectName('QKan') def initGui(self): # Create and insert QKan menu after the 3rd menu if self.menu is None: self.menu = QMenu('QKan', self.iface.mainWindow().menuBar()) actions = self.iface.mainWindow().menuBar().actions() prepend = actions[3] self.menu_action = self.iface.mainWindow().menuBar().insertMenu(prepend, self.menu) # Calls initGui on all known QKan plugins for plugin in self.plugins: plugin.initGui() self.sort_actions() def sort_actions(self): # Finally sort all actions self.actions.sort(key=lambda x: x.text().lower()) self.menu.clear() self.menu.addActions(self.actions) def unload(self): from qgis.utils import unloadPlugin # Unload all other instances for instance in self.instances: print('Unloading ', instance.name) if not unloadPlugin(instance.name): print('Failed to unload plugin!') # Remove entries from own menu for action in self.menu.actions(): self.menu.removeAction(action) # Remove entries from Plugin menu and toolbar for action in self.actions: self.iface.removeToolBarIcon(action) # Remove the toolbar del self.toolbar # Remove menu self.iface.mainWindow().menuBar().removeAction(self.menu_action) # Call unload on all loaded plugins for plugin in self.plugins: plugin.unload() def register(self, instance): self.instances.append(instance) self.plugins += instance.plugins def unregister(self, instance): self.instances.remove(instance) for plugin in instance.plugins: self.plugins.remove(plugin) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.__actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.menu.addAction(action) self.actions.append(action) return action
class QDataTableView(QTableView): """ This is an actual table of waves. """ def __init__(self, model=None, title="Table", *args): """ Initialize the view. model is the DataTableModel to use. title is the window title. """ QTableView.__init__(self, *args) self._app = QApplication.instance().window if model is None: model = DataTableModel() self.setModel(model) self.setWindowTitle(title) self.setAttribute(Qt.WA_DeleteOnClose) self.setSelectionMode(QAbstractItemView.ContiguousSelection) # contiguous instead of extended so that we can easily insert/delete cells more easily. See note below. self.setEditTriggers(QAbstractItemView.AnyKeyPressed | QAbstractItemView.SelectedClicked | QAbstractItemView.DoubleClicked) self.horizontalHeader().setMovable(True) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.showCellContextMenu) self.verticalHeader().setContextMenuPolicy(Qt.CustomContextMenu) self.verticalHeader().customContextMenuRequested.connect(self.showVerticalHeaderMenu) self.setupHorizontalHeaderMenu() def name(self): """Return the name of the table.""" return self.windowTitle() def renameWave(self, wave): """ Create a dialog box to rename a wave. When the dialog box is filled out and submitted, try to rename the wave. """ # Setup dialog box renameWaveDialog = QDialog(self) renameWaveUi = Ui_RenameWaveDialog() renameWaveUi.setupUi(renameWaveDialog) renameWaveUi.oldWaveName.setText(wave.name()) renameWaveDialog.setVisible(True) def saveRename(): """Save button pressed. Do work to save new name.""" newName = wave.validateWaveName(str(renameWaveUi.newWaveNameLineEdit.text())) if str(renameWaveUi.newWaveNameLineEdit.text()) == "": failedMessage = QMessageBox(renameWaveDialog) failedMessage.setText("Cannot use a blank name.") failedMessage.exec_() renameWaveDialog.setVisible(True) elif self._app.waves().goodWaveName(newName) and wave.setName(newName): renameWaveDialog.close() else: failedMessage = QMessageBox() failedMessage.setText("Unable to rename wave.") failedMessage.exec_() renameWaveDialog.setVisible(True) def cancelRename(): """Cancel button pressed.""" renameWaveDialog.close() # connect actions renameWaveUi.buttons.accepted.connect(saveRename) renameWaveUi.buttons.rejected.connect(cancelRename) def removeWave(self, visualIndex): self.model().removeColumn(visualIndex) def addWave(self, wave, visualIndex): if self.model().addColumn(wave): # The wave was added (i.e. it did not already exist in the table) # We need to move the newly added column from the end to where the user clicked self.horizontalHeader().moveSection(self.model().columnCount() - 1, visualIndex + 1) def insertCells(self): """Insert cells into waves based on selected cells in this table.""" # Sort by rows, so that we start at the top of the waves # This is the reason we only do contiguous selections, because # tracking which cells to insert into is really difficult otherwise selectedCells = self.selectedIndexes() selectedCells.sort(None, QModelIndex.row, False) # Disconnect the dataModified signal before changing anything, then connect # it after changing everything. This way, multiple calls are not emitted # in the middle, for no reason. # We create the waves list just in case the selectedCells indexes aren't valid # aftewards. waves = Util.uniqueList(map(lambda x: x.internalPointer(), selectedCells)) for wave in waves: wave.blockSignals(True) for cell in selectedCells: try: cell.internalPointer().insert(cell.row(), "") except: # The cell did not exist (i.e. the wave does not extend this far) # but another wave does, so do not fail completely pass for wave in waves: wave.blockSignals(False) wave.dataModified.emit() wave.lengthChanged.emit() def deleteCells(self): """Delete cells from waves based on selected cells in this table.""" # Sort by rows, inverted, so that we start by deleting at the # bottom of the waves, and don't screw up index values along # the way selectedCells = self.selectedIndexes() selectedCells.sort(None, QModelIndex.row, True) # Disconnect the dataModified signal before changing anything, then connect # it after changing everything. This way, multiple calls are not emitted # in the middle, for no reason. # We create the waves list just in case the selectedCells indexes aren't valid # aftewards. waves = Util.uniqueList(map(lambda x: x.internalPointer(), selectedCells)) for wave in waves: wave.blockSignals(True) for cell in selectedCells: try: cell.internalPointer().pop(cell.row()) except: # The cell did not exist (i.e. the wave does not extend this far) # but another wave does, so do not fail completely pass for wave in waves: wave.blockSignals(False) wave.dataModified.emit() wave.lengthChanged.emit() def showCellContextMenu(self, point): """Display the menu that occurs when right clicking on a table cell.""" clickedCell = self.indexAt(point) if not clickedCell.isValid(): # User clicked on a part of the table without a cell return False cellMenu = QMenu(self) insertCellAction = QAction("Insert Cells", cellMenu) deleteCellAction = QAction("Delete Cells", cellMenu) cellMenu.addAction(insertCellAction) cellMenu.addAction(deleteCellAction) # Connect signals insertCellAction.triggered.connect(self.insertCells) deleteCellAction.triggered.connect(self.deleteCells) # Display menu cellMenu.exec_(self.mapToGlobal(point)) # Disconnect signals insertCellAction.triggered.disconnect(self.insertCells) deleteCellAction.triggered.disconnect(self.deleteCells) def showVerticalHeaderMenu(self, point): """Display the menu that occurs when right clicking on a vertical header.""" rowMenu = QMenu(self) insertRowAction = QAction("Insert Rows", rowMenu) deleteRowAction = QAction("Delete Rows", rowMenu) rowMenu.addAction(insertRowAction) rowMenu.addAction(deleteRowAction) # Connect signals insertRowAction.triggered.connect(self.insertCells) deleteRowAction.triggered.connect(self.deleteCells) # Display menu rowMenu.exec_(self.mapToGlobal(point)) # Disconnect signals insertRowAction.triggered.disconnect(self.insertCells) deleteRowAction.triggered.disconnect(self.deleteCells) def setupHorizontalHeaderMenu(self): self.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu) self.horizontalHeader().customContextMenuRequested.connect(self.showHorizontalHeaderMenu) self.horizontalHeaderMenu = QMenu(self.horizontalHeader()) # Create actions self.renameWaveAction = QAction("Rename Wave", self.horizontalHeaderMenu) self.removeWaveAction = QAction("Remove Wave", self.horizontalHeaderMenu) self.addWaveMenu = QMenu("Add Wave", self.horizontalHeaderMenu) # Add actions to menu self.horizontalHeaderMenu.addAction(self.addWaveMenu.menuAction()) self.horizontalHeaderMenu.addAction(self.renameWaveAction) self.horizontalHeaderMenu.addAction(self.removeWaveAction) def showHorizontalHeaderMenu(self, point): """Display the menu that occurs when right clicking on a column header.""" logicalIndex = self.horizontalHeader().logicalIndexAt(point) visualIndex = self.horizontalHeader().visualIndex(logicalIndex) self.selectColumn(logicalIndex) #print "l: " + str(logicalIndex) #print "v: " + str(visualIndex) selectedWave = self.model().waves()[logicalIndex] # Create helper functions, defined for this specific menu location def renameWaveHelper(): self.renameWave(selectedWave) def removeWaveHelper(): #self.removeWave(selectedWave) self.removeWave(visualIndex) def addWaveHelper(wave): self.addWave(wave, visualIndex) def addNewWaveHelper(): wave = Wave(self._app.waves().findGoodWaveName()) self._app.waves().addWave(wave) self.addWave(wave, visualIndex) self.addWaveMenu.clear() # Add "New Wave" entry newWaveAction = QAction("New Wave", self.addWaveMenu) self.addWaveMenu.addAction(newWaveAction) newWaveAction.triggered.connect(addNewWaveHelper) # Get current list of waves for "add wave to table" menu #for wave in self._app.waves().waves().values(): for wave in self._app.model('appWaves').waves(): waveAction = AddWaveAction(wave, self.addWaveMenu) self.addWaveMenu.addAction(waveAction) waveAction.addWaveClicked.connect(addWaveHelper) # Connect actions self.renameWaveAction.triggered.connect(renameWaveHelper) self.removeWaveAction.triggered.connect(removeWaveHelper) self.horizontalHeaderMenu.exec_(self.mapToGlobal(point)) # Disconnect actions. We need to do this or else there will be multiple connections # when we open the menu again, and the old connections will have strange visualIndex values self.renameWaveAction.triggered.disconnect(renameWaveHelper) self.removeWaveAction.triggered.disconnect(removeWaveHelper) for waveAction in self.addWaveMenu.actions(): try: waveAction.addWaveClicked.disconnect(addWaveHelper) except: waveAction.triggered.disconnect(addNewWaveHelper) def reset(self): QTableView.reset(self) self.resizeRowsToContents() self.resizeColumnsToContents() def keyPressEvent(self, event): """Capture certain types of keypress events and handle them different ways.""" # When data has been edited, move to the next row in the column and continue editing. currentIndex = self.currentIndex() #print "row: " + str(currentIndex.row()) + ", col: " + str(currentIndex.column()) if currentIndex.isValid(): if self.state() == QAbstractItemView.EditingState: if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: Util.debug(3, "DataTableView.keyPressEvent", "Enter key pressed in table") newIndex = self.model().createIndex(currentIndex.row() + 1, currentIndex.column()) self.setCurrentIndex(newIndex) self.edit(newIndex) self.setCurrentIndex(newIndex) return elif event.key() == Qt.Key_Up: Util.debug(3, "DataTableView.keyPressEvent", "Up key pressed in table") newIndex = self.model().createIndex(currentIndex.row() - 1, currentIndex.column()) #print "nrow: " + str(newIndex.row()) + ", ncol: " + str(newIndex.column()) #self.setCurrentIndex(newIndex) self.setState(QAbstractItemView.NoState) elif event.key() == Qt.Key_Down: Util.debug(3, "DataTableView.keyPressEvent", "Down key pressed in table") newIndex = self.model().createIndex(currentIndex.row() + 1, currentIndex.column()) #print "nrow: " + str(newIndex.row()) + ", ncol: " + str(newIndex.column()) #self.setCurrentIndex(newIndex) self.setState(QAbstractItemView.NoState) # Nothing found, so resort to default behavior QTableView.keyPressEvent(self, event)
class BaseTabs(QTabWidget): """TabWidget with context menu and corner widgets""" def __init__(self, parent, actions=None, menu=None, corner_widgets=None, menu_use_tooltips=False): QTabWidget.__init__(self, parent) self.setUsesScrollButtons(True) self.corner_widgets = {} self.menu_use_tooltips = menu_use_tooltips if menu is None: self.menu = QMenu(self) if actions: add_actions(self.menu, actions) else: self.menu = menu # Corner widgets if corner_widgets is None: corner_widgets = {} corner_widgets.setdefault(Qt.TopLeftCorner, []) corner_widgets.setdefault(Qt.TopRightCorner, []) self.browse_button = create_toolbutton(self, icon=get_icon("browse_tab.png"), tip=_("Browse tabs")) self.browse_tabs_menu = QMenu(self) self.browse_button.setMenu(self.browse_tabs_menu) self.browse_button.setPopupMode(self.browse_button.InstantPopup) self.connect(self.browse_tabs_menu, SIGNAL("aboutToShow()"), self.update_browse_tabs_menu) corner_widgets[Qt.TopLeftCorner] += [self.browse_button] self.set_corner_widgets(corner_widgets) def update_browse_tabs_menu(self): """Update browse tabs menu""" self.browse_tabs_menu.clear() names = [] dirnames = [] for index in range(self.count()): if self.menu_use_tooltips: text = unicode(self.tabToolTip(index)) else: text = unicode(self.tabText(index)) names.append(text) if osp.isfile(text): # Testing if tab names are filenames dirnames.append(osp.dirname(text)) offset = None # If tab names are all filenames, removing common path: if len(names) == len(dirnames): common = get_common_path(dirnames) if common is None: offset = None else: offset = len(common)+1 if offset <= 3: # Common path is not a path but a drive letter... offset = None for index, text in enumerate(names): tab_action = create_action(self, text[offset:], icon=self.tabIcon(index), toggled=lambda state, index=index: self.setCurrentIndex(index), tip=self.tabToolTip(index)) tab_action.setChecked(index == self.currentIndex()) self.browse_tabs_menu.addAction(tab_action) def set_corner_widgets(self, corner_widgets): """ Set tabs corner widgets corner_widgets: dictionary of (corner, widgets) corner: Qt.TopLeftCorner or Qt.TopRightCorner widgets: list of widgets (may contains integers to add spacings) """ assert isinstance(corner_widgets, dict) assert all(key in (Qt.TopLeftCorner, Qt.TopRightCorner) for key in corner_widgets) self.corner_widgets.update(corner_widgets) for corner, widgets in self.corner_widgets.iteritems(): cwidget = QWidget() cwidget.hide() prev_widget = self.cornerWidget(corner) if prev_widget: prev_widget.close() self.setCornerWidget(cwidget, corner) clayout = QHBoxLayout() clayout.setContentsMargins(0, 0, 0, 0) for widget in widgets: if isinstance(widget, int): clayout.addSpacing(widget) else: clayout.addWidget(widget) cwidget.setLayout(clayout) cwidget.show() def add_corner_widgets(self, widgets, corner=Qt.TopRightCorner): self.set_corner_widgets({corner: self.corner_widgets.get(corner, [])+widgets}) def contextMenuEvent(self, event): """Override Qt method""" if self.menu: self.menu.popup(event.globalPos()) def mousePressEvent(self, event): """Override Qt method""" if event.button() == Qt.MidButton: index = self.tabBar().tabAt(event.pos()) if index >= 0: self.emit(SIGNAL("close_tab(int)"), index) event.accept() return QTabWidget.mousePressEvent(self, event) def keyPressEvent(self, event): """Override Qt method""" ctrl = event.modifiers() & Qt.ControlModifier key = event.key() handled = False if ctrl and self.count() > 0: index = self.currentIndex() if key == Qt.Key_PageUp and index > 0: self.setCurrentIndex(index-1) handled = True elif key == Qt.Key_PageDown and index < self.count()-1: self.setCurrentIndex(index+1) handled = True if not handled: QTabWidget.keyPressEvent(self, event) def set_close_function(self, func): """Setting Tabs close function None -> tabs are not closable""" state = func is not None if state: self.connect(self, SIGNAL("close_tab(int)"), func) try: # Assuming Qt >= 4.5 QTabWidget.setTabsClosable(self, state) self.connect(self, SIGNAL("tabCloseRequested(int)"), func) except AttributeError: # Workaround for Qt < 4.5 close_button = create_toolbutton(self, triggered=func, icon=get_icon("fileclose.png"), tip=_("Close current tab")) self.setCornerWidget(close_button if state else None)
class WsdtGui(LayerViewerGui): ########################################### ### AppletGuiInterface Concrete Methods ### ########################################### def appletDrawer(self): return self._drawer def stopAndCleanUp(self): # Unsubscribe to all signals for fn in self.__cleanup_fns: fn() # Base class super( WsdtGui, self ).stopAndCleanUp() ########################################### ########################################### def __init__(self, parentApplet, topLevelOperatorView): self.__cleanup_fns = [] self._currently_updating = False self.topLevelOperatorView = topLevelOperatorView super(WsdtGui, self).__init__( parentApplet, topLevelOperatorView ) self._sp_colortable = generateRandomColors(256, clamp={'v': 1.0, 's' : 0.5}, zeroIsTransparent=True) self._threshold_colortable = [ QColor(0, 0, 0, 0).rgba(), # transparent QColor(0, 255, 0, 255).rgba() ] # green # Any time watershed is re-computed, re-update the layer set, in case the set of debug layers has changed. self.topLevelOperatorView.watershed_completed.subscribe( self.updateAllLayers ) def initAppletDrawerUi(self): """ Overridden from base class (LayerViewerGui) """ op = self.topLevelOperatorView def configure_update_handlers( qt_signal, op_slot ): qt_signal.connect( self.configure_operator_from_gui ) op_slot.notifyDirty( self.configure_gui_from_operator ) self.__cleanup_fns.append( partial( op_slot.unregisterDirty, self.configure_gui_from_operator ) ) def control_layout( label_text, widget ): row_layout = QHBoxLayout() row_layout.addWidget( QLabel(label_text) ) row_layout.addSpacerItem( QSpacerItem(10, 0, QSizePolicy.Expanding) ) row_layout.addWidget(widget) return row_layout drawer_layout = QVBoxLayout() channel_button = QPushButton() self.channel_menu = QMenu(self) # Must retain menus (in self) or else they get deleted. channel_button.setMenu(self.channel_menu) channel_button.clicked.connect(channel_button.showMenu) def populate_channel_menu(*args): if sip.isdeleted(channel_button): return self.channel_menu.clear() self.channel_actions = [] for ch in range(op.Input.meta.getTaggedShape()['c']): action = QAction("Channel {}".format(ch), self.channel_menu) action.setCheckable(True) self.channel_menu.addAction(action) self.channel_actions.append(action) configure_update_handlers( action.toggled, op.ChannelSelections ) populate_channel_menu() op.Input.notifyMetaChanged( populate_channel_menu ) self.__cleanup_fns.append( partial( op.Input.unregisterMetaChanged, populate_channel_menu ) ) drawer_layout.addLayout( control_layout( "Input Channel", channel_button ) ) self.channel_button = channel_button threshold_box = QDoubleSpinBox() threshold_box.setDecimals(2) threshold_box.setMinimum(0.00) threshold_box.setMaximum(1.0) threshold_box.setSingleStep(0.1) configure_update_handlers( threshold_box.valueChanged, op.Pmin ) drawer_layout.addLayout( control_layout( "Threshold", threshold_box ) ) self.threshold_box = threshold_box membrane_size_box = QSpinBox() membrane_size_box.setMinimum(0) membrane_size_box.setMaximum(1000000) configure_update_handlers( membrane_size_box.valueChanged, op.MinMembraneSize ) drawer_layout.addLayout( control_layout( "Min Membrane Size", membrane_size_box ) ) self.membrane_size_box = membrane_size_box seed_presmoothing_box = QDoubleSpinBox() seed_presmoothing_box.setDecimals(1) seed_presmoothing_box.setMinimum(0.0) seed_presmoothing_box.setMaximum(10.0) seed_presmoothing_box.setSingleStep(0.1) configure_update_handlers( seed_presmoothing_box.valueChanged, op.SigmaMinima ) drawer_layout.addLayout( control_layout( "Presmooth before seeds", seed_presmoothing_box ) ) self.seed_presmoothing_box = seed_presmoothing_box seed_method_combo = QComboBox() seed_method_combo.addItem("Connected") seed_method_combo.addItem("Clustered") configure_update_handlers( seed_method_combo.currentIndexChanged, op.GroupSeeds ) drawer_layout.addLayout( control_layout( "Seed Labeling", seed_method_combo ) ) self.seed_method_combo = seed_method_combo watershed_presmoothing_box = QDoubleSpinBox() watershed_presmoothing_box.setDecimals(1) watershed_presmoothing_box.setMinimum(0.0) watershed_presmoothing_box.setMaximum(10.0) watershed_presmoothing_box.setSingleStep(0.1) configure_update_handlers( watershed_presmoothing_box.valueChanged, op.SigmaWeights ) drawer_layout.addLayout( control_layout( "Presmooth before watershed", watershed_presmoothing_box ) ) self.watershed_presmoothing_box = watershed_presmoothing_box superpixel_size_box = QSpinBox() superpixel_size_box.setMinimum(0) superpixel_size_box.setMaximum(1000000) configure_update_handlers( superpixel_size_box.valueChanged, op.MinSegmentSize ) drawer_layout.addLayout( control_layout( "Min Superpixel Size", superpixel_size_box ) ) self.superpixel_size_box = superpixel_size_box preserve_pmaps_box = QCheckBox() configure_update_handlers( preserve_pmaps_box.toggled, op.PreserveMembranePmaps ) drawer_layout.addLayout( control_layout( "Preserve membrane probabilities", preserve_pmaps_box ) ) self.preserve_pmaps_box = preserve_pmaps_box enable_debug_box = QCheckBox() configure_update_handlers( enable_debug_box.toggled, op.EnableDebugOutputs ) drawer_layout.addLayout( control_layout( "Show Debug Layers", enable_debug_box ) ) self.enable_debug_box = enable_debug_box op.Superpixels.notifyReady(self.configure_gui_from_operator) op.Superpixels.notifyUnready(self.configure_gui_from_operator) self.__cleanup_fns.append( partial( op.Superpixels.unregisterReady, self.configure_gui_from_operator ) ) self.__cleanup_fns.append( partial( op.Superpixels.unregisterUnready, self.configure_gui_from_operator ) ) self.update_ws_button = QPushButton("Update Watershed", clicked=self.onUpdateWatershedsButton) drawer_layout.addWidget( self.update_ws_button ) drawer_layout.setSpacing(0) drawer_layout.addSpacerItem( QSpacerItem(0, 10, QSizePolicy.Minimum, QSizePolicy.Expanding) ) # Finally, the whole drawer widget drawer = QWidget(parent=self) drawer.setLayout(drawer_layout) # Save these members for later use self._drawer = drawer # Initialize everything with the operator's initial values self.configure_gui_from_operator() @contextmanager def set_updating(self): assert not self._currently_updating self._currently_updating = True yield self._currently_updating = False def configure_gui_from_operator(self, *args): if self._currently_updating: return False with self.set_updating(): op = self.topLevelOperatorView channel_selections = op.ChannelSelections.value for ch in range(op.Input.meta.shape[-1]): self.channel_actions[ch].setChecked(ch in channel_selections) if len(channel_selections) == 0: self.channel_button.setText("Please Select") else: self.channel_button.setText(",".join(map(str, channel_selections))) self.threshold_box.setValue( op.Pmin.value ) self.membrane_size_box.setValue( op.MinMembraneSize.value ) self.superpixel_size_box.setValue( op.MinSegmentSize.value ) self.seed_presmoothing_box.setValue( op.SigmaMinima.value ) self.watershed_presmoothing_box.setValue( op.SigmaWeights.value ) self.seed_method_combo.setCurrentIndex( int(op.GroupSeeds.value) ) self.preserve_pmaps_box.setChecked( op.PreserveMembranePmaps.value ) self.enable_debug_box.setChecked( op.EnableDebugOutputs.value ) self.update_ws_button.setEnabled( op.Superpixels.ready() ) def configure_operator_from_gui(self): if self._currently_updating: return False with self.set_updating(): op = self.topLevelOperatorView channel_selections = [] for ch in range(len(self.channel_actions)): if self.channel_actions[ch].isChecked(): channel_selections.append(ch) op.ChannelSelections.setValue( channel_selections ) op.Pmin.setValue( self.threshold_box.value() ) op.MinMembraneSize.setValue( self.membrane_size_box.value() ) op.MinSegmentSize.setValue( self.superpixel_size_box.value() ) op.SigmaMinima.setValue( self.seed_presmoothing_box.value() ) op.SigmaWeights.setValue( self.watershed_presmoothing_box.value() ) op.GroupSeeds.setValue( bool(self.seed_method_combo.currentIndex()) ) op.PreserveMembranePmaps.setValue( self.preserve_pmaps_box.isChecked() ) op.EnableDebugOutputs.setValue( self.enable_debug_box.isChecked() ) # The GUI may need to respond to some changes in the operator outputs. self.configure_gui_from_operator() def onUpdateWatershedsButton(self): def updateThread(): """ Temporarily unfreeze the cache and freeze it again after the views are finished rendering. """ self.topLevelOperatorView.FreezeCache.setValue(False) # This is hacky, but for now it's the only way to do it. # We need to make sure the rendering thread has actually seen that the cache # has been updated before we ask it to wait for all views to be 100% rendered. # If we don't wait, it might complete too soon (with the old data). ndim = len(self.topLevelOperatorView.Superpixels.meta.shape) self.topLevelOperatorView.Superpixels((0,)*ndim, (1,)*ndim).wait() # Wait for the image to be rendered into all three image views for imgView in self.editor.imageViews: if imgView.isVisible(): imgView.scene().joinRenderingAllTiles() self.topLevelOperatorView.FreezeCache.setValue(True) self.getLayerByName("Superpixels").visible = True th = threading.Thread(target=updateThread) th.start() def setupLayers(self): layers = [] op = self.topLevelOperatorView # Superpixels if op.Superpixels.ready(): layer = ColortableLayer( LazyflowSource(op.Superpixels), self._sp_colortable ) layer.colortableIsRandom = True layer.name = "Superpixels" layer.visible = True layer.opacity = 0.5 layers.append(layer) del layer # Debug layers if op.debug_results: for name, compressed_array in op.debug_results.items(): axiskeys = op.Superpixels.meta.getAxisKeys()[:-1] # debug images don't have a channel axis permutation = map(lambda key: axiskeys.index(key) if key in axiskeys else None, 'txyzc') arraysource = ArraySource( TransposedView(compressed_array, permutation) ) if compressed_array.dtype == np.uint32: layer = ColortableLayer(arraysource, self._sp_colortable) else: layer = GrayscaleLayer(arraysource) # TODO: Normalize? Maybe the drange should be included with the debug image. layer.name = name layer.visible = False layer.opacity = 1.0 layers.append(layer) del layer # Threshold if op.ThresholdedInput.ready(): layer = ColortableLayer( LazyflowSource(op.ThresholdedInput), self._threshold_colortable ) layer.name = "Thresholded Input" layer.visible = True layer.opacity = 1.0 layers.append(layer) del layer # Raw Data (grayscale) if op.Input.ready(): layer = self._create_grayscale_layer_from_slot( op.Input, op.Input.meta.getTaggedShape()['c'] ) layer.name = "Input" layer.visible = False layer.opacity = 1.0 layers.append(layer) del layer # Raw Data (grayscale) if op.RawData.ready(): layer = self.createStandardLayerFromSlot( op.RawData ) layer.name = "Raw Data" layer.visible = True layer.opacity = 1.0 layers.append(layer) del layer return layers
class View(QMainWindow, auxilia.Actions): def __init__(self, configuration, mpdclient, app): QMainWindow.__init__(self) self.app = app self.focus = time() self.shuttingDown = False self.config = configuration self.mpdclient = mpdclient appIcon = QIcon(DATA_DIR + 'icons/Pythagora.png') uic.loadUi(DATA_DIR + 'ui/Pythagora.ui', self) self.KDE = KDE self.setWindowTitle('Pythagora') self.setWindowIcon(appIcon) # Load all forms. self.createViews() # Create 'Connect to' menu. self.menuConnect = QMenu('Connect To') self.menuConnect.menuAction().setIcon( auxilia.PIcon('network-disconnect')) self.connectButton = QToolButton() self.connectButton.setPopupMode(QToolButton.InstantPopup) self.connectButton.setIcon(auxilia.PIcon('network-disconnect')) self.connectButton.setMenu(self.menuConnect) # Create 'MDP' menu. self.menuMPD = QMenu('MPD') self.menuMPD.menuAction().setIcon(auxilia.PIcon('network-workgroup')) self.mpdButton = QToolButton() self.mpdButton.setPopupMode(QToolButton.InstantPopup) self.mpdButton.setIcon(auxilia.PIcon('network-workgroup')) self.mpdButton.setMenu(self.menuMPD) self.reloadLibrary = self.actionLibReload(self.menuMPD, self.__libReload) self.updateLibrary = self.actionLibUpdate( self.menuMPD, lambda: self.mpdclient.send('update')) self.rescanLibrary = self.actionLibRescan( self.menuMPD, lambda: self.mpdclient.send('rescan')) # Fill Toolbar. self.toolBar.addWidget(self.connectButton) self.toolBar.addWidget(self.mpdButton) # Fill Statusbar. self.serverLabel = QLabel('Not connected') self.numSongsLabel = QLabel('Songs') self.playTimeLabel = QLabel('playTime') self.statusbar.addWidget(self.serverLabel) self.statusbar.addPermanentWidget(self.numSongsLabel) self.statusbar.addPermanentWidget(self.playTimeLabel) self.connect(self.menuConnect, SIGNAL('aboutToShow()'), self.__buildConnectTo) self.connect(self.actionExit, SIGNAL('triggered()'), self.app.quit) self.connect(self.actionSettings, SIGNAL('triggered()'), self.showConfig) # Set up trayicon and menu. if KDE: self.trayIcon = KTrayIcon(appIcon, self) else: self.trayIcon = QTrayIcon(appIcon, self) connectMenuAction = self.menuConnect.menuAction() self.trayIcon.addMenuItem(connectMenuAction) self.trayIcon.addMenuItem(self.actionSettings) self.connect(self.trayIcon, SIGNAL('activate()'), self.toggleHideRestore) self.connect(self.trayIcon, SIGNAL('secondaryActivateRequested(QPoint)'), self.__playPause) self.connect(self.tabs, SIGNAL('currentChanged(int)'), self.__tabsIndexChanged) self.connect(self.tabs.tabBar(), SIGNAL('tabMoved(int,int)'), self.__tabMoved) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self.__storeSplitter) # Apply configuration. self.resize(configuration.mgrSize) self.splitter.setSizes(configuration.mgrSplit) self.tabs.setCurrentIndex(configuration.tabsIndex) self.closeEvent = self.closeEvent self.connect(self.app, SIGNAL('aboutToQuit()'), self.shutdown) self.show() #============================================================================== # Code for switching tabs on drag & drop. (__init__() continues) #============================================================================== # Instantiate timer self.tabTimer = QTimer() self.connect(self.tabTimer, SIGNAL('timeout()'), self.__selectTab) # Overload the default dragEvents. (none?) self.tabs.dragLeaveEvent = self.dragLeaveEvent self.tabs.dragEnterEvent = self.dragEnterEvent self.tabs.dragMoveEvent = self.dragMoveEvent def dragEnterEvent(self, event): '''Starts timer on enter and sets first position.''' self.tabPos = event.pos() event.accept() self.tabTimer.start(500) def dragLeaveEvent(self, event): '''If the mouse leaves the tabWidget stop the timer.''' self.tabTimer.stop() def dragMoveEvent(self, event): '''Keep track of the mouse and change the position, restarts the timer when moved.''' tabPos = event.pos() moved = tabPos.manhattanLength() - self.tabPos.manhattanLength() if moved > 7 or moved < -7: self.tabTimer.start(500) self.tabPos = tabPos def __selectTab(self): '''Changes the view to the tab where the mouse was hovering above.''' index = self.tabs.tabBar().tabAt(self.tabPos) self.tabs.setCurrentIndex(index) self.tabTimer.stop() def __libReload(self): self.mpdclient.send( 'listallinfo', callback=lambda mainlist: self.emit(SIGNAL('reloadLibrary'), mpdlibrary.Library(mainlist))) #============================================================================== def createViews(self): '''Set up our different view handlers.''' # Standard views. self.playerForm = PlayerForm(self, self.app, self.mpdclient, self.config) self.currentList = CurrentPlaylistForm.CurrentPlaylistForm( self, self.app, self.mpdclient, self.config) # Plugin views. loadedPlugins = [] for plugin in plugins.allPlugins: loadedPlugins.append( plugin.getWidget(self, self.mpdclient, self.config)) for name in self.config.tabOrder: for plugin in loadedPlugins: if plugin.moduleName == name: self.tabs.addTab(plugin, auxilia.PIcon(plugin.moduleIcon), plugin.moduleName) break def shutdown(self): self.shuttingDown = True self.app.processEvents() self.mpdclient.disconnect() self.config.mgrSize = self.size() print 'debug: shutdown finished' def showConfig(self): self.config.showConfiguration(self) def closeEvent(self, event): '''Catch MainWindow's close event so we can hide it instead.''' self.hide() event.ignore() def __storeSplitter(self): self.config.mgrSplit = self.splitter.sizes() def __tabsIndexChanged(self, value): self.config.tabsIndex = self.tabs.currentIndex() def __tabMoved(self, old, new): print "DEBUG: Tab from", old, "moved to", new order = self.config.tabOrder order.insert(new, order.pop(old)) self.config.tabOrder = order def __toggleShoutCast(self, value): self.config.showShoutcast = value self.stackedWidget.setCurrentIndex(value) def toggleHideRestore(self): '''Show or hide the window based on some parameters. We can detect when we are obscured and come to the top. In other cases we hide if mapped and show if not. ''' if KDE: if KWindowSystem.activeWindow() == self.winId() and self.isVisible( ): self.hide() else: self.show() KWindowSystem.forceActiveWindow(self.winId()) else: if self.isVisible(): self.hide() else: self.show() def __playPause(self): self.playerForm.play.emit(SIGNAL('clicked(bool)'), True) def __buildConnectTo(self): self.menuConnect.clear() self.menuConnect.addAction(auxilia.PIcon('dialog-cancel'), 'None (disconnect)') connected = self.mpdclient.connected() for server in self.config.knownHosts: if connected and self.config.server and self.config.server[ 0] == server: icon = auxilia.PIcon('network-connect') else: icon = auxilia.PIcon('network-disconnect') self.menuConnect.addAction(icon, server)
class Interface(QFrame): def __init__(self, parent=None): QFrame.__init__(self, parent) self.design = Ui_Frame() self.design.setupUi(self) self.recentsmenu = QMenu() self.design.recent.setMenu(self.recentsmenu) self.thread = None self._setupsignals() self.old_thread = None self.sim = None self.settings = QSettings( "Random free software you stump around", "simuleds") self.recents = list(self._readrecent()) self._updaterecents() def _setupsignals(self): #ui signals -> load a firmware self.connect(self.design.load, SIGNAL('clicked()'), self.loadfile) #ui signals -> reset button on the proto QObject.connect(self.design.start, SIGNAL('clicked()'), self.reset) def getboxbynum(self, num): return getattr(self.design, 'led_%d' % num) def setsim(self, simklass): try: self._setsim(simklass()) except: self.err(format_exc()) raise def err(self, message): self.log("<b>%s</b>" % message, style='color:#ff0000;') def log(self, message, prefix='', style=''): if style != '': style = 'style="%s"' % style if prefix != '': prefix = '<span><b>%s:</b> </span>' % prefix self.design.log.append("%s<span %s>%s</span><br/>" % (prefix, style, message)) def simlog(self, message): self.log(message, prefix='sim', style='color:#0000ff;') def _setsim(self, sim): if self.thread: self.thread.stop() self.thread.terminate() self.thread.wait() if self.sim: self._disconnectsim(sim) self._connectsim(sim) self.old_thread = self.thread self.thread = ArduiThread(sim.start, sim.stop) self.thread.start() self.sim = sim def reset(self): if not self.sim: return self.sim.reset() def _connectsim(self, sim): #ui signals -> logs QObject.connect(sim, _LOOPMSGSIGNAL, self.simlog) #signals to set box values for index in xrange(api.ARDUINO_DIGITAL_PIN_NB): box = self.getboxbynum(index) QObject.connect(sim.pins[index], _PINSIGNAL, box.setChecked) def _disconnectsim(self, sim): #ui signals -> logs QObject.disconnect(sim, _LOOPMSGSIGNAL, self.simlog) #signals to set box values for index in xrange(api.ARDUINO_DIGITAL_PIN_NB): box = self.getboxbynum(index) QObject.disconnect(sim.pins[index], _PINSIGNAL, box.setChecked) def loadfile(self): self.settings.beginGroup("Last opened") filename = unicode(self.settings.value("firmware", ".").toString()) simklass, filename = self._loadfile(filename) if not simklass: self.log("Cancel firmware load.") return else: self.setsim(simklass) self.settings.setValue("firmware", filename) self.settings.endGroup() self.addrecent(filename) self.writerecentlist() self.settings.sync() def loadfilefactory(self, filename): def loadit(): simklass = self._loadfile(filename, dialog=False)[0] if simklass: self.setsim(simklass) self.addrecent(filename) return loadit def _updaterecents(self): self.design.recent.setEnabled(bool(self.recents)) self.recentsmenu.clear() if not self.recents: return for item in self.recents: self.recentsmenu.addAction(item, self.loadfilefactory(item)) def _readrecent(self): self.settings.beginGroup("Recent files") recents = self.settings.value("list", []).toList() self.settings.endGroup() return self._recentlist(recents) def writerecentlist(self): self.settings.beginGroup("Recent files") self.settings.setValue("list", self.recents) self.settings.endGroup() def addrecent(self, filename): if filename in self.recents: self.recents.remove(filename) self.recents.insert(0, filename) #limit to size 5 self.recents = self.recents[:5] self._updaterecents() def _recentlist(self, origlist, mostrecent=''): if mostrecent: yield mostrecent count = 1 else: count = 0 for item in origlist: if isinstance(item, QVariant): item = unicode(item.toString()) if item == mostrecent: continue yield item count += 1 if count > 5: break def _loadfile(self, filename, dialog=True): """ Interacts with user to get a sim plugin """ while True: if dialog: filename = unicode( QFileDialog.getOpenFileName(self, "Choose a firmware", dirname(filename), "Python files with setup() and loop() " "functions (*.py) (*.py)")) if not filename: self.log("Aborting, you did not specify a file.") return None, None if not exists(filename): self.err("File '%s' does not exist." % filename) QMessageBox.critical(None, "Error", "File '%s' does not exist." % filename) dialog = True continue self.log("opening file: %s." % filename) try: simklass = simfactory('my pony sim', filename) except (SyntaxError, TypeError), err: self.err(format_exc()) QMessageBox.critical( None, "Error", "It seems '%s' is an invalid Python source." % filename) continue except Exception, err: self.err(format_exc()) QMessageBox.warning(None, "Error", unicode(err)) continue return simklass, filename
class MainWindow(QMainWindow): groups = dict() typeQListWidgetHeader = 1000 showHostsInGroups = False currentGroupName = None # used to simple detect currently selected group to show menu def __init__(self): super(MainWindow, self).__init__() self.config = Config() self.db = Database(self.config.getConnectionString()) cryptoKey = self.getCryptoKey() self.hosts = Hosts(self.db, cryptoKey) # menu used for each host self.hostMenu = QMenu() self.editAction = QAction(QIcon(':/ico/edit.svg'), "Edit", self.hostMenu) self.editAction.triggered.connect(self.editHost) self.hostMenu.addAction(self.editAction) # menu used for headers of groups self.groupsHeaderMenu = QMenu() self.editGroupAction = QAction(QIcon(':/ico/edit.svg'), "Edit group", self.groupsHeaderMenu) self.editGroupAction.triggered.connect(self.editGroup) self.deleteGroupAction = QAction(QIcon(':/ico/remove.svg'), "Delete group", self.groupsHeaderMenu) self.deleteGroupAction.triggered.connect(self.deleteGroup) self.groupsHeaderMenu.addAction(self.editGroupAction) self.groupsHeaderMenu.addAction(self.deleteGroupAction) self.duplicateAction = QAction(QIcon(':/ico/copy.svg'), "Duplicate", self.hostMenu) self.duplicateAction.triggered.connect(self.duplicateHost) self.hostMenu.addAction(self.duplicateAction) # todo: confirm for delete action self.deleteAction = QAction(QIcon(':/ico/remove.svg'), "Delete", self.hostMenu) self.deleteAction.triggered.connect(self.deleteHost) self.hostMenu.addAction(self.deleteAction) self.connectFramelessMenu = actions.generateScreenChoseMenu( self.hostMenu, self.connectFrameless, ':/ico/frameless.svg', "Connect frameless") self.hostMenu.addMenu(self.connectFramelessMenu) self.assignGroupAction = QAction("Assign group", self.hostMenu) self.assignGroupAction.triggered.connect(self.assignGroup) self.hostMenu.addAction(self.assignGroupAction) # setup main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # when top level changed, we changing dock title bar self.dockWidgetTileBar = DockWidgetTitleBar() self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) self.ui.hostsDock.topLevelChanged.connect(self.dockLevelChanged) # set global menu self.globalMenu = QMenu() self.globalMenu.addAction(QIcon(':/ico/add.svg'), 'Add host', self.addHost) # groups menu self.groupsMenu = QMenu("Groups") self.groupsMenu.aboutToShow.connect(self.setGroupsMenu) self.globalMenu.addMenu(self.groupsMenu) # disable menu indicator self.ui.menu.setStyleSheet( "QPushButton::menu-indicator {image: none;}") self.positionMenu = QMenu("Dock position") self.positionMenu.addAction( "Left", lambda: self.setDockPosition(Qt.LeftDockWidgetArea)) self.positionMenu.addAction( "Right", lambda: self.setDockPosition(Qt.RightDockWidgetArea)) self.positionMenu.addAction("Float", self.setDockFloat) self.globalMenu.addMenu(self.positionMenu) self.globalMenu.addAction('Change tray icon visibility', self.changeTrayIconVisibility) self.globalMenu.addAction('Settings', self.showSettings) self.globalMenu.addAction('Quit', self.close) self.ui.menu.setMenu(self.globalMenu) # set events on hosts list self.ui.hostsList.itemDoubleClicked.connect(self.slotConnectHost) self.ui.hostsList.itemClicked.connect(self.slotShowHost) self.ui.hostsList.customContextMenuRequested.connect( self.slotShowHostContextMenu) # set tab widget self.tabWidget = MyTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.tabWidget.customContextMenuRequested.connect( self.showCentralWidgetContextMenu) # set tray icon self.tray = QSystemTrayIcon(QIcon(":/ico/myrdp.svg")) self.tray.activated.connect(self.trayActivated) self.trayMenu = QMenu() self.trayMenu.addAction("Hide tray icon", self.changeTrayIconVisibility) self.connectHostMenuTray = ConnectHostMenu(self.hosts) self.connectHostMenuTray.triggered.connect( self.connectHostFromTrayMenu) self.trayMenu.addMenu(self.connectHostMenuTray) self.trayMenu.addAction("Quit", self.close) self.tray.setContextMenu(self.trayMenu) self.restoreSettings() # host list self.ui.filter.textChanged.connect(self.setHostList) self.setHostList() def getCryptoKey(self, passphrase=None): try: return self.config.getPrivateKey(passphrase) except ValueError: passwordDialog = PasswordDialog() retCode = passwordDialog.exec_() if retCode == QtGui.QDialog.Accepted: return self.getCryptoKey(passwordDialog.getPassword()) else: raise SystemError("Password required") def showSettings(self): settingsWidget = self.findChild(QWidget, "settings") if settingsWidget is None: self.settingsWidget = SettingsPage() self.settingsWidget.setObjectName("settings") self.tabWidget.insertTab(0, self.settingsWidget, QIcon(":/ico/settings.svg"), 'Settings') index = self.tabWidget.indexOf(self.settingsWidget) self.tabWidget.setCurrentIndex(index) def connectHostFromMenu(self, action): self.connectHost(unicode(action.text())) def connectHostFromTrayMenu(self, action): tabPage = self.connectHost(unicode(action.text())) if not self.isVisible(): self.tabWidget.setDetached(True, tabPage) def trayActivated(self, reason): if reason != QSystemTrayIcon.Trigger: return if self.isVisible(): self.hide() else: self.show() self.activateWindow() def changeTrayIconVisibility(self): if self.tray.isVisible(): self.tray.hide() if not self.isVisible(): self.show() else: self.tray.show() def refreshGroups(self): groupList = self.hosts.getGroupsList() for group in groupList: if group not in self.groups: # add new groups as visible self.groups[group] = True # remove not existing groups keysToDelete = set(self.groups.keys()) - set(groupList) for key in keysToDelete: self.groups.pop(key) def assignGroup(self): groups = self.hosts.getGroupsList() assignGroupDialog = AssignGroupDialog(groups) groupToAssign = assignGroupDialog.assign() if groupToAssign is not False: # None could be used to unassign the group groupToAssign = None if groupToAssign.isEmpty() else unicode( groupToAssign) for hostName in self.getSelectedHosts(): self.hosts.assignGroup(hostName, groupToAssign) self.db.tryCommit() self.setHostList() def setGroupsMenu(self): self.groupsMenu.clear() addGroupAction = self.groupsMenu.addAction('Add group') addGroupAction.triggered.connect(self.addGroup) deleteGroupAction = self.groupsMenu.addAction('Delete group') deleteGroupAction.triggered.connect(self.showDeleteGroupDialog) showHostsInGroupsAction = self.groupsMenu.addAction( 'Show host list in groups') showHostsInGroupsAction.triggered.connect(self.changeHostListView) showHostsInGroupsAction.setCheckable(True) showHostsInGroupsAction.setChecked(self.showHostsInGroups) self.groupsMenu.addSeparator() for group, checked in self.groups.items(): action = QAction(group, self.groupsMenu) action.setCheckable(True) action.setChecked(checked) action.triggered.connect(self.groupsVisibilityChanged) self.groupsMenu.addAction(action) def addGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.add() self._processHostSubmit(resp) def groupsVisibilityChanged(self, checked): currentGroup = unicode(self.sender().text()) self.groups[currentGroup] = checked self.setHostList() def setDockPosition(self, dockWidgetArea): if self.ui.hostsDock.isFloating(): self.ui.hostsDock.setFloating(False) self.addDockWidget(dockWidgetArea, self.ui.hostsDock) def setDockFloat(self): if self.ui.hostsDock.isFloating(): return # default title bar must be set before is float because sometimes window make strange crash self.ui.hostsDock.setTitleBarWidget(None) self.ui.hostsDock.setFloating(True) def dockLevelChanged(self, isFloating): if isFloating: # changing title bar widget if is not none, probably true will be only once on start with saved float state if self.ui.hostsDock.titleBarWidget(): self.ui.hostsDock.setTitleBarWidget(None) else: self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) def showFramelessWidget(self): self.t.show() self.t.setGeometry(self.frameGeometry()) def getCurrentHostListItemName(self): return self.ui.hostsList.currentItem().text() def getSelectedHosts(self): return [host.text() for host in self.ui.hostsList.selectedItems()] def findHostItemByName(self, name): result = self.ui.hostsList.findItems(name, Qt.MatchExactly) resultLen = len(result) if resultLen != 1: # should be only one host logger.error("Host not found. Got %d results" % resultLen) return result[0] def showCentralWidgetContextMenu(self, pos): menu = QMenu() title = self.ui.hostsDock.windowTitle() hostsDockAction = menu.addAction(title) hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.ui.hostsDock.isVisible()) hostsDockAction.triggered.connect(self.changeHostsDockWidgetVisibility) hostsDockAction = menu.addAction("Tray icon") hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.tray.isVisible()) hostsDockAction.triggered.connect(self.changeTrayIconVisibility) connectHostMenuTray = ConnectHostMenu(self.hosts, "Connect") connectHostMenuTray.triggered.connect(self.connectHostFromMenu) menu.addMenu(connectHostMenuTray) menu.exec_(self.tabWidget.mapToGlobal(pos)) def changeHostListView(self, checked): self.showHostsInGroups = checked self.setHostList() def changeHostsDockWidgetVisibility(self): isVisible = self.ui.hostsDock.isVisible() self.ui.hostsDock.setVisible(not isVisible) def isHostListHeader(self, item): if not item or item.type() == self.typeQListWidgetHeader: return True return False def slotShowHostContextMenu(self, pos): def changeMenusVisibility(isEnabled): self.connectFramelessMenu.setEnabled(isEnabled) self.editAction.setEnabled(isEnabled) self.duplicateAction.setEnabled(isEnabled) # ignore context menu for group headers item = self.ui.hostsList.itemAt(pos) if self.isHostListHeader(item): item = self.ui.hostsList.itemAt(pos) widgetItem = self.ui.hostsList.itemWidget(item) if widgetItem: self.currentGroupName = widgetItem.text() # yea I'm so dirty if self.currentGroupName != unassignedGroupName: self.groupsHeaderMenu.exec_( self.ui.hostsList.mapToGlobal(pos)) return if len(self.ui.hostsList.selectedItems()) == 1: # single menu changeMenusVisibility(True) else: changeMenusVisibility(False) self.hostMenu.exec_(self.ui.hostsList.mapToGlobal(pos)) def _processHostSubmit(self, resp): if resp["code"]: self.setHostList() hostName = resp.get("name") if hostName: hostItem = self.findHostItemByName(hostName) self.slotConnectHost(hostItem) def addHost(self): hostDialog = HostConfigDialog(self.hosts) self._processHostSubmit(hostDialog.add()) def editHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.edit(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def editGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.edit(self.currentGroupName) self._processHostSubmit(resp) def deleteGroup(self): retCode = self.showOkCancelMessageBox( "Do you want to remove selected group? All assigned hosts " "to this group will be unassigned.", "Confirmation") if retCode == QMessageBox.Cancel: return self.hosts.deleteGroup(self.currentGroupName) self.setHostList() def showDeleteGroupDialog(self): deleteGroupDialog = DeleteGroupDialog(self.hosts) deleteGroupDialog.deleteGroup() self.setHostList() def duplicateHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.duplicate(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def deleteHost(self): retCode = self.showOkCancelMessageBox( "Do you want to remove selected hosts?", "Confirmation") if retCode == QMessageBox.Cancel: return for host in self.getSelectedHosts(): self.hosts.delete(host) self.setHostList() def connectFrameless(self, screenIndex=None): self.connectHost(self.getCurrentHostListItemName(), frameless=True, screenIndex=screenIndex) # Fix to release keyboard from QX11EmbedContainer, when we leave widget through wm border def leaveEvent(self, event): keyG = QWidget.keyboardGrabber() if keyG is not None: keyG.releaseKeyboard() event.accept() # needed? def setHostList(self): """ set hosts list in list view """ self.ui.hostsList.clear() self.refreshGroups() hostFilter = self.ui.filter.text() if self.showHostsInGroups: self.showHostListInGroups(hostFilter) else: self.showHostList(hostFilter) def showHostList(self, hostFilter): groupFilter = [ group for group, visibility in self.groups.items() if visibility ] hosts = self.hosts.getHostsListByHostNameAndGroup( hostFilter, groupFilter) self.ui.hostsList.addItems(hosts) def showHostListInGroups(self, hostFilter): hosts = self.hosts.getGroupedHostNames(hostFilter) for group, hostsList in hosts.items(): if self.groups.get(group, True): if group is None: group = unassignedGroupName groupHeader = QtGui.QListWidgetItem( type=self.typeQListWidgetHeader) groupLabel = QtGui.QLabel(unicode(group)) groupLabel.setProperty('class', 'group-title') self.ui.hostsList.addItem(groupHeader) self.ui.hostsList.setItemWidget(groupHeader, groupLabel) self.ui.hostsList.addItems(hostsList) def slotShowHost(self, item): # on one click we activating tab and showing options self.tabWidget.activateTab(item) def slotConnectHost(self, item): if self.isHostListHeader(item): return self.connectHost(unicode(item.text())) def connectHost(self, hostId, frameless=False, screenIndex=None): hostId = unicode(hostId) # sometimes hostId comes as QString tabPage = self.tabWidget.createTab(hostId) tabPage.reconnectionNeeded.connect(self.connectHost) if frameless: self.tabWidget.detachFrameless(tabPage, screenIndex) try: execCmd, opts = self.getCmd(tabPage, hostId) except LookupError: logger.error(u"Host {} not found.".format(hostId)) return ProcessManager.start(hostId, tabPage, execCmd, opts) return tabPage def getCmd(self, tabPage, hostName): host = self.hosts.get(hostName) # set tabPage widget width, height = tabPage.setSizeAndGetCurrent() # 1et widget winId to embed rdesktop winId = tabPage.x11.winId() # set remote desktop client, at this time works only with freerdp remoteClientType, remoteClientOptions = self.config.getRdpClient() remoteClient = ClientFactory(remoteClientType, **remoteClientOptions) remoteClient.setWindowParameters(winId, width, height) remoteClient.setUserAndPassword(host.user, host.password) remoteClient.setAddress(host.address) return remoteClient.getComposedCommand() def saveSettings(self): self.config.setValue("geometry", self.saveGeometry()) self.config.setValue("windowState", self.saveState()) self.config.setValue('trayIconVisibility', self.tray.isVisible()) self.config.setValue('mainWindowVisibility', self.isVisible()) self.config.setValue('groups', self.groups) self.config.setValue('showHostsInGroups', self.showHostsInGroups) def restoreSettings(self): try: self.restoreGeometry( self.config.getValue("geometry").toByteArray()) self.restoreState( self.config.getValue("windowState").toByteArray()) except Exception: logger.debug("No settings to restore") # restore tray icon state trayIconVisibility = self.config.getValue('trayIconVisibility', "true").toBool() self.tray.setVisible(trayIconVisibility) self.showHostsInGroups = self.config.getValue('showHostsInGroups', 'false').toBool() if self.tray.isVisible(): mainWindowVisibility = self.config.getValue( 'mainWindowVisibility', "true").toBool() self.setVisible(mainWindowVisibility) else: # it tray icon is not visible, always show main window self.show() self.groups = { unicode(k): v for k, v in self.config.getValue('groups', {}).toPyObject().items() } def closeEvent(self, event): if not ProcessManager.hasActiveProcess: self.saveSettings() QCoreApplication.exit() return ret = self.showOkCancelMessageBox("Are you sure do you want to quit?", "Exit confirmation") if ret == QMessageBox.Cancel: event.ignore() return self.saveSettings() ProcessManager.killemall() event.accept() QCoreApplication.exit() def showOkCancelMessageBox(self, messageBoxText, windowTitle): msgBox = QMessageBox(self, text=messageBoxText) msgBox.setWindowTitle(windowTitle) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setIcon(QMessageBox.Question) return msgBox.exec_()
class QuickMapServices: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__).decode( sys.getfilesystemencoding()) # initialize locale self.translator = QTranslator() self.locale = Locale.get_locale() locale_path = os.path.join( self.plugin_dir, 'i18n', 'QuickMapServices_{}.qm'.format(self.locale)) if os.path.exists(locale_path): self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.custom_translator = CustomTranslator() QCoreApplication.installTranslator(self.custom_translator) # Create the dialog (after translation) and keep reference self.info_dlg = AboutDialog() # Check Contrib and User dirs try: ExtraSources.check_extra_dirs() except: error_message = self.tr( 'Extra dirs for %s can\'t be created: %s %s') % ( PluginSettings.product_name(), sys.exc_type, sys.exc_value) self.iface.messageBar().pushMessage(self.tr('Error'), error_message, level=QgsMessageBar.CRITICAL) # Declare instance attributes self.service_actions = [] self.service_layers = [] # TODO: id and smart remove self._scales_list = None # noinspection PyMethodMayBeStatic def tr(self, message): # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('QuickMapServices', message) def initGui(self): #import pydevd #pydevd.settrace('localhost', port=9921, stdoutToServer=True, stderrToServer=True, suspend=False) # Register plugin layer type self.tileLayerType = TileLayerType(self) QgsPluginLayerRegistry.instance().addPluginLayerType( self.tileLayerType) # Create menu icon_path = self.plugin_dir + '/icons/mActionAddLayer.png' self.menu = QMenu(self.tr(u'QuickMapServices')) self.menu.setIcon(QIcon(icon_path)) self.build_menu_tree() # add to QGIS menu/toolbars self.append_menu_buttons() def _load_scales_list(self): scales_filename = os.path.join(self.plugin_dir, 'scales.xml') scales_list = [] # TODO: remake when fix: http://hub.qgis.org/issues/11915 # QgsScaleUtils.loadScaleList(scales_filename, scales_list, importer_message) xml_root = ET.parse(scales_filename).getroot() for scale_el in xml_root.findall('scale'): scales_list.append(scale_el.get('value')) return scales_list @property def scales_list(self): if not self._scales_list: self._scales_list = self._load_scales_list() return self._scales_list def set_nearest_scale(self): #get current scale curr_scale = self.iface.mapCanvas().scale() #find nearest nearest_scale = sys.maxint for scale_str in self.scales_list: scale = scale_str.split(':')[1] scale_int = int(scale) if abs(scale_int - curr_scale) < abs(nearest_scale - curr_scale): nearest_scale = scale_int #set new scale if nearest_scale != sys.maxint: self.iface.mapCanvas().zoomScale(nearest_scale) def set_tms_scales(self): res = QMessageBox.question( self.iface.mainWindow(), self.tr('QuickMapServices'), self. tr('Set SlippyMap scales for current project? \nThe previous settings will be overwritten!' ), QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.Yes: # set scales QgsProject.instance().writeEntry('Scales', '/ScalesList', self.scales_list) # activate QgsProject.instance().writeEntry('Scales', '/useProjectScales', True) # update in main window # ???? no way to update: http://hub.qgis.org/issues/11917 def insert_layer(self): #TODO: need factory! layers4add = [] action = self.menu.sender() ds = action.data() if ds.type == KNOWN_DRIVERS.TMS: service_info = TileServiceInfo(self.tr(ds.alias), ds.copyright_text, ds.tms_url) service_info.zmin = ds.tms_zmin or service_info.zmin service_info.zmax = ds.tms_zmax or service_info.zmax if ds.tms_y_origin_top is not None: service_info.yOriginTop = ds.tms_y_origin_top service_info.epsg_crs_id = ds.tms_epsg_crs_id service_info.postgis_crs_id = ds.tms_postgis_crs_id service_info.custom_proj = ds.tms_custom_proj layer = TileLayer(self, service_info, False) layers4add.append(layer) if ds.type == KNOWN_DRIVERS.GDAL: layer = QgsRasterLayer(ds.gdal_source_file, self.tr(ds.alias)) layers4add.append(layer) if ds.type == KNOWN_DRIVERS.WMS: qgis_wms_uri = u'' if ds.wms_params: qgis_wms_uri += ds.wms_params if ds.wms_layers: layers = ds.wms_layers.split(',') if layers: if ds.wms_turn_over: layers.reverse() qgis_wms_uri += '&layers=' + '&layers='.join( layers) + '&styles=' * len(layers) qgis_wms_uri += '&url=' + ds.wms_url layer = QgsRasterLayer(qgis_wms_uri, self.tr(ds.alias), KNOWN_DRIVERS.WMS.lower()) layers4add.append(layer) if ds.type == KNOWN_DRIVERS.WFS: qgis_wfs_uri_base = ds.wfs_url o = urlparse.urlparse(qgis_wfs_uri_base) request_attrs = dict(urlparse.parse_qsl(o.query)) layers_str = request_attrs.get('TYPENAME', '') layers = layers_str.split(',') for layer_name in layers: new_request_attrs = request_attrs new_request_attrs['TYPENAME'] == layer_name url_parts = list(o) url_parts[4] = "&".join( ["%s=%s" % (k, v) for k, v in new_request_attrs.items()]) qgis_wfs_uri = urlparse.urlunparse(url_parts) layer = QgsVectorLayer( qgis_wfs_uri, "%s - %s" % (self.tr(ds.alias), layer_name), "WFS") layers4add.append(layer) for layer in layers4add: if not layer.isValid(): error_message = self.tr( 'Layer %s can\'t be added to the map!') % ds.alias self.iface.messageBar().pushMessage( self.tr('Error'), error_message, level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage(error_message, level=QgsMessageLog.CRITICAL) else: # Set attribs layer.setAttribution(ds.copyright_text) layer.setAttributionUrl(ds.copyright_link) # Insert to bottom QgsMapLayerRegistry.instance().addMapLayer(layer, False) toc_root = QgsProject.instance().layerTreeRoot() toc_root.insertLayer(len(toc_root.children()), layer) # Save link self.service_layers.append(layer) # Set OTF CRS Transform for map if PluginSettings.enable_otf_3857( ) and ds.type == KNOWN_DRIVERS.TMS: self.iface.mapCanvas().setCrsTransformEnabled(True) self.iface.mapCanvas().setDestinationCrs( TileLayer.CRS_3857) def unload(self): # remove menu/ self.remove_menu_buttons() # clean vars self.menu = None self.toolbutton = None self.service_actions = None self.ds_list = None self.groups_list = None self.service_layers = None # Unregister plugin layer type QgsPluginLayerRegistry.instance().removePluginLayerType( TileLayer.LAYER_TYPE) def build_menu_tree(self): # Main Menu self.menu.clear() self.groups_list = GroupsList() self.ds_list = DataSourcesList() data_sources = self.ds_list.data_sources.values() data_sources.sort(key=lambda x: x.alias or x.id) ds_hide_list = PluginSettings.get_hide_ds_id_list() for ds in data_sources: if ds.id in ds_hide_list: continue ds.action.triggered.connect(self.insert_layer) gr_menu = self.groups_list.get_group_menu(ds.group) gr_menu.addAction(ds.action) if gr_menu not in self.menu.children(): self.menu.addMenu(gr_menu) # Scales, Settings and About actions self.menu.addSeparator() icon_set_nearest_scale_path = self.plugin_dir + '/icons/mActionSettings.png' # TODO change icon set_nearest_scale_act = QAction(QIcon(icon_set_nearest_scale_path), self.tr('Set proper scale'), self.iface.mainWindow()) set_nearest_scale_act.triggered.connect(self.set_nearest_scale) self.menu.addAction(set_nearest_scale_act) # TODO: uncomment after fix self.service_actions.append(set_nearest_scale_act) icon_scales_path = self.plugin_dir + '/icons/mActionSettings.png' # TODO change icon scales_act = QAction(QIcon(icon_scales_path), self.tr('Set SlippyMap scales'), self.iface.mainWindow()) scales_act.triggered.connect(self.set_tms_scales) #self.menu.addAction(scales_act) # TODO: uncomment after fix self.service_actions.append(scales_act) icon_settings_path = self.plugin_dir + '/icons/mActionSettings.png' settings_act = QAction(QIcon(icon_settings_path), self.tr('Settings'), self.iface.mainWindow()) self.service_actions.append(settings_act) settings_act.triggered.connect(self.show_settings_dialog) self.menu.addAction(settings_act) icon_about_path = self.plugin_dir + '/icons/mActionAbout.png' info_act = QAction(QIcon(icon_about_path), self.tr('About'), self.iface.mainWindow()) self.service_actions.append(info_act) info_act.triggered.connect(self.info_dlg.show) self.menu.addAction(info_act) def remove_menu_buttons(self): """ Remove menus/buttons from all toolbars and main submenu :return: None """ # remove menu if self.menu: self.iface.webMenu().removeAction(self.menu.menuAction()) self.iface.addLayerMenu().removeAction(self.menu.menuAction()) # remove toolbar button if self.tb_action: self.iface.webToolBar().removeAction(self.tb_action) self.iface.layerToolBar().removeAction(self.tb_action) def append_menu_buttons(self): """ Append menus and buttons to appropriate toolbar :return: """ # add to QGIS menu if PluginSettings.move_to_layers_menu(): self.iface.addLayerMenu().addMenu(self.menu) else: # need workaround for WebMenu _temp_act = QAction('temp', self.iface.mainWindow()) self.iface.addPluginToWebMenu("_tmp", _temp_act) self.iface.webMenu().addMenu(self.menu) self.iface.removePluginWebMenu("_tmp", _temp_act) # add to QGIS toolbar toolbutton = QToolButton() toolbutton.setPopupMode(QToolButton.InstantPopup) toolbutton.setMenu(self.menu) toolbutton.setIcon(self.menu.icon()) toolbutton.setText(self.menu.title()) toolbutton.setToolTip(self.menu.title()) if PluginSettings.move_to_layers_menu(): self.tb_action = self.iface.layerToolBar().addWidget(toolbutton) else: self.tb_action = self.iface.webToolBar().addWidget(toolbutton) def show_settings_dialog(self): settings_dlg = SettingsDialog() settings_dlg.exec_() # apply settings # self.remove_menu_buttons() self.build_menu_tree()
class ProfileTableViewer( QWidget ): " Profiling results table viewer " escapePressed = pyqtSignal() def __init__( self, scriptName, params, reportTime, dataFile, stats, parent = None ): QWidget.__init__( self, parent ) self.__table = ProfilerTreeWidget( self ) self.__table.escapePressed.connect( self.__onEsc ) self.__script = scriptName self.__stats = stats project = GlobalData().project if project.isLoaded(): self.__projectPrefix = os.path.dirname( project.fileName ) else: self.__projectPrefix = os.path.dirname( scriptName ) if not self.__projectPrefix.endswith( os.path.sep ): self.__projectPrefix += os.path.sep self.__table.setAlternatingRowColors( True ) self.__table.setRootIsDecorated( False ) self.__table.setItemsExpandable( False ) self.__table.setSortingEnabled( True ) self.__table.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.__table.setUniformRowHeights( True ) self.__table.setSelectionMode( QAbstractItemView.SingleSelection ) self.__table.setSelectionBehavior( QAbstractItemView.SelectRows ) headerLabels = [ "", "Calls", "Total time", "Per call", "Cum. time", "Per call", "File name:line", "Function", "Callers", "Callees" ] self.__table.setHeaderLabels( headerLabels ) headerItem = self.__table.headerItem() headerItem.setToolTip( 0, "Indication if it is an outside function" ) headerItem.setToolTip( 1, "Actual number of calls/primitive calls " "(not induced via recursion)" ) headerItem.setToolTip( 2, "Total time spent in function " "(excluding time made in calls " "to sub-functions)" ) headerItem.setToolTip( 3, "Total time divided by number " "of actual calls" ) headerItem.setToolTip( 4, "Total time spent in function and all " "subfunctions (from invocation till exit)" ) headerItem.setToolTip( 5, "Cumulative time divided by number " "of primitive calls" ) headerItem.setToolTip( 6, "Function location" ) headerItem.setToolTip( 7, "Function name" ) headerItem.setToolTip( 8, "Function callers" ) headerItem.setToolTip( 9, "Function callees" ) self.__table.itemActivated.connect( self.__activated ) totalCalls = self.__stats.total_calls totalPrimitiveCalls = self.__stats.prim_calls # The calls were not induced via recursion totalTime = self.__stats.total_tt txt = "<b>Script:</b> " + self.__script + " " + params.arguments + "<br>" \ "<b>Run at:</b> " + reportTime + "<br>" + \ str( totalCalls ) + " function calls (" + \ str( totalPrimitiveCalls ) + " primitive calls) in " + \ FLOAT_FORMAT % totalTime + " CPU seconds" summary = QLabel( txt ) summary.setToolTip( txt ) summary.setSizePolicy( QSizePolicy.Ignored, QSizePolicy.Fixed ) summary.setFrameStyle( QFrame.StyledPanel ) summary.setAutoFillBackground( True ) summaryPalette = summary.palette() summaryBackground = summaryPalette.color( QPalette.Background ) summaryBackground.setRgb( min( summaryBackground.red() + 30, 255 ), min( summaryBackground.green() + 30, 255 ), min( summaryBackground.blue() + 30, 255 ) ) summaryPalette.setColor( QPalette.Background, summaryBackground ) summary.setPalette( summaryPalette ) vLayout = QVBoxLayout() vLayout.setContentsMargins( 0, 0, 0, 0 ) vLayout.setSpacing( 0 ) vLayout.addWidget( summary ) vLayout.addWidget( self.__table ) self.setLayout( vLayout ) self.__createContextMenu() self.__populate( totalTime ) return def __onEsc( self ): " Triggered when Esc is pressed " self.escapePressed.emit() return def __createContextMenu( self ): " Creates context menu for the table raws " self.__contextMenu = QMenu( self ) self.__callersMenu = QMenu( "Callers", self ) self.__outsideCallersMenu = QMenu( "Outside callers", self ) self.__calleesMenu = QMenu( "Callees", self ) self.__outsideCalleesMenu = QMenu( "Outside callees", self ) self.__contextMenu.addMenu( self.__callersMenu ) self.__contextMenu.addMenu( self.__outsideCallersMenu ) self.__contextMenu.addSeparator() self.__contextMenu.addMenu( self.__calleesMenu ) self.__contextMenu.addMenu( self.__outsideCalleesMenu ) self.__contextMenu.addSeparator() self.__disasmAct = self.__contextMenu.addAction( PixmapCache().getIcon( 'disasmmenu.png' ), "Disassemble", self.__onDisassemble ) self.__callersMenu.triggered.connect( self.__onCallContextMenu ) self.__outsideCallersMenu.triggered.connect( self.__onCallContextMenu ) self.__calleesMenu.triggered.connect( self.__onCallContextMenu ) self.__outsideCalleesMenu.triggered.connect( self.__onCallContextMenu ) self.__table.setContextMenuPolicy( Qt.CustomContextMenu ) self.__table.customContextMenuRequested.connect( self.__showContextMenu ) return def __showContextMenu( self, point ): " Context menu " self.__callersMenu.clear() self.__outsideCallersMenu.clear() self.__calleesMenu.clear() self.__outsideCalleesMenu.clear() # Detect what the item was clicked item = self.__table.itemAt( point ) funcName = item.getFunctionName() self.__disasmAct.setEnabled( item.getFileName() != "" and \ not funcName.startswith( "<" ) ) # Build the context menu if item.callersCount() == 0: self.__callersMenu.setEnabled( False ) self.__outsideCallersMenu.setEnabled( False ) else: callers = self.__stats.stats[ item.getFuncIDs() ][ 4 ] callersList = callers.keys() callersList.sort() for callerFunc in callersList: menuText = self.__getCallLine( callerFunc, callers[ callerFunc ] ) if self.__isOutsideItem( callerFunc[ 0 ] ): act = self.__outsideCallersMenu.addAction( menuText ) else: act = self.__callersMenu.addAction( menuText ) funcFileName, funcLine, funcName = self.__getLocationAndName( callerFunc ) act.setData( QVariant( funcFileName + ":" + \ str( funcLine ) + ":" + \ funcName ) ) self.__callersMenu.setEnabled( not self.__callersMenu.isEmpty() ) self.__outsideCallersMenu.setEnabled( not self.__outsideCallersMenu.isEmpty() ) if item.calleesCount() == 0: self.__calleesMenu.setEnabled( False ) self.__outsideCalleesMenu.setEnabled( False ) else: callees = self.__stats.all_callees[ item.getFuncIDs() ] calleesList = callees.keys() calleesList.sort() for calleeFunc in calleesList: menuText = self.__getCallLine( calleeFunc, callees[ calleeFunc ] ) if self.__isOutsideItem( calleeFunc[ 0 ] ): act = self.__outsideCalleesMenu.addAction( menuText ) else: act = self.__calleesMenu.addAction( menuText ) funcFileName, funcLine, funcName = self.__getLocationAndName( calleeFunc ) act.setData( QVariant( funcFileName + ":" + \ str( funcLine ) + ":" + \ funcName ) ) self.__calleesMenu.setEnabled( not self.__calleesMenu.isEmpty() ) self.__outsideCalleesMenu.setEnabled( not self.__outsideCalleesMenu.isEmpty() ) self.__contextMenu.popup( QCursor.pos() ) return def __onDisassemble( self ): " On disassemble something " item = self.__table.selectedItems()[ 0 ] GlobalData().mainWindow.showDisassembler( item.getFileName(), item.getFunctionName() ) return def __resize( self ): " Resizes columns to the content " self.__table.header().resizeSections( QHeaderView.ResizeToContents ) self.__table.header().setStretchLastSection( True ) self.__table.header().resizeSection( OUTSIDE_COL_INDEX, 28 ) self.__table.header().setResizeMode( OUTSIDE_COL_INDEX, QHeaderView.Fixed ) return def setFocus( self ): " Set focus to the proper widget " self.__table.setFocus() return def __isOutsideItem( self, fileName ): " Detects if the record should be shown as an outside one " return not fileName.startswith( self.__projectPrefix ) def __activated( self, item, column ): " Triggered when the item is activated " try: line = item.getLineNumber() fileName = item.getFileName() if line == 0 or not os.path.isabs( fileName ): return GlobalData().mainWindow.openFile( fileName, line ) except: logging.error( "Could not jump to function location" ) return @staticmethod def __getFuncShortLocation( funcFileName, funcLine ): " Provides unified shortened function location " if funcFileName == "": return "" return os.path.basename( funcFileName ) + ":" + str( funcLine ) def __getCallLine( self, func, props ): " Provides the formatted call line " funcFileName, funcLine, funcName = self.__getLocationAndName( func ) if isinstance( props, tuple ): actualCalls, primitiveCalls, totalTime, cumulativeTime = props if primitiveCalls != actualCalls: callsString = str( actualCalls ) + "/" + str( primitiveCalls ) else: callsString = str( actualCalls ) return callsString + "\t" + FLOAT_FORMAT % totalTime + "\t" + \ FLOAT_FORMAT % cumulativeTime + "\t" + \ self.__getFuncShortLocation( funcFileName, funcLine ) + \ "(" + funcName + ")" # I've never seen this branch working so it is just in case. # There was something like this in the pstats module return self.__getFuncShortLocation( funcFileName, funcLine ) + \ "(" + funcName + ")" @staticmethod def __getLocationAndName( func ): " Provides standardized function file name, line and its name " if func[ : 2 ] == ( '~', 0 ): # special case for built-in functions name = func[ 2 ] if name.startswith( '<' ) and name.endswith( '>' ): return "", 0, "{%s}" % name[ 1 : -1 ] return "", 0, name return func[ 0 ], func[ 1 ], func[ 2 ] def __createItem( self, func, totalCPUTime, primitiveCalls, actualCalls, totalTime, cumulativeTime, timePerCall, cumulativeTimePerCall, callers ): " Creates an item to display " values = [] values.append( "" ) if primitiveCalls == actualCalls: values.append( str( actualCalls ) ) else: values.append( str( actualCalls ) + "/" + str( primitiveCalls ) ) if totalCPUTime == 0.0: values.append( FLOAT_FORMAT % totalTime ) else: values.append( FLOAT_FORMAT % totalTime + " \t(%3.2f%%)" % (totalTime / totalCPUTime * 100) ) values.append( FLOAT_FORMAT % timePerCall ) values.append( FLOAT_FORMAT % cumulativeTime ) values.append( FLOAT_FORMAT % cumulativeTimePerCall ) # Location and name will be filled in the item constructor values.append( "" ) values.append( "" ) # Callers callersCount = len( callers ) values.append( str( callersCount ) ) # Callees if func in self.__stats.all_callees: calleesCount = len( self.__stats.all_callees[ func ] ) else: calleesCount = 0 values.append( str( calleesCount ) ) item = ProfilingTableItem( values, self.__isOutsideItem( func[ 0 ] ), func ) if callersCount != 0: tooltip = "" callersList = callers.keys() callersList.sort() for callerFunc in callersList[ : MAX_CALLS_IN_TOOLTIP ]: if tooltip != "": tooltip += "\n" tooltip += self.__getCallLine( callerFunc, callers[ callerFunc ] ) if callersCount > MAX_CALLS_IN_TOOLTIP: tooltip += "\n. . ." item.setToolTip( 8, tooltip ) if calleesCount != 0: callees = self.__stats.all_callees[ func ] tooltip = "" calleesList = callees.keys() calleesList.sort() for calleeFunc in calleesList[ : MAX_CALLS_IN_TOOLTIP ]: if tooltip != "": tooltip += "\n" tooltip += self.__getCallLine( calleeFunc, callees[ calleeFunc ] ) if calleesCount > MAX_CALLS_IN_TOOLTIP: tooltip += "\n. . ." item.setToolTip( 9, tooltip ) self.__table.addTopLevelItem( item ) return def __populate( self, totalCPUTime ): " Populates the data " for func, ( primitiveCalls, actualCalls, totalTime, cumulativeTime, callers ) in self.__stats.stats.items(): # Calc time per call if actualCalls == 0: timePerCall = 0.0 else: timePerCall = totalTime / actualCalls # Calc time per cummulative call if primitiveCalls == 0: cumulativeTimePerCall = 0.0 else: cumulativeTimePerCall = cumulativeTime / primitiveCalls self.__createItem( func, totalCPUTime, primitiveCalls, actualCalls, totalTime, cumulativeTime, timePerCall, cumulativeTimePerCall, callers ) self.__resize() self.__table.header().setSortIndicator( 2, Qt.DescendingOrder ) self.__table.sortItems( 2, self.__table.header().sortIndicatorOrder() ) return def togglePath( self, state ): " Switches between showing full paths or file names in locations " for index in xrange( 0, self.__table.topLevelItemCount() ): self.__table.topLevelItem( index ).updateLocation( state ) self.__resize() return def __onCallContextMenu( self, act ): " Triggered when a context menu action is requested " name = str( act.data().toString() ) for index in xrange( 0, self.__table.topLevelItemCount() ): item = self.__table.topLevelItem( index ) if item.match( name ): self.__table.clearSelection() self.__table.scrollToItem( item ) self.__table.setCurrentItem( item ) return def onSaveAs( self, fileName ): " Saves the table to a file in CSV format " try: f = open( fileName, "wt" ) headerItem = self.__table.headerItem() outsideIndex = -1 for index in xrange( 0, headerItem.columnCount() ): title = str( headerItem.text( index ) ) if title == "": outsideIndex = index title = "Outside" if index != 0: f.write( ',' + title ) else: f.write( title ) for index in xrange( 0, self.__table.topLevelItemCount() ): item = self.__table.topLevelItem( index ) f.write( "\n" ) for column in xrange( 0, item.columnCount() ): if column != 0: f.write( ',' ) if column == outsideIndex: if item.isOutside(): f.write( "Y" ) else: f.write( "N" ) else: text = str( item.text( column ) ) f.write( text.replace( '\t', '' ) ) f.close() except Exception, ex: logging.error( ex ) return
class Main(plugin.Plugin): " Main Class " def initialize(self, *args, **kwargs): " Init Main Class " super(Main, self).initialize(*args, **kwargs) self.infile = QLineEdit(path.expanduser("~")) self.infile.setPlaceholderText(' /full/path/to/file ') self.infile.returnPressed.connect(self.run) self.completer, self.dirs = QCompleter(self), QDirModel(self) self.dirs.setFilter(QDir.AllEntries | QDir.NoDotAndDotDot) self.completer.setModel(self.dirs) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.infile.setCompleter(self.completer) self.menu = QMenu('Base64') self.menu.aboutToShow.connect(self.build_submenu) self.ex_locator = self.locator.get_service('explorer') self.ex_locator.add_project_menu(self.menu, lang='all') self.open = QPushButton(QIcon.fromTheme("folder-open"), 'Open') self.open.setCursor(QCursor(Qt.PointingHandCursor)) self.open.clicked.connect(lambda: self.infile.setText(str( QFileDialog.getOpenFileName(self.dock, "Open a File to Encode...", path.expanduser("~"), ';;'.join(['{}(*.{})'.format(e.upper(), e) for e in ['*', 'jpg', 'png', 'webp', 'svg', 'gif', 'webm']]))))) self.chckbx1 = QCheckBox('Use basic Caesar Cipher (ROT13)') self.chckbx1.setToolTip('Use "string".decode("rot13") to Decipher ! ') self.chckbx2 = QCheckBox('Use "data:type/subtype;base64,..."') self.chckbx2.setChecked(True) self.chckbx3 = QCheckBox('Copy encoded output to Clipboard') self.chckbx4 = QCheckBox('Use URL-Safe Base64 Encoder') self.combo1 = QComboBox() self.combo1.addItems(['Do Not Generate Code', 'Generate CSS embed Code', 'Generate Python Embed Code', 'Generate HTML embed Code', 'Generate JS embed Code', 'Generate QML embed Code']) self.combo1.currentIndexChanged.connect(self.combo_changed) self.output = QTextEdit(''' We can only see a short distance ahead, but we can see plenty there that needs to be done. - Alan Turing ''') self.output.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.button = QPushButton(QIcon.fromTheme("face-cool"), 'Encode BASE64') self.button.setCursor(QCursor(Qt.PointingHandCursor)) self.button.setMinimumSize(100, 50) self.button.clicked.connect(self.run) glow = QGraphicsDropShadowEffect(self) glow.setOffset(0) glow.setBlurRadius(99) glow.setColor(QColor(99, 255, 255)) self.button.setGraphicsEffect(glow) glow.setEnabled(True) class TransientWidget(QWidget): ' persistant widget thingy ' def __init__(self, widget_list): ' init sub class ' super(TransientWidget, self).__init__() vbox = QVBoxLayout(self) for each_widget in widget_list: vbox.addWidget(each_widget) tw = TransientWidget((QLabel('<i>Encode file as plain text string</i>'), QLabel('<b>File to Encode:'), self.infile, self.open, self.chckbx2, self.chckbx3, self.chckbx1, self.chckbx4, QLabel('<b>Embedding Template Code:'), self.combo1, QLabel(' <b>Base64 String Output: '), self.output, QLabel('<center><small><i>' + ''.join((__doc__, __version__, __license__, 'by', __author__))), self.button )) self.scrollable, self.dock = QScrollArea(), QDockWidget() self.scrollable.setWidgetResizable(True) self.scrollable.setWidget(tw) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.dock.setWidget(self.scrollable) ExplorerContainer().addTab(self.dock, "Base64") self.guimode = QComboBox(self.dock) self.guimode.addItems(['Full Mode', 'Simple Mode']) self.guimode.currentIndexChanged.connect(self.guimode_change) def guimode_change(self): """ Change from Simple Mode to Full Mode by Hide or Show Widgets """ if self.guimode.currentIndex() is 0: self.chckbx1.show() self.chckbx2.show() self.chckbx3.show() self.chckbx4.show() else: self.chckbx1.hide() self.chckbx2.hide() self.chckbx3.hide() self.chckbx4.hide() self.chckbx1.setChecked(False) self.chckbx2.setChecked(True) self.chckbx3.setChecked(False) self.chckbx4.setChecked(False) def build_submenu(self): ''' build sub menu on the fly based on file path ''' self.menu.clear() if self.ex_locator.get_current_project_item().isFolder is not True: filenam = self.ex_locator.get_current_project_item().get_full_path() self.menu.addActions([ QAction('Copy {} as Base64'.format(path.basename(filenam)[:50]), self, triggered=lambda: QApplication.clipboard().setText( '"data:{};charset=utf-8;base64,{}"'.format( guess_type(filenam, strict=False)[0], b64encode(open(filenam, "rb").read())))), QAction('Copy {} as Base64 URL-Safe'.format( path.basename(filenam)[:50]), self, triggered=lambda: QApplication.clipboard().setText( '"data:{};charset=utf-8;base64,{}"'.format( guess_type(filenam, strict=False)[0], urlsafe_b64encode(open(filenam, "rb").read()))))]) self.menu.show() def run(self): ' run the encoding ' mimetype = guess_type(str(self.infile.text()).strip(), strict=False)[0] _mime = mimetype if mimetype is not None else self.ask_mime() fle = str(self.infile.text()).strip().replace('file:///', '/') encoder = urlsafe_b64encode if self.chckbx4.isChecked() else b64encode if int(path.getsize(fle)) / 1024 / 1024 >= 1: QMessageBox.information(self.dock, __doc__, '''<b style="color:red"> WARNING!: File size is > 1 Megabyte!,<br> this will take some time, depending your CPU Processing power!.''') output = '"{}{}{}{}"'.format( 'data:' if self.chckbx2.isChecked() is True else '', _mime if self.chckbx2.isChecked() is True else '', ';charset=utf-8;base64,' if self.chckbx2.isChecked() is True else '', encoder(open(fle, "rb").read())) if self.combo1.currentIndex() is 1: output = ('html, body { margin:0; padding:0; background: url(' + output + ') no-repeat center center fixed; background-size:cover }') elif self.combo1.currentIndex() is 2: output = PY_EMBED.format(getuser(), datetime.now().isoformat().split('.')[0], output) elif self.combo1.currentIndex() is 3: output = '<img src={} alt="{}" title="{}"/>'.format(output, fle.split(sep)[-1], fle.split(sep)[-1]) elif self.combo1.currentIndex() is 4: output = 'var embedded_file = window.atob({}); '.format(output) elif self.combo1.currentIndex() is 5: output = 'Image { source: ' + output + ' } ' if self.chckbx1.isChecked() is True: output = str(output).encode('rot13') if self.chckbx3.isChecked() is True: QApplication.clipboard().setText(output) self.output.setPlainText(output) self.output.setFocus() self.output.selectAll() def ask_mime(self): ' ask user for mime type ' return str(QInputDialog.getText(self.dock, __doc__, 'Write a MIME-Type', QLineEdit.Normal, 'application/octet-stream')[0]).strip().lower() def combo_changed(self): ' on combo changed ' if self.combo1.currentIndex() is 1 or self.combo1.currentIndex() is 3: self.chckbx1.setChecked(False) self.chckbx2.setChecked(True) elif self.combo1.currentIndex() is 2 or self.combo1.currentIndex() is 4: self.chckbx1.setChecked(False) self.chckbx2.setChecked(False)
class OneColumnTree(QTreeWidget): def __init__(self, parent): QTreeWidget.__init__(self, parent) self.setItemsExpandable(True) self.setColumnCount(1) self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*,int)'), self.activated) self.connect(self, SIGNAL('itemClicked(QTreeWidgetItem*,int)'), self.clicked) # Setup context menu self.menu = QMenu(self) self.collapse_all_action = None self.collapse_selection_action = None self.expand_all_action = None self.expand_selection_action = None self.common_actions = self.setup_common_actions() self.__expanded_state = None self.connect(self, SIGNAL('itemSelectionChanged()'), self.item_selection_changed) self.item_selection_changed() def activated(self): raise NotImplementedError def clicked(self): pass def set_title(self, title): self.setHeaderLabels([title]) def setup_common_actions(self): """Setup context menu common actions""" self.collapse_all_action = create_action(self, text=translate('OneColumnTree', 'Collapse all'), icon=get_icon('collapse.png'), triggered=self.collapseAll) self.expand_all_action = create_action(self, text=translate('OneColumnTree', 'Expand all'), icon=get_icon('expand.png'), triggered=self.expandAll) self.restore_action = create_action(self, text=translate('OneColumnTree', 'Restore'), tip=translate('OneColumnTree', 'Restore original tree layout'), icon=get_icon('restore.png'), triggered=self.restore) self.collapse_selection_action = create_action(self, text=translate('OneColumnTree', 'Collapse selection'), icon=get_icon('collapse_selection.png'), triggered=self.collapse_selection) self.expand_selection_action = create_action(self, text=translate('OneColumnTree', 'Expand selection'), icon=get_icon('expand_selection.png'), triggered=self.expand_selection) return [self.collapse_all_action, self.expand_all_action, self.restore_action, None, self.collapse_selection_action, self.expand_selection_action] def update_menu(self): self.menu.clear() items = self.selectedItems() actions = self.get_actions_from_items(items) if actions: actions.append(None) actions += self.common_actions add_actions(self.menu, actions) def get_actions_from_items(self, items): # Right here: add other actions if necessary # (reimplement this method) return [] def restore(self): self.collapseAll() for item in self.get_top_level_items(): self.expandItem(item) def is_item_expandable(self, item): """To be reimplemented in child class See example in project explorer widget""" return True def __expand_item(self, item): if self.is_item_expandable(item): self.expandItem(item) for index in range(item.childCount()): child = item.child(index) self.__expand_item(child) def expand_selection(self): items = self.selectedItems() if not items: items = self.get_top_level_items() for item in items: self.__expand_item(item) if items: self.scrollToItem(items[0]) def __collapse_item(self, item): self.collapseItem(item) for index in range(item.childCount()): child = item.child(index) self.__collapse_item(child) def collapse_selection(self): items = self.selectedItems() if not items: items = self.get_top_level_items() for item in items: self.__collapse_item(item) if items: self.scrollToItem(items[0]) def item_selection_changed(self): """Item selection has changed""" is_selection = len(self.selectedItems()) > 0 self.expand_selection_action.setEnabled(is_selection) self.collapse_selection_action.setEnabled(is_selection) def get_top_level_items(self): """Iterate over top level items""" return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())] def get_items(self): """Return items (excluding top level items)""" itemlist = [] def add_to_itemlist(item): for index in range(item.childCount()): citem = item.child(index) itemlist.append(citem) add_to_itemlist(citem) for tlitem in self.get_top_level_items(): add_to_itemlist(tlitem) return itemlist def get_scrollbar_position(self): return (self.horizontalScrollBar().value(), self.verticalScrollBar().value()) def set_scrollbar_position(self, position): hor, ver = position self.horizontalScrollBar().setValue(hor) self.verticalScrollBar().setValue(ver) def get_expanded_state(self): self.save_expanded_state() return self.__expanded_state def set_expanded_state(self, state): self.__expanded_state = state self.restore_expanded_state() def save_expanded_state(self): """Save all items expanded state""" self.__expanded_state = {} def add_to_state(item): user_text = get_item_user_text(item) self.__expanded_state[user_text] = item.isExpanded() def browse_children(item): add_to_state(item) for index in range(item.childCount()): citem = item.child(index) user_text = get_item_user_text(citem) self.__expanded_state[user_text] = citem.isExpanded() browse_children(citem) for tlitem in self.get_top_level_items(): browse_children(tlitem) def restore_expanded_state(self): """Restore all items expanded state""" if self.__expanded_state is None: return for item in self.get_items()+self.get_top_level_items(): user_text = get_item_user_text(item) is_expanded = self.__expanded_state.get(user_text) if is_expanded is not None: item.setExpanded(is_expanded) def sort_top_level_items(self, key): """Sorting tree wrt top level items""" self.save_expanded_state() items = sorted([self.takeTopLevelItem(0) for index in range(self.topLevelItemCount())], key=key) for index, item in enumerate(items): self.insertTopLevelItem(index, item) self.restore_expanded_state() def contextMenuEvent(self, event): """Override Qt method""" self.update_menu() self.menu.popup(event.globalPos())
class Plugin(QObject): def __init__(self, iface): QObject.__init__(self) self.__iface = iface self.__shortcuts = [] self.__current_section = QComboBox() self.__current_section.setMinimumWidth(150) self.__current_graph = QComboBox() self.__current_graph.setMinimumWidth(150) self.__toolbar = None self.__axis_layer = None self.__menu = None self.__log_strati = None def initGui(self): for keyseq, slot in ( (Qt.CTRL + Qt.ALT + Qt.Key_K, self.__create_group), (Qt.CTRL + Qt.ALT + Qt.Key_S, self.__select_next_group), (Qt.CTRL + Qt.ALT + Qt.Key_N, self.__next_section), (Qt.CTRL + Qt.ALT + Qt.Key_B, self.__previous_section) ): short = QShortcut(QKeySequence(keyseq), self.__iface.mainWindow()) short.setContext(Qt.ApplicationShortcut) short.activated.connect(slot) self.__shortcuts.append(short) self.__menu = QMenu("Albion") self.__menu.aboutToShow.connect(self.__create_menu_entries) self.__iface.mainWindow().menuBar().addMenu(self.__menu) self.__toolbar = QToolBar('Albion') self.__iface.addToolBar(self.__toolbar) self.__toolbar.addAction(icon('log_strati.svg'), 'stratigraphic log').triggered.connect(self.__log_strati_clicked) self.__toolbar.addWidget(self.__current_graph) self.__current_graph.currentIndexChanged[unicode].connect(self.__current_graph_changed) self.__toolbar.addWidget(self.__current_section) self.__current_section.currentIndexChanged[unicode].connect(self.__current_section_changed) self.__toolbar.addAction(icon('previous_line.svg'), 'previous section (Ctrl+Alt+b)').triggered.connect(self.__previous_section) self.__toolbar.addAction(icon('next_line.svg'), 'next section (Ctrl+Alt+n)').triggered.connect(self.__next_section) self.__toolbar.addAction(icon('line_from_selected.svg'), 'create temporary section').triggered.connect(self.__section_from_selection) self.__viewer3d = QDockWidget('3D') self.__viewer3d.setWidget(Viewer3d()) self.__iface.addDockWidget(Qt.LeftDockWidgetArea, self.__viewer3d) self.__iface.mainWindow().tabifyDockWidget( self.__iface.mainWindow().findChild(QDockWidget, "Layers"), self.__viewer3d) self.__viewer3d_ctrl = QToolBar('3D controls') self.__viewer3d_ctrl.addWidget(ViewerControls(self.__viewer3d.widget())) self.__iface.addToolBar(self.__viewer3d_ctrl) QgsProject.instance().readProject.connect(self.__qgis__project__loaded) self.__qgis__project__loaded() # case of reload def unload(self): for s in self.__shortcuts: s.setParent(None) self.__toolbar and self.__toolbar.setParent(None) self.__menu and self.__menu.setParent(None) self.__viewer3d and self.__viewer3d.setParent(None) self.__viewer3d_ctrl and self.__viewer3d_ctrl.setParent(None) def __add_menu_entry(self, name, callback, enabled=True, help_str=''): act = self.__menu.addAction(name) if callback is not None: act.triggered.connect(callback) act.setEnabled(enabled) act.setToolTip(help_str) else: act.setEnabled(False) act.setToolTip("NOT INMPLEMENTED "+help_str) return act def __create_menu_entries(self): self.__menu.clear() self.__add_menu_entry('New &Project', self.__new_project) self.__add_menu_entry('Import Project', self.__import_project) self.__add_menu_entry('Upgrade Project', self.__upgrade_project) self.__menu.addSeparator() self.__add_menu_entry('&Import Data', self.__import_data, self.project is not None, "Import data from directory. The data files are in ") self.__menu.addSeparator() self.__add_menu_entry('Create cells', self.__create_cells, self.project is not None and self.project.has_collar, "Create Delaunay triangulation of collar layer.") self.__add_menu_entry('Refresh all edges', self.__refresh_all_edge, self.project is not None and self.project.has_cell, "Refresh materialized view of all cell edges used by graph possible edges.") self.__menu.addSeparator() #self.__add_menu_entry(u'Create section views 0° and 90°', self.__create_section_view_0_90, # False and self.project is not None and self.project.has_hole, # "Create section views (i.e. cut directions) to work West-East and South-North for this project") #self.__add_menu_entry(u'Create section views -45° and 45°', None, # False and self.project is not None and self.project.has_hole, # "Create section views (i.e. cut directions) to work SE-NW and SW-NE for this project") # #self.__menu.addSeparator() self.__add_menu_entry('Create sections', self.__create_sections, self.project is not None and self.project.has_group_cell, "Once cell groups have been defined, create section lines.") self.__menu.addSeparator() self.__add_menu_entry('Compute &Mineralization', self.__compute_mineralization, self.project is not None and self.project.has_radiometry, "") self.__menu.addSeparator() self.__add_menu_entry('New &Graph', self.__new_graph, self.project is not None, "Create a new grap.h") self.__add_menu_entry('Delete Graph', self.__delete_graph, self.project is not None) self.__menu.addSeparator() self.__add_menu_entry('Create volumes', self.__create_volumes, self.project is not None and bool(self.__current_graph.currentText()), "Create volumes associated with current graph.") self.__menu.addSeparator() self.__add_menu_entry('Create terminations', self.__create_terminations, self.project is not None and bool(self.__current_graph.currentText()), "Create terminations associated with current graph.") self.__menu.addSeparator() self.__add_menu_entry('Toggle axis', self.__toggle_axis) self.__menu.addSeparator() self.__add_menu_entry('Export Project', self.__export_project, self.project is not None) self.__add_menu_entry('Export Sections', None, self.project is not None and self.project.has_section, "Export triangulated section in .obj or .dxf format") self.__add_menu_entry('Export Volume', self.__export_volume, self.project is not None and bool(self.__current_graph.currentText()), "Export volume of current graph in .obj or .dxf format") self.__menu.addSeparator() self.__menu.addAction('Help').triggered.connect(self.open_help) def __current_graph_changed(self, graph_id): if self.project is None: return self.__viewer3d.widget().set_graph(graph_id) def __getattr__(self, name): if name == "project": project_name = QgsProject.instance().readEntry("albion", "project_name", "")[0] return Project(project_name) if project_name else None else: raise AttributeError(name) def __refresh_all_edge(self): if self.project is None: return self.project.refresh_all_edge() def __create_terminations(self): if self.project is None: return self.project.create_terminations(self.__current_graph.currentText()) self.__viewer3d.widget().refresh_data() self.__refresh_layers('section') def __create_volumes(self): if self.project is None: return self.project.create_volumes(self.__current_graph.currentText()) self.__viewer3d.widget().refresh_data() def __next_section(self): if self.project is None: return self.project.next_section(self.__current_section.currentText()) self.__refresh_layers('section') self.__viewer3d.widget().scene.update('section') self.__viewer3d.widget().update() def __previous_section(self): if self.project is None: return self.project.previous_section(self.__current_section.currentText()) self.__refresh_layers('section') self.__viewer3d.widget().scene.update('section') self.__viewer3d.widget().update() def __refresh_layers(self, name=None): for layer in self.__iface.mapCanvas().layers(): if name is None or layer.name().find(name) != -1: layer.triggerRepaint() def __current_section_changed(self, section_id): layers = QgsMapLayerRegistry.instance().mapLayersByName(u"group_cell") if len(layers): layers[0].setSubsetString("section_id='{}'".format(section_id)) self.__refresh_layers('section') def __select_next_group(self): if self.project and self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"cell": self.__iface.activeLayer().removeSelection() self.__iface.activeLayer().selectByExpression( "id in ({})".format(",".join(project.next_group_ids()))) def __create_group(self): if self.project and self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"cell": if self.__iface.activeLayer().selectedFeatureCount(): section = self.__current_section.currentText() self.project.create_group(section, [f['id'] for f in self.__iface.activeLayer().selectedFeatures()]) self.__iface.activeLayer().removeSelection() self.__refresh_layers('group_cell') def __qgis__project__loaded(self): if self.project is None: return self.__current_graph.clear() self.__current_section.clear() self.__current_section.addItems(self.project.sections()) self.__current_graph.addItems(self.project.graphs()) layers = QgsMapLayerRegistry.instance().mapLayersByName('section.anchor') if len(layers): layers[0].editingStopped.connect(self.__update_section_list) self.__viewer3d.widget().resetScene(self.project) def __update_section_list(self): self.__current_section.clear() self.__current_section.addItems(self.project.sections()) def __upgrade_project(self): project_name, ok = QInputDialog.getText(self.__iface.mainWindow(), "Database name", "Database name:", QLineEdit.Normal, '') if not ok: return Project(project_name).update() def __new_project(self): # @todo open dialog to configure project name and srid fil = QFileDialog.getSaveFileName(None, u"New project name (no space, plain ascii)", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "QGIS poject file (*.qgs)") if not fil: return fil = fil if len(fil)>4 and fil[-4:]=='.qgs' else fil+'.qgs' fil = fil.replace(' ','_') try: fil.decode('ascii') except UnicodeDecodeError: self.__iface.messageBar().pushError('Albion:', "project name may only contain asci character (no accent)") return srid, ok = QInputDialog.getText(self.__iface.mainWindow(), "Project SRID", "Project SRID EPSG:", QLineEdit.Normal, '32632') if not ok: return srid = int(srid) project_name = str(os.path.split(fil)[1][:-4]) if Project.exists(project_name): if QMessageBox.Yes != QMessageBox(QMessageBox.Information, "Delete existing DB", "Database {} exits, to you want to delete it ?".format(project_name), QMessageBox.Yes|QMessageBox.No).exec_(): return Project.delete(project_name) self.__iface.messageBar().pushInfo('Albion:', "creating project...") Project.create(project_name, srid) # load template open(fil, 'w').write( open(resource('template_project.qgs')).read().replace('template_project', project_name)) self.__iface.newProject() QgsProject.instance().setFileName(fil) QgsProject.instance().read() QgsProject.instance().writeEntry("albion", "project_name", project_name) QgsProject.instance().writeEntry("albion", "srid", srid) QgsProject.instance().write() self.__qgis__project__loaded() def __import_data(self): if self.project is None: return if not QgsProject.instance().readEntry("albion", "conn_info", "")[0]: return dir_ = QFileDialog.getExistingDirectory(None, u"Data directory", QgsProject.instance().readEntry("albion", "last_dir", "")[0]) if not dir_: return QgsProject.instance().writeEntry("albion", "last_dir", dir_), progressMessageBar = self.__iface.messageBar().createMessage("Loading {}...".format(dir_)) progress = QProgressBar() progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) progressMessageBar.layout().addWidget(progress) self.__iface.messageBar().pushWidget(progressMessageBar, self.__iface.messageBar().INFO) self.project.import_data(dir_, ProgressBar(progress)) self.project.triangulate() self.project.create_section_view_0_90(4) self.__iface.messageBar().clearWidgets() collar = QgsMapLayerRegistry.instance().mapLayersByName('collar')[0] collar.reload() collar.updateExtents() self.__iface.setActiveLayer(collar) QApplication.instance().processEvents() while self.__iface.mapCanvas().isDrawing(): QApplication.instance().processEvents() self.__iface.zoomToActiveLayer() self.__iface.actionSaveProject().trigger() self.__viewer3d.widget().resetScene(self.project) self.__current_section.clear() self.__current_section.addItems(self.project.sections()) def __new_graph(self): if self.project is None: return graph, ok = QInputDialog.getText(self.__iface.mainWindow(), "Graph", "Graph name:", QLineEdit.Normal, 'test_graph') if not ok: return parent, ok = QInputDialog.getText(self.__iface.mainWindow(), "Parent Graph", "Parent Graph name:", QLineEdit.Normal, '') if not ok: return self.project.new_graph(graph, parent) self.__current_graph.addItem(graph) self.__current_graph.setCurrentIndex(self.__current_graph.findText(graph)) def __delete_graph(self): if self.project is None: return graph, ok = QInputDialog.getText(self.__iface.mainWindow(), "Graph", "Graph name:", QLineEdit.Normal, self.__current_graph.currentText()) if not ok: return self.__current_graph.removeItem(self.__current_graph.findText(graph)) def __toggle_axis(self): if self.__axis_layer: pass QgsMapLayerRegistry.instance().removeMapLayer(self.__axis_layer.id()) self.__axis_layer = None else: self.__axis_layer = AxisLayer(self.__iface.mapCanvas().mapSettings().destinationCrs()) QgsMapLayerRegistry.instance().addMapLayer(self.__axis_layer) self.__refresh_layers() def __create_cells(self): if self.project is None: return self.project.triangulate() self.__refresh_layers('cells') def __create_sections(self): if self.project is None: return self.project.create_sections() def __compute_mineralization(self): MineralizationDialog(self.project).exec_() def __export_volume(self): if self.project is None: return fil = QFileDialog.getSaveFileName(None, u"Export volume for current graph", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "File formats (*.dxf *.obj)") if not fil: return QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)), if fil[-4:] == '.obj': self.project.export_obj(self.__current_graph.currentText(), fil) elif fil[-4:] == '.dxf': self.project.export_dxf(self.__current_graph.currentText(), fil) else: self.__iface.messageBar().pushWarning('Albion', 'unsupported extension for volume export') def __import_project(self): fil = QFileDialog.getOpenFileName(None, u"Import project from file", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "File formats (*.zip)") if not fil: return QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)), if fil[-4:] != '.zip': self.__iface.messageBar().pushWarning('Albion', 'unsupported extension for import') project_name = os.path.split(fil)[1][:-4] dir_ = tempfile.gettempdir() with zipfile.ZipFile(fil, "r") as z: z.extractall(dir_) dump = find_in_dir(dir_, '.dump') prj = find_in_dir(dir_, '.qgs') self.__iface.messageBar().pushInfo('Albion', 'loading {} from {}'.format(project_name, dump)) if Project.exists(project_name): if QMessageBox.Yes != QMessageBox(QMessageBox.Information, "Delete existing DB", "Database {} exits, to you want to delete it ?".format(project_name), QMessageBox.Yes|QMessageBox.No).exec_(): return Project.delete(project_name) project = Project.import_(project_name, dump) QgsProject.instance().read(QFileInfo(prj)) def __export_project(self): if self.project is None: return fil = QFileDialog.getSaveFileName(None, u"Export project", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "Data files(*.zip)") if not fil: return QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)), if os.path.exists(fil): os.remove(fil) with zipfile.ZipFile(fil, 'w') as project: dump = tempfile.mkstemp()[1] self.project.export(dump) project.write(dump, self.project.name+'.dump') project.write(QgsProject.instance().fileName(), os.path.split(QgsProject.instance().fileName())[1]) def __create_section_view_0_90(self): if self.project is None: return self.project.create_section_view_0_90() def __log_strati_clicked(self): #@todo switch behavior when in section view -> ortho self.__click_tool = QgsMapToolEmitPoint(self.__iface.mapCanvas()) self.__iface.mapCanvas().setMapTool(self.__click_tool) self.__click_tool.canvasClicked.connect(self.__map_log_clicked) def __map_log_clicked(self, point, button): self.__click_tool.setParent(None) self.__click_tool = None if self.project is None: self.__log_strati and self.__log_strati.setParent(None) self.__log_strati = None return if self.__log_strati is None: self.__log_strati = QDockWidget('Stratigraphic Log') self.__log_strati.setWidget(BoreHoleWindow(self.project)) self.__iface.addDockWidget(Qt.LeftDockWidgetArea, self.__log_strati) self.__iface.mainWindow().tabifyDockWidget( self.__iface.mainWindow().findChild(QDockWidget, "Layers"), self.__log_strati) res = self.project.closest_hole_id(point.x(), point.y()) if res: self.__log_strati.widget().scene.set_current_id(res) self.__log_strati.show() self.__log_strati.raise_() def __section_from_selection(self): if self.project is None: return if self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"collar" \ and self.__iface.activeLayer().selectedFeatures(): collar = self.__iface.activeLayer() selection = collar.selectedFeatures() if len(selection) < 2: return def align(l): assert len(l) >= 2 res = numpy.array(l[:2]) for p in l[2:]: u, v = res[0] - res[1], p - res[1] if numpy.dot(u,v) < 0: res[1] = p elif numpy.dot(u, u) < numpy.dot(v,v): res[0] = p # align with ref direction sqrt2 = math.sqrt(2.)/2 l = l[numpy.argsort(numpy.dot(l-res[0], res[1]-res[0]))] d = numpy.array(l[-1] - l[0]) dr = numpy.array([(0,1),(sqrt2, sqrt2),(1,0), (sqrt2, -sqrt2)]) i = numpy.argmax(numpy.abs(dr.dot(d))) return l if (dr.dot(d))[i] > 0 else l[::-1] line = LineString(align(numpy.array([f.geometry().asPoint() for f in selection]))) collar.removeSelection() self.project.set_section_geom(self.__current_section.currentText(), line) self.__refresh_layers('section') self.__viewer3d.widget().scene.update('section') self.__viewer3d.widget().update() def open_help(self): QDesktopServices.openUrl(QUrl.fromLocalFile(os.path.join(os.path.dirname(__file__), 'doc', 'build', 'html', 'index.html')))
class TrayStarter(QSystemTrayIcon): """ Class implementing a starter for the system tray. """ def __init__(self): """ Constructor """ QSystemTrayIcon.__init__(self, UI.PixmapCache.getIcon( unicode(Preferences.getTrayStarter("TrayStarterIcon")))) self.maxMenuFilePathLen = 75 self.rsettings = QSettings(QSettings.IniFormat, QSettings.UserScope, Globals.settingsNameOrganization, Globals.settingsNameRecent) self.recentProjects = QStringList() self.__loadRecentProjects() self.recentMultiProjects = QStringList() self.__loadRecentMultiProjects() self.recentFiles = QStringList() self.__loadRecentFiles() self.connect(self, SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.__activated) self.__menu = QMenu(self.trUtf8("Eric4 tray starter")) self.recentProjectsMenu = QMenu(self.trUtf8('Recent Projects'), self.__menu) self.connect(self.recentProjectsMenu, SIGNAL('aboutToShow()'), self.__showRecentProjectsMenu) self.connect(self.recentProjectsMenu, SIGNAL('triggered(QAction *)'), self.__openRecent) self.recentMultiProjectsMenu = \ QMenu(self.trUtf8('Recent Multiprojects'), self.__menu) self.connect(self.recentMultiProjectsMenu, SIGNAL('aboutToShow()'), self.__showRecentMultiProjectsMenu) self.connect(self.recentMultiProjectsMenu, SIGNAL('triggered(QAction *)'), self.__openRecent) self.recentFilesMenu = QMenu(self.trUtf8('Recent Files'), self.__menu) self.connect(self.recentFilesMenu, SIGNAL('aboutToShow()'), self.__showRecentFilesMenu) self.connect(self.recentFilesMenu, SIGNAL('triggered(QAction *)'), self.__openRecent) act = self.__menu.addAction( self.trUtf8("Eric4 tray starter"), self.__about) font = act.font() font.setBold(True) act.setFont(font) self.__menu.addSeparator() self.__menu.addAction(self.trUtf8("QRegExp editor"), self.__startQRegExp) self.__menu.addAction(self.trUtf8("Python re editor"), self.__startPyRe) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("uiPreviewer.png"), self.trUtf8("UI Previewer"), self.__startUIPreviewer) self.__menu.addAction(UI.PixmapCache.getIcon("trPreviewer.png"), self.trUtf8("Translations Previewer"), self.__startTRPreviewer) self.__menu.addAction(UI.PixmapCache.getIcon("unittest.png"), self.trUtf8("Unittest"), self.__startUnittest) self.__menu.addAction(UI.PixmapCache.getIcon("ericWeb.png"), self.trUtf8("eric4 Web Browser"), self.__startHelpViewer) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("diffFiles.png"), self.trUtf8("Compare Files"), self.__startDiff) self.__menu.addAction(UI.PixmapCache.getIcon("compareFiles.png"), self.trUtf8("Compare Files side by side"), self.__startCompare) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("sqlBrowser.png"), self.trUtf8("SQL Browser"), self.__startSqlBrowser) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("iconEditor.png"), self.trUtf8("Icon Editor"), self.__startIconEditor) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("pluginInstall.png"), self.trUtf8("Install Plugin"), self.__startPluginInstall) self.__menu.addAction(UI.PixmapCache.getIcon("pluginUninstall.png"), self.trUtf8("Uninstall Plugin"), self.__startPluginUninstall) self.__menu.addAction(UI.PixmapCache.getIcon("pluginRepository.png"), self.trUtf8("Plugin Repository"), self.__startPluginRepository) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("configure.png"), self.trUtf8('Preferences'), self.__startPreferences) self.__menu.addAction(UI.PixmapCache.getIcon("erict.png"), self.trUtf8("eric4 IDE"), self.__startEric) self.__menu.addAction(UI.PixmapCache.getIcon("editor.png"), self.trUtf8("eric4 Mini Editor"), self.__startMiniEditor) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("configure.png"), self.trUtf8('Preferences (tray starter)'), self.__showPreferences) self.__menu.addSeparator() # recent files self.menuRecentFilesAct = self.__menu.addMenu(self.recentFilesMenu) # recent multi projects self.menuRecentMultiProjectsAct = \ self.__menu.addMenu(self.recentMultiProjectsMenu) # recent projects self.menuRecentProjectsAct = self.__menu.addMenu(self.recentProjectsMenu) self.__menu.addSeparator() self.__menu.addAction(UI.PixmapCache.getIcon("exit.png"), self.trUtf8('Quit'), qApp.quit) def __loadRecentProjects(self): """ Private method to load the recently opened project filenames. """ rp = self.rsettings.value(Globals.recentNameProject) if rp.isValid(): for f in rp.toStringList(): if QFileInfo(f).exists(): self.recentProjects.append(f) def __loadRecentMultiProjects(self): """ Private method to load the recently opened multi project filenames. """ rmp = self.rsettings.value(Globals.recentNameMultiProject) if rmp.isValid(): for f in rmp.toStringList(): if QFileInfo(f).exists(): self.recentMultiProjects.append(f) def __loadRecentFiles(self): """ Private method to load the recently opened filenames. """ rf = self.rsettings.value(Globals.recentNameFiles) if rf.isValid(): for f in rf.toStringList(): if QFileInfo(f).exists(): self.recentFiles.append(f) def __activated(self, reason): """ Private slot to handle the activated signal. @param reason reason code of the signal (QSystemTrayIcon.ActivationReason) """ if reason == QSystemTrayIcon.Context or \ reason == QSystemTrayIcon.MiddleClick: self.__showContextMenu() elif reason == QSystemTrayIcon.DoubleClick: self.__startEric() def __showContextMenu(self): """ Private slot to show the context menu. """ self.menuRecentProjectsAct.setEnabled(len(self.recentProjects) > 0) self.menuRecentMultiProjectsAct.setEnabled(len(self.recentMultiProjects) > 0) self.menuRecentFilesAct.setEnabled(len(self.recentFiles) > 0) pos = QCursor.pos() x = pos.x() - self.__menu.sizeHint().width() pos.setX(x > 0 and x or 0) y = pos.y() - self.__menu.sizeHint().height() pos.setY(y > 0 and y or 0) self.__menu.popup(pos) def __startProc(self, applName, *applArgs): """ Private method to start an eric4 application. @param applName name of the eric4 application script (string) @param *applArgs variable list of application arguments """ proc = QProcess() applPath = os.path.join(getConfig("ericDir"), applName) args = QStringList() args.append(applPath) for arg in applArgs: args.append(unicode(arg)) if not os.path.isfile(applPath) or not proc.startDetached(sys.executable, args): QMessageBox.critical(self, self.trUtf8('Process Generation Error'), self.trUtf8( '<p>Could not start the process.<br>' 'Ensure that it is available as <b>%1</b>.</p>' ).arg(applPath), self.trUtf8('OK')) def __startMiniEditor(self): """ Private slot to start the eric4 Mini Editor. """ self.__startProc("eric4_editor.py", "--config=%s" % Utilities.getConfigDir()) def __startEric(self): """ Private slot to start the eric4 IDE. """ self.__startProc("eric4.py", "--config=%s" % Utilities.getConfigDir()) def __startPreferences(self): """ Private slot to start the eric4 configuration dialog. """ self.__startProc("eric4_configure.py", "--config=%s" % Utilities.getConfigDir()) def __startPluginInstall(self): """ Private slot to start the eric4 plugin installation dialog. """ self.__startProc("eric4_plugininstall.py", "--config=%s" % Utilities.getConfigDir()) def __startPluginUninstall(self): """ Private slot to start the eric4 plugin uninstallation dialog. """ self.__startProc("eric4_pluginuninstall.py", "--config=%s" % Utilities.getConfigDir()) def __startPluginRepository(self): """ Private slot to start the eric4 plugin repository dialog. """ self.__startProc("eric4_pluginrepository.py", "--config=%s" % Utilities.getConfigDir()) def __startHelpViewer(self): """ Private slot to start the eric4 web browser. """ self.__startProc("eric4_webbrowser.py", "--config=%s" % Utilities.getConfigDir()) def __startUIPreviewer(self): """ Private slot to start the eric4 UI previewer. """ self.__startProc("eric4_uipreviewer.py", "--config=%s" % Utilities.getConfigDir()) def __startTRPreviewer(self): """ Private slot to start the eric4 translations previewer. """ self.__startProc("eric4_trpreviewer.py", "--config=%s" % Utilities.getConfigDir()) def __startUnittest(self): """ Private slot to start the eric4 unittest dialog. """ self.__startProc("eric4_unittest.py", "--config=%s" % Utilities.getConfigDir()) def __startDiff(self): """ Private slot to start the eric4 diff dialog. """ self.__startProc("eric4_diff.py", "--config=%s" % Utilities.getConfigDir()) def __startCompare(self): """ Private slot to start the eric4 compare dialog. """ self.__startProc("eric4_compare.py", "--config=%s" % Utilities.getConfigDir()) def __startSqlBrowser(self): """ Private slot to start the eric4 sql browser dialog. """ self.__startProc("eric4_sqlbrowser.py", "--config=%s" % Utilities.getConfigDir()) def __startIconEditor(self): """ Private slot to start the eric4 icon editor dialog. """ self.__startProc("eric4_iconeditor.py", "--config=%s" % Utilities.getConfigDir()) def __startQRegExp(self): """ Private slot to start the eric4 QRegExp editor dialog. """ self.__startProc("eric4_qregexp.py", "--config=%s" % Utilities.getConfigDir()) def __startPyRe(self): """ Private slot to start the eric4 Python re editor dialog. """ self.__startProc("eric4_re.py", "--config=%s" % Utilities.getConfigDir()) def __showRecentProjectsMenu(self): """ Private method to set up the recent projects menu. """ self.recentProjects.clear() self.rsettings.sync() self.__loadRecentProjects() self.recentProjectsMenu.clear() idx = 1 for rp in self.recentProjects: if idx < 10: formatStr = '&%d. %s' else: formatStr = '%d. %s' act = self.recentProjectsMenu.addAction(\ formatStr % (idx, Utilities.compactPath(unicode(rp), self.maxMenuFilePathLen))) act.setData(QVariant(rp)) idx += 1 def __showRecentMultiProjectsMenu(self): """ Private method to set up the recent multi projects menu. """ self.recentMultiProjects.clear() self.rsettings.sync() self.__loadRecentMultiProjects() self.recentMultiProjectsMenu.clear() idx = 1 for rmp in self.recentMultiProjects: if idx < 10: formatStr = '&%d. %s' else: formatStr = '%d. %s' act = self.recentMultiProjectsMenu.addAction(\ formatStr % (idx, Utilities.compactPath(unicode(rmp), self.maxMenuFilePathLen))) act.setData(QVariant(rmp)) idx += 1 def __showRecentFilesMenu(self): """ Private method to set up the recent files menu. """ self.recentFiles.clear() self.rsettings.sync() self.__loadRecentFiles() self.recentFilesMenu.clear() idx = 1 for rf in self.recentFiles: if idx < 10: formatStr = '&%d. %s' else: formatStr = '%d. %s' act = self.recentFilesMenu.addAction(\ formatStr % (idx, Utilities.compactPath(unicode(rf), self.maxMenuFilePathLen))) act.setData(QVariant(rf)) idx += 1 def __openRecent(self, act): """ Private method to open a project or file from the list of rencently opened projects or files. @param act reference to the action that triggered (QAction) """ filename = unicode(act.data().toString()) if filename: self.__startProc("eric4.py", filename) def __showPreferences(self): """ Private slot to set the preferences. """ from Preferences.ConfigurationDialog import ConfigurationDialog dlg = ConfigurationDialog(None, 'Configuration', True, fromEric = True, displayMode = ConfigurationDialog.TrayStarterMode) self.connect(dlg, SIGNAL('preferencesChanged'), self.preferencesChanged) dlg.show() dlg.showConfigurationPageByName("trayStarterPage") dlg.exec_() QApplication.processEvents() if dlg.result() == QDialog.Accepted: dlg.setPreferences() Preferences.syncPreferences() self.preferencesChanged() def preferencesChanged(self): """ Public slot to handle a change of preferences. """ self.setIcon( UI.PixmapCache.getIcon( unicode(Preferences.getTrayStarter("TrayStarterIcon")))) def __about(self): """ Private slot to handle the About dialog. """ from Plugins.AboutPlugin.AboutDialog import AboutDialog dlg = AboutDialog() dlg.exec_()
class View(QMainWindow, auxilia.Actions, MainWindow): partyMode = None hide_window = True def __init__(self, modelManager, configuration, mpdclient, library, app): QMainWindow.__init__(self) self.modelManager = modelManager self.app = app self.app.commitData = self.commitData self.focus = time() self.shuttingDown = False self.config = configuration self.mpdclient = mpdclient self.library = library self.appIcon = os.path.abspath(DATA_DIR+'icons/Pythagora.png') self.setupUi(self) self.setWindowTitle('Pythagora') self.setWindowIcon(QIcon(self.appIcon)) # Create standard views. self.playerForm = PlayerForm(self.modelManager, self.config) self.topLayout.addWidget(self.playerForm) self.connect(self.modelManager.playerState, SIGNAL('currentSongChanged'), self.playerForm.updateSong) self.connect(self.modelManager.playerState, SIGNAL('volumeChanged'), self.playerForm.setVolume) self.connect(self.modelManager.playerState, SIGNAL('progressChanged'), self.playerForm.setProgress) self.connect(self.modelManager.playerState, SIGNAL('playStateChanged'), self.playerForm.setPlayState) self.connect(self.modelManager.playerState, SIGNAL('bitrateChanged'), self.playerForm.songLabel.setBitrate) self.dbusNotifications = DBusNotifications(self.config) self.connect(self.modelManager.playerState, SIGNAL('currentSongChanged'), self.dbusNotifications.showNotification) self.toolBarLayout = self.playerForm.toolBarLayout self.currentList = CurrentPlaylistForm.CurrentPlaylistForm( self.modelManager, self, self.app, self.config) self.currentListLayout.addWidget(self.currentList) self.connect(self.playerForm.play, SIGNAL('clicked(bool)'), self.modelManager.playerState.playPause) self.connect(self.playerForm.back, SIGNAL('clicked(bool)'), self.modelManager.playerState.previousSong) self.connect(self.playerForm.forward, SIGNAL('clicked(bool)'), self.modelManager.playerState.nextSong) self.connect(self.playerForm.stop, SIGNAL('clicked(bool)'), self.modelManager.playerState.stop) self.connect(self.playerForm.volume, SIGNAL('valueChanged(int)'), self.modelManager.playerState.setVolume) # Standard toolbar buttons. self.exitAction = self.actionExit(self, self.app.quit) self.exitButton = QToolButton() self.exitButton.setAutoRaise(True) self.exitButton.setIconSize(QSize(22, 22)) self.exitButton.setDefaultAction(self.exitAction) self.settingsAction = self.actionSettings(self, self.showConfig) self.settingsButton = QToolButton() self.settingsButton.setAutoRaise(True) self.settingsButton.setIconSize(QSize(22, 22)) self.settingsButton.setDefaultAction(self.settingsAction) # Create 'Connect to' menu. self.menuConnect = QMenu('Connect To') self.menuConnect.menuAction().setIcon(auxilia.PIcon('network-disconnect')) self.connectButton = QToolButton() self.connectButton.setAutoRaise(True) self.connectButton.setIconSize(QSize(22, 22)) self.connectButton.setPopupMode(QToolButton.InstantPopup) self.connectButton.setIcon(auxilia.PIcon('network-disconnect')) self.connectButton.setText('Connect To') self.connectButton.setMenu(self.menuConnect) # Create 'MDP' menu. self.menuMPD = QMenu('MPD') self.menuMPD.menuAction().setIcon(auxilia.PIcon('network-workgroup')) self.connect(self.menuConnect, SIGNAL('aboutToShow()'), self._buildConnectTo) self.mpdButton = QToolButton() self.mpdButton.setAutoRaise(True) self.mpdButton.setIconSize(QSize(22, 22)) self.mpdButton.setPopupMode(QToolButton.InstantPopup) self.mpdButton.setIcon(auxilia.PIcon('network-workgroup')) self.mpdButton.setText('MPD') self.mpdButton.setMenu(self.menuMPD) self.reloadLibrary = self.actionLibReload(self.menuMPD, lambda: self.modelManager.reloadLibrary(True)) self.updateLibrary = self.actionLibUpdate(self.menuMPD, lambda: self.mpdclient.send('update')) self.rescanLibrary = self.actionLibRescan(self.menuMPD, lambda: self.mpdclient.send('rescan')) # Create 'Outputs' menu. self.menuOutputs = QMenu('Outputs') self.menuOutputs.menuAction().setIcon(auxilia.PIcon('audio-card')) self.connect(self.menuOutputs, SIGNAL('aboutToShow()'), self._buildOutputs) self.outputsButton = QToolButton() self.outputsButton.setAutoRaise(True) self.outputsButton.setIconSize(QSize(22, 22)) self.outputsButton.setPopupMode(QToolButton.InstantPopup) self.outputsButton.setIcon(auxilia.PIcon('audio-card')) self.outputsButton.setText('Outputs') self.outputsButton.setMenu(self.menuOutputs) # Fill Toolbar. self.toolBarLayout.addWidget(self.exitButton) self.toolBarLayout.addWidget(self.settingsButton) self.toolBarLayout.addWidget(self.connectButton) self.toolBarLayout.addWidget(self.outputsButton) self.toolBarLayout.addWidget(self.mpdButton) self.toolBarLayout.addStretch(1) # Fill Statusbar. self.serverLabel = QLabel('Not connected') self.numSongsLabel = QLabel('Songs') self.playTimeLabel = QLabel('playTime') self.statusbar.addWidget(self.serverLabel) self.statusbar.addPermanentWidget(self.numSongsLabel) self.statusbar.addPermanentWidget(self.playTimeLabel) # Set up trayicon and menu. if KDE: self.trayIcon = KTrayIcon(self.appIcon, self) else: self.trayIcon = QTrayIcon(self.appIcon, self) outputsMenuAction = self.menuOutputs.menuAction() connectMenuAction = self.menuConnect.menuAction() self.trayIcon.addMenuItem(outputsMenuAction) self.trayIcon.addMenuItem(connectMenuAction) self.trayIcon.addMenuItem(self.settingsAction) self.connect(self.trayIcon, SIGNAL('activate()'), self.toggleHideRestore) self.connect(self.trayIcon, SIGNAL('secondaryActivateRequested(QPoint)'), self._playPause) self.connect(self.trayIcon, SIGNAL('scrollRequested(int, Qt::Orientation)'), lambda amount: self.modelManager.playerState.volumeUp((amount+30)/60)) self.connect(self.modelManager.playerState, SIGNAL('playStateChanged'), self.trayIcon.setState) self.connect(self.playerForm, SIGNAL('tooltipChanged'), self.trayIcon.setToolTip) self.connect(self.tabs, SIGNAL('currentChanged(int)'), self._tabsIndexChanged) self.connect(self.tabs.tabBar(), SIGNAL('tabMoved(int,int)'), self._tabMoved) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self._storeSplitter) # Apply configuration. self.resize(configuration.mgrSize) self.splitter.setSizes(configuration.mgrSplit) self.tabs.setCurrentIndex(configuration.tabsIndex) self.closeEvent = self.closeEvent self.connect(self.app,SIGNAL('aboutToQuit()'),self.shutdown) #============================================================================== # Code for switching tabs on drag & drop. (__init__() continues) #============================================================================== # Instantiate timer self.tabTimer = QTimer() self.connect(self.tabTimer, SIGNAL('timeout()'), self._selectTab) # Overload the default dragEvents. self.tabs.dragLeaveEvent = self.dragLeaveEvent self.tabs.dragEnterEvent = self.dragEnterEvent self.tabs.dragMoveEvent = self.dragMoveEvent def dragEnterEvent(self, event): '''Starts timer on enter and sets first position.''' self.tabPos = event.pos() event.accept() self.tabTimer.start(500) def dragLeaveEvent(self, event): '''If the mouse leaves the tabWidget stop the timer.''' self.tabTimer.stop() def dragMoveEvent(self, event): '''Keep track of the mouse and change the position, restarts the timer when moved.''' tabPos = event.pos() moved = tabPos.manhattanLength() - self.tabPos.manhattanLength() if moved > 7 or moved < -7: self.tabTimer.start(500) self.tabPos = tabPos def _selectTab(self): '''Changes the view to the tab where the mouse was hovering above.''' index = self.tabs.tabBar().tabAt(self.tabPos) self.tabs.setCurrentIndex(index) self.tabTimer.stop() #============================================================================== def keyPressEvent(self, event): if event.key() == Qt.Key_F11: self._togglePartymode() else: super(View, self).keyPressEvent(event) def createPluginViews(self): '''Set all plugin tabs up.''' loadedPlugins = {} for plugin in plugins.allPlugins: plugin = plugin.getWidget(self.modelManager, self.mpdclient, self.config, self.library) if plugin: loadedPlugins[plugin.moduleName] = plugin for name in self.config.tabOrder: if name in loadedPlugins: plugin = loadedPlugins.pop(name) self.tabs.addTab(plugin, auxilia.PIcon(plugin.moduleIcon), plugin.moduleName) for plugin in loadedPlugins.values(): self.tabs.addTab(plugin, auxilia.PIcon(plugin.moduleIcon), plugin.moduleName) order = self.config.tabOrder order.append(plugin.moduleName) self.config.tabOrder = order def shutdown(self): self.shuttingDown = True self.app.processEvents() self.mpdclient.disconnect() self.config.mgrSize = self.size() print 'debug: shutdown finished' def commitData(self, sessionManager): self.hide_window = False QApplication.commitData(self.app, sessionManager) def showConfig(self): self.config.showConfiguration(self) def closeEvent(self, event): '''Catch MainWindow's close event so we can hide it instead.''' if self.hide_window: self.hide() event.ignore() else: QMainWindow.closeEvent(self, event) def _storeSplitter(self): self.config.mgrSplit = self.splitter.sizes() def _tabsIndexChanged(self, value): self.config.tabsIndex = self.tabs.currentIndex() def _tabMoved(self, old, new): print "DEBUG: Tab from", old, "moved to", new order = self.config.tabOrder order.insert(new, order.pop(old)) self.config.tabOrder = order def toggleHideRestore(self): '''Show or hide the window based on some parameters. We can detect when we are obscured and come to the top. In other cases we hide if mapped and show if not. ''' if KDE: if KWindowSystem.activeWindow() == self.winId() and self.isVisible(): self.hide() else: self.show() KWindowSystem.forceActiveWindow(self.winId()) else: if self.isVisible(): self.hide() else: self.show() def _playPause(self): self.playerForm.play.emit(SIGNAL('clicked(bool)'), True) def _buildConnectTo(self): self.menuConnect.clear() self.menuConnect.addAction(auxilia.PIcon('dialog-cancel'), 'None (disconnect)') connected = self.mpdclient.connected() for server in self.config.knownHosts: if connected and self.config.server and self.config.server[0] == server: icon = auxilia.PIcon('network-connect') else: icon = auxilia.PIcon('network-disconnect') self.menuConnect.addAction(icon, server) def _buildOutputs(self): self.menuOutputs.clear() if self.mpdclient.connected(): print 'debug: Building output menu.' for output in self.mpdclient.outputs(): action = QAction(output.get('outputname', 'No name'), self.menuOutputs) action.setCheckable(True) action.setChecked(output.get('outputenabled', '0') == '1') action.outputid = output.get('outputid') self.menuOutputs.addAction(action) def _togglePartymode(self): if self.partyMode is None: self.partyMode = self.isMaximized() self.showFullScreen() else: if self.partyMode == True: self.showNormal() self.showMaximized() else: self.showNormal() self.partyMode = None
class QuickMapServices: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__).decode(sys.getfilesystemencoding()) # initialize locale self.translator = QTranslator() self.locale = Locale.get_locale() locale_path = os.path.join( self.plugin_dir, 'i18n', 'QuickMapServices_{}.qm'.format(self.locale)) if os.path.exists(locale_path): self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.custom_translator = CustomTranslator() QCoreApplication.installTranslator(self.custom_translator) # Create the dialog (after translation) and keep reference self.info_dlg = AboutDialog() # Check Contrib and User dirs try: ExtraSources.check_extra_dirs() except: error_message = self.tr('Extra dirs for %s can\'t be created: %s %s') % (PluginSettings.product_name(), sys.exc_type, sys.exc_value) self.iface.messageBar().pushMessage(self.tr('Error'), error_message, level=QgsMessageBar.CRITICAL) # Declare instance attributes self.service_actions = [] self.service_layers = [] # TODO: id and smart remove self._scales_list = None # noinspection PyMethodMayBeStatic def tr(self, message): # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('QuickMapServices', message) def initGui(self): #import pydevd #pydevd.settrace('localhost', port=9921, stdoutToServer=True, stderrToServer=True, suspend=False) # Register plugin layer type self.tileLayerType = TileLayerType(self) QgsPluginLayerRegistry.instance().addPluginLayerType(self.tileLayerType) # Create menu icon_path = self.plugin_dir + '/icons/mActionAddLayer.png' self.menu = QMenu(self.tr(u'QuickMapServices')) self.menu.setIcon(QIcon(icon_path)) self.init_server_panel() self.build_menu_tree() # add to QGIS menu/toolbars self.append_menu_buttons() def _load_scales_list(self): scales_filename = os.path.join(self.plugin_dir, 'scales.xml') scales_list = [] # TODO: remake when fix: http://hub.qgis.org/issues/11915 # QgsScaleUtils.loadScaleList(scales_filename, scales_list, importer_message) xml_root = ET.parse(scales_filename).getroot() for scale_el in xml_root.findall('scale'): scales_list.append(scale_el.get('value')) return scales_list @property def scales_list(self): if not self._scales_list: self._scales_list = self._load_scales_list() return self._scales_list def set_nearest_scale(self): #get current scale curr_scale = self.iface.mapCanvas().scale() #find nearest nearest_scale = sys.maxint for scale_str in self.scales_list: scale = scale_str.split(':')[1] scale_int = int(scale) if abs(scale_int-curr_scale) < abs(nearest_scale - curr_scale): nearest_scale = scale_int #set new scale if nearest_scale != sys.maxint: self.iface.mapCanvas().zoomScale(nearest_scale) def set_tms_scales(self): res = QMessageBox.question( self.iface.mainWindow(), self.tr('QuickMapServices'), self.tr('Set SlippyMap scales for current project? \nThe previous settings will be overwritten!'), QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.Yes: # set scales QgsProject.instance().writeEntry('Scales', '/ScalesList', self.scales_list) # activate QgsProject.instance().writeEntry('Scales', '/useProjectScales', True) # update in main window # ???? no way to update: http://hub.qgis.org/issues/11917 def insert_layer(self): action = self.menu.sender() ds = action.data() add_layer_to_map(ds) def unload(self): # remove menu/panels self.remove_menu_buttons() self.remove_server_panel() # clean vars self.menu = None self.toolbutton = None self.service_actions = None self.ds_list = None self.groups_list = None self.service_layers = None # Unregister plugin layer type QgsPluginLayerRegistry.instance().removePluginLayerType(TileLayer.LAYER_TYPE) def build_menu_tree(self): # Main Menu self.menu.clear() self.groups_list = GroupsList() self.ds_list = DataSourcesList() data_sources = self.ds_list.data_sources.values() data_sources.sort(key=lambda x: x.alias or x.id) ds_hide_list = PluginSettings.get_hide_ds_id_list() for ds in data_sources: if ds.id in ds_hide_list: continue ds.action.triggered.connect(self.insert_layer) gr_menu = self.groups_list.get_group_menu(ds.group) gr_menu.addAction(ds.action) if gr_menu not in self.menu.children(): self.menu.addMenu(gr_menu) # Scales, Settings and About actions self.menu.addSeparator() icon_set_nearest_scale_path = self.plugin_dir + '/icons/mActionSettings.png' # TODO change icon set_nearest_scale_act = QAction(QIcon(icon_set_nearest_scale_path), self.tr('Set proper scale'), self.iface.mainWindow()) set_nearest_scale_act.triggered.connect(self.set_nearest_scale) self.menu.addAction(set_nearest_scale_act) # TODO: uncomment after fix self.service_actions.append(set_nearest_scale_act) icon_scales_path = self.plugin_dir + '/icons/mActionSettings.png' # TODO change icon scales_act = QAction(QIcon(icon_scales_path), self.tr('Set SlippyMap scales'), self.iface.mainWindow()) scales_act.triggered.connect(self.set_tms_scales) #self.menu.addAction(scales_act) # TODO: uncomment after fix self.service_actions.append(scales_act) icon_settings_path = self.plugin_dir + '/icons/mapservices.png' server_panel_act = self.server_toolbox.toggleViewAction() server_panel_act.setIcon(QIcon(icon_settings_path)) server_panel_act.setText(self.tr('Search QMS')) self.service_actions.append(server_panel_act) self.menu.addAction(server_panel_act) icon_settings_path = self.plugin_dir + '/icons/mActionSettings.png' settings_act = QAction(QIcon(icon_settings_path), self.tr('Settings'), self.iface.mainWindow()) self.service_actions.append(settings_act) settings_act.triggered.connect(self.show_settings_dialog) self.menu.addAction(settings_act) icon_about_path = self.plugin_dir + '/icons/mActionAbout.png' info_act = QAction(QIcon(icon_about_path), self.tr('About'), self.iface.mainWindow()) self.service_actions.append(info_act) info_act.triggered.connect(self.info_dlg.show) self.menu.addAction(info_act) def remove_menu_buttons(self): """ Remove menus/buttons from all toolbars and main submenu :return: None """ # remove menu if self.menu: self.iface.webMenu().removeAction(self.menu.menuAction()) self.iface.addLayerMenu().removeAction(self.menu.menuAction()) # remove toolbar button if self.tb_action: self.iface.webToolBar().removeAction(self.tb_action) self.iface.layerToolBar().removeAction(self.tb_action) def append_menu_buttons(self): """ Append menus and buttons to appropriate toolbar :return: """ # add to QGIS menu if PluginSettings.move_to_layers_menu(): self.iface.addLayerMenu().addMenu(self.menu) else: # need workaround for WebMenu _temp_act = QAction('temp', self.iface.mainWindow()) self.iface.addPluginToWebMenu("_tmp", _temp_act) self.iface.webMenu().addMenu(self.menu) self.iface.removePluginWebMenu("_tmp", _temp_act) # add to QGIS toolbar toolbutton = QToolButton() toolbutton.setPopupMode(QToolButton.InstantPopup) toolbutton.setMenu(self.menu) toolbutton.setIcon(self.menu.icon()) toolbutton.setText(self.menu.title()) toolbutton.setToolTip(self.menu.title()) if PluginSettings.move_to_layers_menu(): self.tb_action = self.iface.layerToolBar().addWidget(toolbutton) else: self.tb_action = self.iface.webToolBar().addWidget(toolbutton) def show_settings_dialog(self): settings_dlg = SettingsDialog() settings_dlg.exec_() # apply settings # self.remove_menu_buttons() self.build_menu_tree() # self.append_menu_buttons() def init_server_panel(self): self.server_toolbox = QmsServiceToolbox(self.iface) self.iface.addDockWidget(PluginSettings.server_dock_area(), self.server_toolbox) self.server_toolbox.setWindowIcon(QIcon(self.plugin_dir + '/icons/mapservices.png')) self.server_toolbox.setVisible(PluginSettings.server_dock_visibility()) # self.server_toolbox.setFloating(PluginSettings.dock_floating()) # self.server_toolbox.resize(PluginSettings.dock_size()) # self.server_toolbox.move(PluginSettings.dock_pos()) # self.server_toolbox.setWindowIcon(QIcon(path.join(_current_path, 'edit-find-project.png'))) def remove_server_panel(self): mw = self.iface.mainWindow() PluginSettings.set_server_dock_area(mw.dockWidgetArea(self.server_toolbox)) PluginSettings.set_server_dock_visibility(self.server_toolbox.isVisible()) # PluginSettings.set_dock_floating(self.__quick_tlb.isFloating()) # PluginSettings.set_dock_pos(self.__quick_tlb.pos()) # PluginSettings.set_dock_size(self.__quick_tlb.size()) # PluginSettings.set_dock_geocoder_name(self.__quick_tlb.get_active_geocoder_name()) self.iface.removeDockWidget(self.server_toolbox) del self.server_toolbox
class GraphFragmentView(NulogBaseFragmentView): colours = ['#487118', '#7AB41D', '#F8E930', '#F29324', '#EA752C', '#C1292E', '#8C0A14', '#5F3269', '#874B94', '#212483', '#528FC8', '#196D38', # rest of the list is the original one (pre Harmony) '#47BE4F', '#CC0099', '#E0DD8D', '#FF8A2B', '#4B5DFF', '#6DF8BE', '#9C56FF', '#BE7344', '#CCBE78', '#E0ACD0', '#FF37E1', '#45709E', '#676FFF', '#4CAC84', '#35FF1A', '#806170', '#C3BF46', '#E0829A', '#E6CBB7'] show_count = False def __init__(self, fetcher, parent=None): NulogBaseFragmentView.__init__(self, fetcher) self.info_area = InfoArea(self.parent()) self.colsort_actions = [] self.colsort_menu = QMenu(self) self.hide_count = False self.ready = False def getActions(self): action = QAction(tr('Sort by'), self) action.setMenu(self.colsort_menu) return [action] def setColsortActions(self): self.colsort_menu.clear() self.colsort_actions = [] def make_lambda(col): return lambda: self.sortBy(col) for col in self.columns: action = QAction(arg_types[col].label, self) # TODO: might be radiobuttons instead of checkbox. action.setCheckable(True) self.connect(action, SIGNAL('triggered()'), make_lambda(col)) self.colsort_menu.addAction(action) self.colsort_actions += [action] def get_model_label(self, row, col, arg_data): if col == 1: return arg_data.value else: return arg_data.label def updateData(self, result): if self.is_closed: return if not NulogBaseFragmentView.updateData(self, result): return False if len(self.columns) != len(self.colsort_actions): self.setColsortActions() if self.result['args'].has_key('sortby'): for i, col in enumerate(self.columns): self.colsort_actions[i].setChecked(self.result['args']['sortby'] == col) self.ready = False if self.result['args'].has_key('start') and self.result['args']['start'] == 0 and \ self.chart_type == PIECHART: self.fetcher.count(self.updateData_bis) return True else: self.ready = True return True def updateData_bis(self, count): if not count: count =0 count = int(count) if self.is_closed: return if not self.ready: # Number of entries shown (note lines, but values in pie) shown_count = 0 row = 0 for row, line in enumerate(self.data): # if len(line[1]) > 0: shown_count += int(line[1]) # Add a part for "the" other entries # if shown_count < count: self.my_model.insertRows(row+1, 1, QModelIndex()) self.add_line(row+1, ['Other', count - shown_count]) self.ready = True if self.data: self.emit(SIGNAL("updateData_bis")) return True return False
class DirView(QTreeView): """Base file/directory tree view""" def __init__(self, parent=None): super(DirView, self).__init__(parent) self.name_filters = None self.parent_widget = parent self.valid_types = None self.show_all = None self.menu = None self.common_actions = None self.__expanded_state = None self._to_be_loaded = None self.fsmodel = None self.setup_fs_model() self._scrollbar_positions = None # ---- Model def setup_fs_model(self): """Setup filesystem model""" filters = QDir.AllDirs | QDir.Files | QDir.Drives | QDir.NoDotAndDotDot self.fsmodel = QFileSystemModel(self) self.fsmodel.setFilter(filters) self.fsmodel.setNameFilterDisables(False) def install_model(self): """Install filesystem model""" self.setModel(self.fsmodel) def setup_view(self): """Setup view""" self.install_model() self.connect(self.fsmodel, SIGNAL("directoryLoaded(QString)"), lambda: self.resizeColumnToContents(0)) self.setAnimated(False) self.setSortingEnabled(True) self.sortByColumn(0, Qt.AscendingOrder) def set_name_filters(self, name_filters): """Set name filters""" self.name_filters = name_filters self.fsmodel.setNameFilters(name_filters) def set_show_all(self, state): """Toggle 'show all files' state""" if state: self.fsmodel.setNameFilters([]) else: self.fsmodel.setNameFilters(self.name_filters) def get_filename(self, index): """Return filename associated with *index*""" if index: return osp.normpath(unicode(self.fsmodel.filePath(index))) def get_index(self, filename): """Return index associated with filename""" return self.fsmodel.index(filename) def get_selected_filenames(self): """Return selected filenames""" if self.selectionMode() == self.ExtendedSelection: return [self.get_filename(idx) for idx in self.selectedIndexes()] else: return [self.get_filename(self.currentIndex())] def get_dirname(self, index): """Return dirname associated with *index*""" fname = self.get_filename(index) if fname: if osp.isdir(fname): return fname else: return osp.dirname(fname) # ---- Tree view widget def setup(self, name_filters=["*.py", "*.pyw"], valid_types=(".py", ".pyw"), show_all=False): """Setup tree widget""" self.setup_view() self.set_name_filters(name_filters) self.valid_types = valid_types self.show_all = show_all # Setup context menu self.menu = QMenu(self) self.common_actions = self.setup_common_actions() # ---- Context menu def setup_common_actions(self): """Setup context menu common actions""" # Filters filters_action = create_action( self, _("Edit filename filters..."), None, get_icon("filter.png"), triggered=self.edit_filter ) # Show all files all_action = create_action(self, _("Show all files"), toggled=self.toggle_all) all_action.setChecked(self.show_all) self.toggle_all(self.show_all) return [filters_action, all_action] def edit_filter(self): """Edit name filters""" filters, valid = QInputDialog.getText( self, _("Edit filename filters"), _("Name filters:"), QLineEdit.Normal, ", ".join(self.name_filters) ) if valid: filters = [f.strip() for f in unicode(filters).split(",")] self.parent_widget.sig_option_changed.emit("name_filters", filters) self.set_name_filters(filters) def toggle_all(self, checked): """Toggle all files mode""" self.parent_widget.sig_option_changed.emit("show_all", checked) self.show_all = checked self.set_show_all(checked) def create_file_new_actions(self, fnames): """Return actions for submenu 'New...'""" if not fnames: return [] new_file_act = create_action( self, _("File..."), icon="filenew.png", triggered=lambda: self.new_file(fnames[-1]) ) new_module_act = create_action( self, _("Module..."), icon="py.png", triggered=lambda: self.new_module(fnames[-1]) ) new_folder_act = create_action( self, _("Folder..."), icon="folder_new.png", triggered=lambda: self.new_folder(fnames[-1]) ) new_package_act = create_action( self, _("Package..."), icon=get_icon("package_collapsed.png"), triggered=lambda: self.new_package(fnames[-1]), ) return [new_file_act, new_folder_act, None, new_module_act, new_package_act] def create_file_import_actions(self, fnames): """Return actions for submenu 'Import...'""" return [] def create_file_manage_actions(self, fnames): """Return file management actions""" only_files = all([osp.isfile(_fn) for _fn in fnames]) only_modules = all([osp.splitext(_fn)[1] in (".py", ".pyw", ".ipy") for _fn in fnames]) only_valid = all([osp.splitext(_fn)[1] in self.valid_types for _fn in fnames]) run_action = create_action(self, _("Run"), icon="run_small.png", triggered=self.run) edit_action = create_action(self, _("Edit"), icon="edit.png", triggered=self.clicked) move_action = create_action(self, _("Move..."), icon="move.png", triggered=self.move) delete_action = create_action(self, _("Delete..."), icon="delete.png", triggered=self.delete) rename_action = create_action(self, _("Rename..."), icon="rename.png", triggered=self.rename) open_action = create_action(self, _("Open"), triggered=self.open) actions = [] if only_modules: actions.append(run_action) if only_valid and only_files: actions.append(edit_action) else: actions.append(open_action) actions += [delete_action, rename_action] basedir = fixpath(osp.dirname(fnames[0])) if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]): actions.append(move_action) actions += [None] # VCS support is quite limited for now, so we are enabling the VCS # related actions only when a single file/folder is selected: dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0]) if len(fnames) == 1 and vcs.is_vcs_repository(dirname): vcs_ci = create_action( self, _("Commit"), icon="vcs_commit.png", triggered=lambda fnames=[dirname]: self.vcs_command(fnames, tool="commit"), ) vcs_log = create_action( self, _("Browse repository"), icon="vcs_browse.png", triggered=lambda fnames=[dirname]: self.vcs_command(fnames, tool="browse"), ) actions += [None, vcs_ci, vcs_log] return actions def create_folder_manage_actions(self, fnames): """Return folder management actions""" actions = [] if os.name == "nt": _title = _("Open command prompt here") else: _title = _("Open terminal here") action = create_action( self, _title, icon="cmdprompt.png", triggered=lambda fnames=fnames: self.open_terminal(fnames) ) actions.append(action) _title = _("Open Python interpreter here") action = create_action( self, _title, icon="python.png", triggered=lambda fnames=fnames: self.open_interpreter(fnames) ) actions.append(action) return actions def create_context_menu_actions(self): """Create context menu actions""" actions = [] fnames = self.get_selected_filenames() new_actions = self.create_file_new_actions(fnames) if len(new_actions) > 1: # Creating a submenu only if there is more than one entry new_act_menu = QMenu(_("New"), self) add_actions(new_act_menu, new_actions) actions.append(new_act_menu) else: actions += new_actions import_actions = self.create_file_import_actions(fnames) if len(import_actions) > 1: # Creating a submenu only if there is more than one entry import_act_menu = QMenu(_("Import"), self) add_actions(import_act_menu, import_actions) actions.append(import_act_menu) else: actions += import_actions if actions: actions.append(None) if fnames: actions += self.create_file_manage_actions(fnames) if actions: actions.append(None) if fnames and all([osp.isdir(_fn) for _fn in fnames]): actions += self.create_folder_manage_actions(fnames) if actions: actions.append(None) actions += self.common_actions return actions def update_menu(self): """Update context menu""" self.menu.clear() add_actions(self.menu, self.create_context_menu_actions()) # ---- Events def viewportEvent(self, event): """Reimplement Qt method""" # Prevent Qt from crashing or showing warnings like: # "QSortFilterProxyModel: index from wrong model passed to # mapFromSource", probably due to the fact that the file system model # is being built. See Issue 1250. # # This workaround was inspired by the following KDE bug: # https://bugs.kde.org/show_bug.cgi?id=172198 # # Apparently, this is a bug from Qt itself. self.executeDelayedItemsLayout() return QTreeView.viewportEvent(self, event) def contextMenuEvent(self, event): """Override Qt method""" self.update_menu() self.menu.popup(event.globalPos()) def keyPressEvent(self, event): """Reimplement Qt method""" if event.key() in (Qt.Key_Enter, Qt.Key_Return): self.clicked() elif event.key() == Qt.Key_F2: self.rename() elif event.key() == Qt.Key_Delete: self.delete() else: QTreeView.keyPressEvent(self, event) def mouseDoubleClickEvent(self, event): """Reimplement Qt method""" QTreeView.mouseDoubleClickEvent(self, event) self.clicked() def clicked(self): """Selected item was double-clicked or enter/return was pressed""" fnames = self.get_selected_filenames() for fname in fnames: if osp.isdir(fname): self.directory_clicked(fname) else: self.open([fname]) def directory_clicked(self, dirname): """Directory was just clicked""" pass # ---- Drag def dragEnterEvent(self, event): """Drag and Drop - Enter event""" event.setAccepted(event.mimeData().hasFormat("text/plain")) def dragMoveEvent(self, event): """Drag and Drop - Move event""" if event.mimeData().hasFormat("text/plain"): event.setDropAction(Qt.MoveAction) event.accept() else: event.ignore() def startDrag(self, dropActions): """Reimplement Qt Method - handle drag event""" data = QMimeData() data.setUrls([QUrl(fname) for fname in self.get_selected_filenames()]) drag = QDrag(self) drag.setMimeData(data) drag.exec_() # ---- File/Directory actions def open(self, fnames=None): """Open files with the appropriate application""" if fnames is None: fnames = self.get_selected_filenames() for fname in fnames: ext = osp.splitext(fname)[1] if osp.isfile(fname) and ext in self.valid_types: self.parent_widget.sig_open_file.emit(fname) else: self.open_outside_spyder([fname]) def open_outside_spyder(self, fnames): """Open file outside Spyder with the appropriate application If this does not work, opening unknown file in Spyder, as text file""" for path in sorted(fnames): path = file_uri(path) ok = programs.start_file(path) if not ok: self.parent_widget.emit(SIGNAL("edit(QString)"), path) def open_terminal(self, fnames): """Open terminal""" for path in sorted(fnames): self.parent_widget.emit(SIGNAL("open_terminal(QString)"), path) def open_interpreter(self, fnames): """Open interpreter""" for path in sorted(fnames): self.parent_widget.emit(SIGNAL("open_interpreter(QString)"), path) def run(self, fnames=None): """Run Python scripts""" if fnames is None: fnames = self.get_selected_filenames() for fname in fnames: self.parent_widget.emit(SIGNAL("run(QString)"), fname) def remove_tree(self, dirname): """Remove whole directory tree Reimplemented in project explorer widget""" shutil.rmtree(dirname, onerror=misc.onerror) def delete_file(self, fname, multiple, yes_to_all): """Delete file""" if multiple: buttons = QMessageBox.Yes | QMessageBox.YesAll | QMessageBox.No | QMessageBox.Cancel else: buttons = QMessageBox.Yes | QMessageBox.No if yes_to_all is None: answer = QMessageBox.warning( self, _("Delete"), _("Do you really want " "to delete <b>%s</b>?") % osp.basename(fname), buttons ) if answer == QMessageBox.No: return yes_to_all elif answer == QMessageBox.Cancel: return False elif answer == QMessageBox.YesAll: yes_to_all = True try: if osp.isfile(fname): misc.remove_file(fname) self.parent_widget.emit(SIGNAL("removed(QString)"), fname) else: self.remove_tree(fname) self.parent_widget.emit(SIGNAL("removed_tree(QString)"), fname) return yes_to_all except EnvironmentError, error: action_str = _("delete") QMessageBox.critical( self, _("Project Explorer"), _("<b>Unable to %s <i>%s</i></b>" "<br><br>Error message:<br>%s") % (action_str, fname, unicode(error)), ) return False
class MincutConfig(ParentAction): def __init__(self, mincut): """ Class constructor """ self.mincut = mincut self.controller = self.mincut.controller self.schema_name = self.controller.schema_name def config(self): """ B5-99: Config """ # Dialog multi_selector self.dlg_multi = Multi_selector() utils_giswater.setDialog(self.dlg_multi) self.tbl_config = self.dlg_multi.findChild(QTableView, "tbl") self.btn_insert = self.dlg_multi.findChild(QPushButton, "btn_insert") self.btn_delete = self.dlg_multi.findChild(QPushButton, "btn_delete") table = "anl_mincut_selector_valve" self.menu_valve = QMenu() self.dlg_multi.btn_insert.pressed.connect( partial(self.fill_insert_menu, table)) btn_cancel = self.dlg_multi.findChild(QPushButton, "btn_cancel") btn_cancel.pressed.connect(partial(self.close_dialog, self.dlg_multi)) self.menu_valve.clear() self.dlg_multi.btn_insert.setMenu(self.menu_valve) self.dlg_multi.btn_delete.pressed.connect( partial(self.delete_records_config, self.tbl_config, table)) self.fill_table_config(self.tbl_config, self.schema_name + "." + table) # Open form self.dlg_multi.setWindowFlags(Qt.WindowStaysOnTopHint) self.dlg_multi.open() def fill_insert_menu(self, table): """ Insert menu on QPushButton->QMenu """ self.menu_valve.clear() node_type = "VALVE" sql = ("SELECT id FROM " + self.schema_name + ".node_type" " WHERE type = '" + node_type + "' ORDER BY id") rows = self.controller.get_rows(sql) if not rows: return # Fill menu for row in rows: elem = row[0] # If not exist in table _selector_state insert to menu # Check if we already have data with selected id sql = "SELECT id FROM " + self.schema_name + "." + table + " WHERE id = '" + elem + "'" rows = self.controller.get_rows(sql) if not rows: self.menu_valve.addAction(elem, partial(self.insert, elem, table)) def insert(self, id_action, table): """ On action(select value from menu) execute SQL """ # Insert value into database sql = "INSERT INTO " + self.schema_name + "." + table + " (id) VALUES ('" + id_action + "')" self.controller.execute_sql(sql) self.fill_table_config(self.tbl_config, self.schema_name + "." + table) def fill_table_config(self, widget, table_name): """ Set a model with selected filter. Attach that model to selected table """ # Set model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.select() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view widget.setModel(model) def delete_records_config(self, widget, table_name): """ Delete selected elements of the table """ # Get selected rows selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return inf_text = "" list_id = "" for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = widget.model().record(row).value("id") inf_text += str(id_) + ", " list_id = list_id + "'" + str(id_) + "', " inf_text = inf_text[:-2] list_id = list_id[:-2] message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, inf_text) if answer: sql = ("DELETE FROM " + self.schema_name + "." + table_name + "" " WHERE id IN (" + list_id + ")") self.controller.execute_sql(sql) widget.model().select() def mg_mincut_management(self): """ Button 27: Mincut management """ self.action = "mg_mincut_management" # Create the dialog and signals self.dlg_min_edit = Mincut_edit() utils_giswater.setDialog(self.dlg_min_edit) self.load_settings(self.dlg_min_edit) self.tbl_mincut_edit = self.dlg_min_edit.findChild( QTableView, "tbl_mincut_edit") self.txt_mincut_id = self.dlg_min_edit.findChild( QLineEdit, "txt_mincut_id") self.tbl_mincut_edit.setSelectionBehavior(QAbstractItemView.SelectRows) # Adding auto-completion to a QLineEdit self.completer = QCompleter() self.txt_mincut_id.setCompleter(self.completer) model = QStringListModel() sql = "SELECT DISTINCT(id) FROM " + self.schema_name + ".v_ui_anl_mincut_result_cat " rows = self.controller.get_rows(sql) values = [] for row in rows: values.append(str(row[0])) model.setStringList(values) self.completer.setModel(model) self.txt_mincut_id.textChanged.connect( partial(self.filter_by_id, self.tbl_mincut_edit, self.txt_mincut_id, "v_ui_anl_mincut_result_cat")) self.dlg_min_edit.tbl_mincut_edit.doubleClicked.connect( self.open_mincut) self.dlg_min_edit.btn_cancel.pressed.connect( partial(self.close_dialog, self.dlg_min_edit)) self.dlg_min_edit.rejected.connect( partial(self.close_dialog, self.dlg_min_edit)) self.dlg_min_edit.btn_delete.clicked.connect( partial(self.delete_mincut_management, self.tbl_mincut_edit, "v_ui_anl_mincut_result_cat", "id")) # Fill ComboBox state sql = ("SELECT name" " FROM " + self.schema_name + ".anl_mincut_cat_state" " ORDER BY name") rows = self.controller.get_rows(sql) utils_giswater.fillComboBox("state_edit", rows) self.dlg_min_edit.state_edit.activated.connect( partial(self.filter_by_state, self.tbl_mincut_edit, self.dlg_min_edit.state_edit, "v_ui_anl_mincut_result_cat")) self.controller.log_info("test 1") # Set a model with selected filter. Attach that model to selected table self.fill_table_mincut_management( self.tbl_mincut_edit, self.schema_name + ".v_ui_anl_mincut_result_cat") self.set_table_columns(self.tbl_mincut_edit, "v_ui_anl_mincut_result_cat") self.controller.log_info( "test set table columns for mincut management ") #self.mincut.set_table_columns(self.tbl_mincut_edit, "v_ui_anl_mincut_result_cat") # Open the dialog self.dlg_min_edit.setWindowFlags(Qt.WindowStaysOnTopHint) self.dlg_min_edit.show() def open_mincut(self): """ Open mincut form with selected record of the table """ selected_list = self.tbl_mincut_edit.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return row = selected_list[0].row() # Get mincut_id from selected row result_mincut_id = self.tbl_mincut_edit.model().record(row).value("id") # Close this dialog and open selected mincut self.close_dialog(self.dlg_min_edit) self.mincut.init_mincut_form() self.mincut.load_mincut(result_mincut_id) def filter_by_id(self, table, widget_txt, tablename): id_ = utils_giswater.getWidgetText(widget_txt) if id_ != 'null': expr = " id = '" + id_ + "'" # Refresh model with selected filter table.model().setFilter(expr) table.model().select() else: self.fill_table_mincut_management( self.tbl_mincut_edit, self.schema_name + "." + tablename) def filter_by_state(self, table, widget, tablename): state = utils_giswater.getWidgetText(widget) if state != 'null': expr_filter = " state = '" + str(state) + "'" self.controller.log_info(str(expr_filter)) # Refresh model with selected expr_filter table.model().setFilter(expr_filter) table.model().select() else: self.fill_table_mincut_management( self.tbl_mincut_edit, self.schema_name + "." + tablename) def fill_table_mincut_management(self, widget, table_name): """ Set a model with selected filter. Attach that model to selected table """ # Set model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.sort(0, 1) model.select() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view widget.setModel(model) def delete_mincut_management(self, widget, table_name, column_id): """ Delete selected elements of the table (by id) """ # Get selected rows selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return inf_text = "" list_id = "" for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = widget.model().record(row).value(str(column_id)) inf_text += str(id_) + ", " list_id = list_id + "'" + str(id_) + "', " inf_text = inf_text[:-2] list_id = list_id[:-2] message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, inf_text) if answer: sql = ("DELETE FROM " + self.schema_name + "." + table_name + "" " WHERE " + column_id + " IN (" + list_id + ")") self.controller.execute_sql(sql) widget.model().select()
class WsdtGui(LayerViewerGui): ########################################### ### AppletGuiInterface Concrete Methods ### ########################################### def appletDrawer(self): return self._drawer def stopAndCleanUp(self): # Unsubscribe to all signals for fn in self.__cleanup_fns: fn() # Base class super( WsdtGui, self ).stopAndCleanUp() ########################################### ########################################### def __init__(self, parentApplet, topLevelOperatorView): self.__cleanup_fns = [] self._currently_updating = False self.topLevelOperatorView = topLevelOperatorView super(WsdtGui, self).__init__( parentApplet, topLevelOperatorView ) self._sp_colortable = generateRandomColors(256, clamp={'v': 1.0, 's' : 0.5}, zeroIsTransparent=True) self._threshold_colortable = [ QColor(0, 0, 0, 0).rgba(), # transparent QColor(0, 255, 0, 255).rgba() ] # green # Any time watershed is re-computed, re-update the layer set, in case the set of debug layers has changed. self.topLevelOperatorView.watershed_completed.subscribe( self.updateAllLayers ) def initAppletDrawerUi(self): """ Overridden from base class (LayerViewerGui) """ op = self.topLevelOperatorView def configure_update_handlers( qt_signal, op_slot ): qt_signal.connect( self.configure_operator_from_gui ) op_slot.notifyDirty( self.configure_gui_from_operator ) self.__cleanup_fns.append( partial( op_slot.unregisterDirty, self.configure_gui_from_operator ) ) def control_layout( label_text, widget ): row_layout = QHBoxLayout() row_layout.addWidget( QLabel(label_text) ) row_layout.addSpacerItem( QSpacerItem(10, 0, QSizePolicy.Expanding) ) row_layout.addWidget(widget) return row_layout drawer_layout = QVBoxLayout() channel_button = QPushButton() self.channel_menu = QMenu(self) # Must retain menus (in self) or else they get deleted. channel_button.setMenu(self.channel_menu) channel_button.clicked.connect(channel_button.showMenu) def populate_channel_menu(*args): if sip.isdeleted(channel_button): return self.channel_menu.clear() self.channel_actions = [] for ch in range(op.Input.meta.getTaggedShape()['c']): action = QAction("Channel {}".format(ch), self.channel_menu) action.setCheckable(True) self.channel_menu.addAction(action) self.channel_actions.append(action) configure_update_handlers( action.toggled, op.ChannelSelections ) populate_channel_menu() op.Input.notifyMetaChanged( populate_channel_menu ) drawer_layout.addLayout( control_layout( "Input Channel", channel_button ) ) self.channel_button = channel_button threshold_box = QDoubleSpinBox() threshold_box.setDecimals(2) threshold_box.setMinimum(0.00) threshold_box.setMaximum(1.0) threshold_box.setSingleStep(0.1) configure_update_handlers( threshold_box.valueChanged, op.Pmin ) drawer_layout.addLayout( control_layout( "Threshold", threshold_box ) ) self.threshold_box = threshold_box membrane_size_box = QSpinBox() membrane_size_box.setMinimum(0) membrane_size_box.setMaximum(1000000) configure_update_handlers( membrane_size_box.valueChanged, op.MinMembraneSize ) drawer_layout.addLayout( control_layout( "Min Membrane Size", membrane_size_box ) ) self.membrane_size_box = membrane_size_box seed_presmoothing_box = QDoubleSpinBox() seed_presmoothing_box.setDecimals(1) seed_presmoothing_box.setMinimum(0.0) seed_presmoothing_box.setMaximum(10.0) seed_presmoothing_box.setSingleStep(0.1) configure_update_handlers( seed_presmoothing_box.valueChanged, op.SigmaMinima ) drawer_layout.addLayout( control_layout( "Presmooth before seeds", seed_presmoothing_box ) ) self.seed_presmoothing_box = seed_presmoothing_box seed_method_combo = QComboBox() seed_method_combo.addItem("Connected") seed_method_combo.addItem("Clustered") configure_update_handlers( seed_method_combo.currentIndexChanged, op.GroupSeeds ) drawer_layout.addLayout( control_layout( "Seed Labeling", seed_method_combo ) ) self.seed_method_combo = seed_method_combo watershed_presmoothing_box = QDoubleSpinBox() watershed_presmoothing_box.setDecimals(1) watershed_presmoothing_box.setMinimum(0.0) watershed_presmoothing_box.setMaximum(10.0) watershed_presmoothing_box.setSingleStep(0.1) configure_update_handlers( watershed_presmoothing_box.valueChanged, op.SigmaWeights ) drawer_layout.addLayout( control_layout( "Presmooth before watershed", watershed_presmoothing_box ) ) self.watershed_presmoothing_box = watershed_presmoothing_box superpixel_size_box = QSpinBox() superpixel_size_box.setMinimum(0) superpixel_size_box.setMaximum(1000000) configure_update_handlers( superpixel_size_box.valueChanged, op.MinSegmentSize ) drawer_layout.addLayout( control_layout( "Min Superpixel Size", superpixel_size_box ) ) self.superpixel_size_box = superpixel_size_box preserve_pmaps_box = QCheckBox() configure_update_handlers( preserve_pmaps_box.toggled, op.PreserveMembranePmaps ) drawer_layout.addLayout( control_layout( "Preserve membrane probabilities", preserve_pmaps_box ) ) self.preserve_pmaps_box = preserve_pmaps_box enable_debug_box = QCheckBox() configure_update_handlers( enable_debug_box.toggled, op.EnableDebugOutputs ) drawer_layout.addLayout( control_layout( "Show Debug Layers", enable_debug_box ) ) self.enable_debug_box = enable_debug_box op.Superpixels.notifyReady(self.configure_gui_from_operator) op.Superpixels.notifyUnready(self.configure_gui_from_operator) self.__cleanup_fns.append( partial( op.Superpixels.unregisterReady, self.configure_gui_from_operator ) ) self.__cleanup_fns.append( partial( op.Superpixels.unregisterUnready, self.configure_gui_from_operator ) ) self.update_ws_button = QPushButton("Update Watershed", clicked=self.onUpdateWatershedsButton) drawer_layout.addWidget( self.update_ws_button ) drawer_layout.setSpacing(0) drawer_layout.addSpacerItem( QSpacerItem(0, 10, QSizePolicy.Minimum, QSizePolicy.Expanding) ) # Finally, the whole drawer widget drawer = QWidget(parent=self) drawer.setLayout(drawer_layout) # Save these members for later use self._drawer = drawer # Initialize everything with the operator's initial values self.configure_gui_from_operator() @contextmanager def set_updating(self): assert not self._currently_updating self._currently_updating = True yield self._currently_updating = False def configure_gui_from_operator(self, *args): if self._currently_updating: return False with self.set_updating(): op = self.topLevelOperatorView channel_selections = op.ChannelSelections.value for ch in range(op.Input.meta.shape[-1]): self.channel_actions[ch].setChecked(ch in channel_selections) if len(channel_selections) == 0: self.channel_button.setText("Please Select") else: self.channel_button.setText(",".join(map(str, channel_selections))) self.threshold_box.setValue( op.Pmin.value ) self.membrane_size_box.setValue( op.MinMembraneSize.value ) self.superpixel_size_box.setValue( op.MinSegmentSize.value ) self.seed_presmoothing_box.setValue( op.SigmaMinima.value ) self.watershed_presmoothing_box.setValue( op.SigmaWeights.value ) self.seed_method_combo.setCurrentIndex( int(op.GroupSeeds.value) ) self.preserve_pmaps_box.setChecked( op.PreserveMembranePmaps.value ) self.enable_debug_box.setChecked( op.EnableDebugOutputs.value ) self.update_ws_button.setEnabled( op.Superpixels.ready() ) def configure_operator_from_gui(self): if self._currently_updating: return False with self.set_updating(): op = self.topLevelOperatorView channel_selections = [] for ch in range(len(self.channel_actions)): if self.channel_actions[ch].isChecked(): channel_selections.append(ch) op.ChannelSelections.setValue( channel_selections ) op.Pmin.setValue( self.threshold_box.value() ) op.MinMembraneSize.setValue( self.membrane_size_box.value() ) op.MinSegmentSize.setValue( self.superpixel_size_box.value() ) op.SigmaMinima.setValue( self.seed_presmoothing_box.value() ) op.SigmaWeights.setValue( self.watershed_presmoothing_box.value() ) op.GroupSeeds.setValue( bool(self.seed_method_combo.currentIndex()) ) op.PreserveMembranePmaps.setValue( self.preserve_pmaps_box.isChecked() ) op.EnableDebugOutputs.setValue( self.enable_debug_box.isChecked() ) # The GUI may need to respond to some changes in the operator outputs. self.configure_gui_from_operator() def onUpdateWatershedsButton(self): def updateThread(): """ Temporarily unfreeze the cache and freeze it again after the views are finished rendering. """ self.topLevelOperatorView.FreezeCache.setValue(False) # This is hacky, but for now it's the only way to do it. # We need to make sure the rendering thread has actually seen that the cache # has been updated before we ask it to wait for all views to be 100% rendered. # If we don't wait, it might complete too soon (with the old data). ndim = len(self.topLevelOperatorView.Superpixels.meta.shape) self.topLevelOperatorView.Superpixels((0,)*ndim, (1,)*ndim).wait() # Wait for the image to be rendered into all three image views for imgView in self.editor.imageViews: if imgView.isVisible(): imgView.scene().joinRenderingAllTiles() self.topLevelOperatorView.FreezeCache.setValue(True) self.getLayerByName("Superpixels").visible = True th = threading.Thread(target=updateThread) th.start() def setupLayers(self): layers = [] op = self.topLevelOperatorView # Superpixels if op.Superpixels.ready(): layer = ColortableLayer( LazyflowSource(op.Superpixels), self._sp_colortable ) layer.colortableIsRandom = True layer.name = "Superpixels" layer.visible = True layer.opacity = 0.5 layers.append(layer) del layer # Debug layers if op.debug_results: for name, compressed_array in op.debug_results.items(): axiskeys = op.Superpixels.meta.getAxisKeys()[:-1] # debug images don't have a channel axis permutation = map(lambda key: axiskeys.index(key) if key in axiskeys else None, 'txyzc') arraysource = ArraySource( TransposedView(compressed_array, permutation) ) if compressed_array.dtype == np.uint32: layer = ColortableLayer(arraysource, self._sp_colortable) else: layer = GrayscaleLayer(arraysource) # TODO: Normalize? Maybe the drange should be included with the debug image. layer.name = name layer.visible = False layer.opacity = 1.0 layers.append(layer) del layer # Threshold if op.ThresholdedInput.ready(): layer = ColortableLayer( LazyflowSource(op.ThresholdedInput), self._threshold_colortable ) layer.name = "Thresholded Input" layer.visible = True layer.opacity = 1.0 layers.append(layer) del layer # Raw Data (grayscale) if op.Input.ready(): layer = self._create_grayscale_layer_from_slot( op.Input, op.Input.meta.getTaggedShape()['c'] ) layer.name = "Input" layer.visible = False layer.opacity = 1.0 layers.append(layer) del layer # Raw Data (grayscale) if op.RawData.ready(): layer = self.createStandardLayerFromSlot( op.RawData ) layer.name = "Raw Data" layer.visible = True layer.opacity = 1.0 layers.append(layer) del layer return layers
class View(QMainWindow, auxilia.Actions): def __init__(self, configuration, mpdclient, app): QMainWindow.__init__(self) self.app = app self.focus = time() self.shuttingDown = False self.config = configuration self.mpdclient = mpdclient self.appIcon = os.path.abspath(DATA_DIR+'icons/Pythagora.png') uic.loadUi(DATA_DIR+'ui/Pythagora.ui', self) self.KDE = KDE self.setWindowTitle('Pythagora') self.setWindowIcon(QIcon(self.appIcon)) # Load all forms. self.createViews() # Create 'Connect to' menu. self.menuConnect = QMenu('Connect To') self.menuConnect.menuAction().setIcon(auxilia.PIcon('network-disconnect')) self.connectButton = QToolButton() self.connectButton.setPopupMode(QToolButton.InstantPopup) self.connectButton.setIcon(auxilia.PIcon('network-disconnect')) self.connectButton.setMenu(self.menuConnect) # Create 'MDP' menu. self.menuMPD = QMenu('MPD') self.menuMPD.menuAction().setIcon(auxilia.PIcon('network-workgroup')) self.mpdButton = QToolButton() self.mpdButton.setPopupMode(QToolButton.InstantPopup) self.mpdButton.setIcon(auxilia.PIcon('network-workgroup')) self.mpdButton.setMenu(self.menuMPD) self.reloadLibrary = self.actionLibReload(self.menuMPD, self.__libReload) self.updateLibrary = self.actionLibUpdate(self.menuMPD, lambda: self.mpdclient.send('update')) self.rescanLibrary = self.actionLibRescan(self.menuMPD, lambda: self.mpdclient.send('rescan')) # Fill Toolbar. self.toolBar.addWidget(self.connectButton) self.toolBar.addWidget(self.mpdButton) # Fill Statusbar. self.serverLabel = QLabel('Not connected') self.numSongsLabel = QLabel('Songs') self.playTimeLabel = QLabel('playTime') self.statusbar.addWidget(self.serverLabel) self.statusbar.addPermanentWidget(self.numSongsLabel) self.statusbar.addPermanentWidget(self.playTimeLabel) self.connect(self.menuConnect, SIGNAL('aboutToShow()'), self.__buildConnectTo) self.connect(self.actionExit,SIGNAL('triggered()'),self.app.quit) self.connect(self.actionSettings,SIGNAL('triggered()'),self.showConfig) # Set up trayicon and menu. if KDE: self.trayIcon = KTrayIcon(self.appIcon, self) else: self.trayIcon = QTrayIcon(self.appIcon, self) connectMenuAction = self.menuConnect.menuAction() self.trayIcon.addMenuItem(connectMenuAction) self.trayIcon.addMenuItem(self.actionSettings) self.connect(self.trayIcon, SIGNAL('activate()'), self.toggleHideRestore) self.connect(self.trayIcon, SIGNAL('secondaryActivateRequested(QPoint)'), self.__playPause) self.connect(self.tabs, SIGNAL('currentChanged(int)'), self.__tabsIndexChanged) self.connect(self.tabs.tabBar(), SIGNAL('tabMoved(int,int)'), self.__tabMoved) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self.__storeSplitter) # Apply configuration. self.resize(configuration.mgrSize) self.splitter.setSizes(configuration.mgrSplit) self.tabs.setCurrentIndex(configuration.tabsIndex) self.closeEvent = self.closeEvent self.connect(self.app,SIGNAL('aboutToQuit()'),self.shutdown) self.show() #============================================================================== # Code for switching tabs on drag & drop. (__init__() continues) #============================================================================== # Instantiate timer self.tabTimer = QTimer() self.connect(self.tabTimer, SIGNAL('timeout()'), self.__selectTab) # Overload the default dragEvents. (none?) self.tabs.dragLeaveEvent = self.dragLeaveEvent self.tabs.dragEnterEvent = self.dragEnterEvent self.tabs.dragMoveEvent = self.dragMoveEvent def dragEnterEvent(self, event): '''Starts timer on enter and sets first position.''' self.tabPos = event.pos() event.accept() self.tabTimer.start(500) def dragLeaveEvent(self, event): '''If the mouse leaves the tabWidget stop the timer.''' self.tabTimer.stop() def dragMoveEvent(self, event): '''Keep track of the mouse and change the position, restarts the timer when moved.''' tabPos = event.pos() moved = tabPos.manhattanLength() - self.tabPos.manhattanLength() if moved > 7 or moved < -7: self.tabTimer.start(500) self.tabPos = tabPos def __selectTab(self): '''Changes the view to the tab where the mouse was hovering above.''' index = self.tabs.tabBar().tabAt(self.tabPos) self.tabs.setCurrentIndex(index) self.tabTimer.stop() def __libReload(self): self.mpdclient.send('listallinfo', callback= lambda mainlist: self.emit(SIGNAL('reloadLibrary'), mpdlibrary.Library(mainlist))) #============================================================================== def createViews(self): '''Set up our different view handlers.''' # Standard views. self.playerForm = PlayerForm(self, self.app, self.mpdclient, self.config) self.currentList = CurrentPlaylistForm.CurrentPlaylistForm(self, self.app, self.mpdclient, self.config) # Plugin views. loadedPlugins = {} for plugin in plugins.allPlugins: plugin = plugin.getWidget(self, self.mpdclient, self.config) loadedPlugins[plugin.moduleName] = plugin for name in self.config.tabOrder: if name in loadedPlugins: plugin = loadedPlugins.pop(name) self.tabs.addTab(plugin, auxilia.PIcon(plugin.moduleIcon), plugin.moduleName) for plugin in loadedPlugins.values(): self.tabs.addTab(plugin, auxilia.PIcon(plugin.moduleIcon), plugin.moduleName) order = self.config.tabOrder order.append(plugin.moduleName) self.config.tabOrder = order def shutdown(self): self.shuttingDown = True self.app.processEvents() self.mpdclient.disconnect() self.config.mgrSize = self.size() print 'debug: shutdown finished' def showConfig(self): self.config.showConfiguration(self) def closeEvent(self, event): '''Catch MainWindow's close event so we can hide it instead.''' self.hide() event.ignore() def __storeSplitter(self): self.config.mgrSplit = self.splitter.sizes() def __tabsIndexChanged(self, value): self.config.tabsIndex = self.tabs.currentIndex() def __tabMoved(self, old, new): print "DEBUG: Tab from", old, "moved to", new order = self.config.tabOrder order.insert(new, order.pop(old)) self.config.tabOrder = order def __toggleShoutCast(self, value): self.config.showShoutcast = value self.stackedWidget.setCurrentIndex(value) def toggleHideRestore(self): '''Show or hide the window based on some parameters. We can detect when we are obscured and come to the top. In other cases we hide if mapped and show if not. ''' if KDE: if KWindowSystem.activeWindow() == self.winId() and self.isVisible(): self.hide() else: self.show() KWindowSystem.forceActiveWindow(self.winId()) else: if self.isVisible(): self.hide() else: self.show() def __playPause(self): self.playerForm.play.emit(SIGNAL('clicked(bool)'), True) def __buildConnectTo(self): self.menuConnect.clear() self.menuConnect.addAction(auxilia.PIcon('dialog-cancel'), 'None (disconnect)') connected = self.mpdclient.connected() for server in self.config.knownHosts: if connected and self.config.server and self.config.server[0] == server: icon = auxilia.PIcon('network-connect') else: icon = auxilia.PIcon('network-disconnect') self.menuConnect.addAction(icon, server)
class pat_toolbar: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. Args: iface (QgsInterface): An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'pat_plugin_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.actions = [] # Look for the existing menu self.menuPrecAg = self.iface.mainWindow().findChild(QMenu, 'm{}Menu'.format(PLUGIN_SHORT)) # If the menu does not exist, create it! if not self.menuPrecAg: self.menuPrecAg = QMenu('{}'.format(PLUGIN_SHORT), self.iface.mainWindow().menuBar()) self.menuPrecAg.setObjectName('m{}Menu'.format(PLUGIN_SHORT)) actions = self.iface.mainWindow().menuBar().actions() lastAction = actions[-1] self.iface.mainWindow().menuBar().insertMenu(lastAction, self.menuPrecAg) # create a toolbar self.toolbar = self.iface.addToolBar(u'{} Toolbar'.format(PLUGIN_SHORT)) self.toolbar.setObjectName(u'm{}ToolBar'.format(PLUGIN_SHORT)) # Load Defaults settings for First time... for eaKey in ['BASE_IN_FOLDER', 'BASE_OUT_FOLDER']: sFolder = read_setting(PLUGIN_NAME + '/' + eaKey) if sFolder is None or not os.path.exists(sFolder): sFolder = os.path.join(os.path.expanduser('~'), PLUGIN_NAME) if not os.path.exists(sFolder): os.mkdir(sFolder) write_setting(PLUGIN_NAME + '/' + eaKey, os.path.join(os.path.expanduser('~'), PLUGIN_NAME)) self.DEBUG = config.get_debug_mode() self.vesper_queue = [] self.vesper_queue_showing = False self.processVesper = None self.vesper_exe = check_vesper_dependency(iface) if not os.path.exists(TEMPDIR): os.mkdir(TEMPDIR) def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. Args: message (str, QString): String for translation. Returns: QString: Translated version of message. """ return QCoreApplication.translate('pat', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, tool_tip=None, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. Args: icon_path (str): Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. text (str): Text that should be shown in menu items for this action. callback (function): Function to be called when the action is triggered. enabled_flag (bool): A flag indicating if the action should be enabled by default. Defaults to True. add_to_menu (bool): Flag indicating whether the action should also be added to the menu. Defaults to True. add_to_toolbar (bool): Flag indicating whether the action should also be added to the toolbar. Defaults to True. tool_tip (str): Optional text to show in a popup when mouse pointer hovers over the action. status_tip (str): Optional text to show in the status bar when mouse pointer hovers over the action. whats_this (QWidget): Parent widget for the new action. Defaults None. parent (): Optional text to show in the status bar when the mouse pointer hovers over the action. Returns: QAction: The action that was created. Note that the action is also added to self.actions list. """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if tool_tip is not None: action.setToolTip(tool_tip) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.menuPrecAg.addAction(action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" '''Create new menu item source:https://gis.stackexchange.com/questions/169869/adding-multiple-plugins-to-custom-pluginMenu-in-qgis/169880#169880 https://gis.stackexchange.com/questions/127150/how-to-customize-the-qgis-gui-using-python ''' # Finally, add your action to the menu and toolbar self.add_action( icon_path=':/plugins/pat/icons/icon_blockGrid.svg', text=self.tr(u'Create block grid'), tool_tip=self.tr(u'Create raster and VESPER grids for block polygons.'), status_tip=self.tr(u'Create raster and VESPER grids for block polygons.'), callback=self.run_blockGrid, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_cleanTrimPoints.svg', text=self.tr(u'Clean, trim and normalise data points'), tool_tip=self.tr(u'Clean, trim and normalise data points'), status_tip=self.tr(u'Clean, trim and normalise data points'), callback=self.run_cleanTrimPoints, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_vesperKriging.svg', text=self.tr(u'Run kriging using VESPER'), tool_tip=self.tr(u'Run kriging using VESPER'), status_tip=self.tr(u'Run kriging using VESPER'), callback=self.run_preVesper, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_importVesperKriging.svg', text=self.tr(u'Import VESPER results'), tool_tip=self.tr(u'Import VESPER results'), status_tip=self.tr(u'Import VESPER results'), add_to_toolbar=False, callback=self.run_postVesper, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_pointTrailToPolygon.svg', text=self.tr(u'Create polygons from on-the-go GPS point trail'), tool_tip=self.tr(u'Create polygons from on-the-go GPS point trail'), status_tip=self.tr(u'Create polygons from on-the-go GPS point trail'), add_to_toolbar=False, callback=self.run_pointTrailToPolygon, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_rescaleNormalise.svg', text=self.tr(u'Rescale or normalise raster'), tool_tip=self.tr(u'Rescale or normalise raster'), status_tip=self.tr(u'Rescale or normalise raster'), add_to_toolbar=False, callback=self.run_rescaleNormalise, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_randomPixel.svg', text=self.tr(u'Generate random pixel selection'), tool_tip=self.tr(u'Generate random pixel selection'), status_tip=self.tr(u'Generate random pixel selection'), add_to_toolbar=True, callback=self.run_generateRandomPixels, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_gridExtract.svg', text=self.tr(u'Extract raster pixel statistics for points'), tool_tip=self.tr(u'Extract raster pixel statistics for points'), status_tip=self.tr(u'Extract raster pixel statistics for points'), add_to_toolbar=True, callback=self.run_gridExtract, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_calcImgIndices.svg', text=self.tr(u'Calculate image indices for blocks'), tool_tip=self.tr(u'Calculate image indices for blocks'), status_tip=self.tr(u'Calculate image indices for blocks'), add_to_toolbar=True, callback=self.run_calculateImageIndices, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_resampleToBlock.svg', text=self.tr(u'Resample image band to blocks'), tool_tip=self.tr(u'Resample image band to blocks'), status_tip=self.tr(u'Resample image band to blocks'), add_to_toolbar=True, callback=self.run_resampleImage2Block, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_kMeansCluster.svg', text=self.tr(u'Create zones with k-means clustering'), tool_tip=self.tr(u'Create zones with k-means clustering'), status_tip=self.tr(u'Create zones with k-means clustering'), add_to_toolbar=True, callback=self.run_kMeansClustering, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_stripTrialPoints.svg', text=self.tr(u'Create strip trial points'), tool_tip=self.tr(u'Create strip trial points'), status_tip=self.tr(u'Create strip trial points'), add_to_toolbar=True, callback=self.run_stripTrialPoints, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_t-test.svg', text=self.tr(u'Run strip trial t-test analysis'), tool_tip=self.tr(u'Run strip trial t-test analysis'), status_tip=self.tr(u'Run strip trial t-test analysis'), add_to_toolbar=True, callback=self.run_tTestAnalysis, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_wholeOfBlockExp.svg', text=self.tr(u'Whole-of-block analysis'), tool_tip=self.tr(u'Whole-of-block analysis using co-kriging'), status_tip=self.tr(u'Whole-of-block analysis using co-kriging'), add_to_toolbar=True, callback=self.run_wholeOfBlockAnalysis, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_persistor.svg', text=self.tr(u'Persistor'), tool_tip=self.tr(u'Persistence over years'), status_tip=self.tr(u'Persistence over years'), add_to_toolbar=True, callback=self.run_persistor, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_rasterSymbology.svg', text=self.tr(u'Apply Raster Symbology'), tool_tip=self.tr(u'Apply Raster Symbology'), status_tip=self.tr(u'Apply Raster Symbology'), add_to_toolbar=True, callback=self.run_rasterSymbology, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_help.svg', text=self.tr(u'Help'), tool_tip=self.tr(u'Help'), status_tip=self.tr(u'Help'), callback=self.run_help, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_settings.svg', text=self.tr(u'Settings'), tool_tip=self.tr(u'Settings'), add_to_toolbar=False, status_tip=self.tr(u'Settings'), callback=self.run_settings, parent=self.iface.mainWindow()) self.add_action( icon_path=':/plugins/pat/icons/icon_about.svg', text=self.tr(u'About'), tool_tip=self.tr(u'About'), status_tip=self.tr(u'About'), add_to_toolbar=False, callback=self.run_about, parent=self.iface.mainWindow()) @staticmethod def clear_modules(): """Unload pyprecag functions and try to return QGIS. source: inasafe plugin """ # next lets force remove any pyprecag related modules modules = [] for module in sys.modules: if 'pyprecag' in module: LOGGER.debug('Removing: %s' % module) modules.append(module) for module in modules: del (sys.modules[module]) # Lets also clean up all the path additions that were made package_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) LOGGER.debug('Path to remove: %s' % package_path) # We use a list comprehension to ensure duplicate entries are removed LOGGER.debug(sys.path) sys.path = [y for y in sys.path if package_path not in y] LOGGER.debug(sys.path) def unload(self): """Removes the plugin menu/toolbar item and icon from QGIS GUI and clean up temp folder""" if len(self.vesper_queue) > 0: replyQuit = QMessageBox.information(self.iface.mainWindow(), "Quit QGIS", "Quitting QGIS with {} tasks in the " "VESPER queue.\n\t{}".format(len(self.vesper_queue), '\n\t'.join([ea['control_file'] for ea in self.vesper_queue])), QMessageBox.Ok) stop_logging('pyprecag') layermap = QgsMapLayerRegistry.instance().mapLayers() RemoveLayers = [] for name, layer in layermap.iteritems(): if TEMPDIR in layer.source(): RemoveLayers.append(layer.id()) if len(RemoveLayers) > 0: QgsMapLayerRegistry.instance().removeMapLayers(RemoveLayers) # remove the PrecisionAg Temp Folder. try: if not self.DEBUG and os.path.exists(TEMPDIR): shutil.rmtree(TEMPDIR) except Exception as err: exc_type, exc_value, exc_traceback = sys.exc_info() mess = str(traceback.format_exc()) print(mess) self.menuPrecAg.clear() for action in self.actions: self.iface.removePluginMenu(u'{}Menu'.format(PLUGIN_SHORT), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar del self.menuPrecAg self.clear_modules() def queueAddTo(self, vesp_dict): """ Add a control file to the VESPER queue""" if next((x for x in self.vesper_queue if x['control_file'] == vesp_dict["control_file"]) , None) is not None: self.iface.messageBar().pushMessage('Control file is already in the VESPER queue. {}'.format( vesp_dict['control_file']),level=QgsMessageBar.WARNING, duration=15) self.queueDisplay() else: self.vesper_queue.append(vesp_dict) message = 'Added control file to VESPER queue. The queue now contains {} tasks'.format( len(self.vesper_queue)) self.iface.messageBar().pushMessage(message, level=QgsMessageBar.INFO, duration=15) def queueDisplay(self): """display the VESPER queue in the python console""" # open the python console try: pythonConsolePanel = self.iface.mainWindow().findChild(QDockWidget, 'PythonConsole') if not pythonConsolePanel.isVisible(): self.iface.actionShowPythonDialog().trigger() except: # the above will bail if sitting on RecentProjects empty view. self.iface.actionShowPythonDialog().trigger() pythonConsolePanel = self.iface.mainWindow().findChild(QDockWidget, 'PythonConsole') ctrl_width = len(max([os.path.basename(ea['control_file']) for ea in self.vesper_queue], key=len)) epsg_width = len(max([str(ea['epsg']) for ea in self.vesper_queue], key=len)) header = '{:3}\t{:<{cw}}\t{:5}\t{:>{ew}} {}'.format( '#', 'Control File', 'Import', 'EPSG', 'Folder', cw=ctrl_width + 10, ew=epsg_width + 10) print('\n' + '-' * len(header)) print(header) print('-' * len(header)) for i, ea in enumerate(self.vesper_queue): print('{:3}\t{:<{cw}}\t{:5}\t{:>{ew}}\t{}'.format( i + 1, os.path.basename(ea['control_file']), str(bool(ea['epsg'] > 0)), ea['epsg'], os.path.dirname(ea['control_file']), cw=ctrl_width + 10, ew=epsg_width + 10)) print('\n') def queueClear(self): """Clear the VESPER queue of all pending jobs""" # clear all but the one running. if self.processVesper is None: self.vesper_queue = [] self.queueStatusBarHide() else: self.vesper_queue = self.vesper_queue[:1] self.lblVesperQueue.setText('{} tasks in VESPER queue'.format(len(self.vesper_queue))) self.queueDisplay() def queueStatusBarShow(self): """Add to QGIS status bar buttons to show and clear the VESPER queue""" # source: https://gis.stackexchange.com/a/153170 # https://github.com/ActiveState/code/blob/master/recipes/Python/578692_QGstartscript_Change_display/recipe-578692.py if not self.vesper_queue_showing: # it is not initiated self.iface.mainWindow().statusBar().setSizeGripEnabled(False) self.lblVesperQueue = QLabel() self.lblVesperQueue.setText('{} tasks in VESPER queue'.format(len(self.vesper_queue))) self.iface.mainWindow().statusBar().insertPermanentWidget(1, self.lblVesperQueue) self.btnShowQueue = QToolButton() # QToolButton() takes up less room self.btnShowQueue.setToolButtonStyle(Qt.ToolButtonTextOnly) self.btnShowQueue.setText("Show") self.btnShowQueue.clicked.connect(self.queueDisplay) self.iface.mainWindow().statusBar().insertPermanentWidget(2, self.btnShowQueue) self.btnClearQueue = QToolButton() # QPushButton() self.btnClearQueue.setToolButtonStyle(Qt.ToolButtonTextOnly) self.btnClearQueue.setText("Clear") self.btnClearQueue.pressed.connect(self.queueClear) self.iface.mainWindow().statusBar().insertPermanentWidget(3, self.btnClearQueue) self.vesper_queue_showing = True def queueStatusBarHide(self): """Remove VESPER queue information and buttons from the status bar""" for obj in [self.btnClearQueue, self.btnShowQueue, self.lblVesperQueue]: self.iface.mainWindow().statusBar().removeWidget(obj) del obj self.vesper_queue_showing = False def processRunVesper(self): """Run the next task in the VESPER queue""" # Queueing: http://www.qtforum.org/article/32172/qprocess-how-to-run-multiple-processes-in-a-loop.html self.vesper_run_time = time.time() if self.processVesper is None: self.processVesper = QProcess() # set a duration variable self.processVesper.started.connect(self.processStartedVesper) # sets a task for when finished. self.processVesper.finished.connect(self.processFinishedVesper) self.queueStatusBarShow() ctrl_file = self.vesper_queue[0]['control_file'] self.processVesper.setWorkingDirectory(os.path.dirname(ctrl_file)) # run and catch when finished: https://gist.github.com/justinfx/5174795 1)QProcess QTimer.singleShot(100, partial(self.processVesper.start, self.vesper_exe, [ctrl_file])) def processStartedVesper(self): # connected to process.started slot self.vesper_run_time = time.time() def processFinishedVesper(self, exitCode, exitStatus): # connected to process.finished slot """When VESPER is complete, import the results to TIFF and QGIS""" currentTask = self.vesper_queue[0] if exitCode == 0 and exitStatus == QProcess.NormalExit: self.processVesper.close() self.processVesper = None if currentTask['epsg'] > 0: try: out_PredTif, out_SETif, out_CITxt = vesper_text_to_raster(currentTask['control_file'], currentTask['epsg']) raster_sym = RASTER_SYMBOLOGY['Yield'] removeFileFromQGIS(out_PredTif) rasterLyr = addRasterFileToQGIS(out_PredTif, atTop=False) raster_apply_classified_renderer(rasterLyr, rend_type=raster_sym['type'], num_classes=raster_sym['num_classes'], color_ramp=raster_sym['colour_ramp']) removeFileFromQGIS(out_SETif) addRasterFileToQGIS(out_SETif, atTop=False) except Exception as err: message = "Could not import from VESPER to raster TIFF possibly due to a " \ "VESPER error.\n{}".format(os.path.basename(currentTask['control_file'])) LOGGER.error(message) message = "Completed VESPER kriging for {}\t Duration H:M:SS - {dur}".format( os.path.basename(currentTask['control_file']), dur=datetime.timedelta(seconds=time.time() - self.vesper_run_time)) self.iface.messageBar().pushMessage(message, level=QgsMessageBar.INFO, duration=15) LOGGER.info(message) else: message = "Error occurred with VESPER kriging for {}".format(currentTask['control_file']) self.iface.messageBar().pushMessage(message, level=QgsMessageBar.CRITICAL, duration=0) LOGGER.error(message) self.vesper_queue = self.vesper_queue[1:] # remove the recently finished one which will always be at position 0 self.lblVesperQueue.setText('{} tasks in VESPER queue'.format(len(self.vesper_queue))) if len(self.vesper_queue) > 0: self.vesper_run_time = time.time() self.processRunVesper() else: self.vesper_queue = [] self.vesper_run_time = '' self.queueStatusBarHide() return def run_persistor(self): """Run method for the Persistor dialog""" if parse_version(pyprecag.__version__) < parse_version('0.2.0'): self.iface.messageBar().pushMessage("Persistor is not supported in " "pyprecag {}. Upgrade to version 0.3.0+".format( pyprecag.__version__), level=QgsMessageBar.WARNING, duration=15) return dlgPersistor = PersistorDialog(self.iface) # Show the dialog dlgPersistor.show() if dlgPersistor.exec_(): message = 'Persistor completed successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) # LOGGER.info(message) # Close Dialog dlgPersistor.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_wholeOfBlockAnalysis(self): """Run method for the fit to block grid dialog""" # https://gis.stackexchange.com/a/160146 result = check_R_dependency() if result is not True: self.iface.messageBar().pushMessage("R configuration", result, level=QgsMessageBar.WARNING, duration=15) return proc_alg_mess = ProcessingAlgMessages(self.iface) QgsMessageLog.instance().messageReceived.connect(proc_alg_mess.processingCatcher) # Then get the algorithm you're interested in (for instance, Join Attributes): alg = Processing.getAlgorithm("r:wholeofblockanalysis") if alg is None: self.iface.messageBar().pushMessage("Whole-of-block analysis algorithm could not" " be found", level=QgsMessageBar.CRITICAL) return # Instantiate the commander window and open the algorithm's interface cw = CommanderWindow(self.iface.mainWindow(), self.iface.mapCanvas()) if alg is not None: cw.runAlgorithm(alg) # if proc_alg_mess.alg_name == '' then cancel was clicked if proc_alg_mess.error: self.iface.messageBar().pushMessage("Whole-of-block analysis", proc_alg_mess.error_msg, level=QgsMessageBar.CRITICAL, duration=0) elif proc_alg_mess.alg_name != '': data_column = proc_alg_mess.parameters['Data_Column'] # load rasters into qgis as grouped layers. for key, val in proc_alg_mess.output_files.items(): grplyr = os.path.join('Whole-of-block {}'.format(data_column), val['title']) for ea_file in val['files']: removeFileFromQGIS(ea_file) raster_layer = addRasterFileToQGIS(ea_file, group_layer_name=grplyr, atTop=False) if key in ['p_val']: raster_apply_unique_value_renderer(raster_layer) self.iface.messageBar().pushMessage("Whole-of-block analysis Completed Successfully!", level=QgsMessageBar.INFO, duration=15) del proc_alg_mess def run_stripTrialPoints(self): if parse_version(pyprecag.__version__) < parse_version('0.2.0'): self.iface.messageBar().pushMessage( "Create strip trial points tool is not supported in pyprecag {}. " "Upgrade to version 0.2.0+".format(pyprecag.__version__), level=QgsMessageBar.WARNING, duration=15) return """Run method for the Strip trial points dialog""" dlgStripTrialPoints = StripTrialPointsDialog(self.iface) # Show the dialog dlgStripTrialPoints.show() if dlgStripTrialPoints.exec_(): message = 'Strip trial points created successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) # LOGGER.info(message) # Close Dialog dlgStripTrialPoints.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_tTestAnalysis(self): if parse_version(pyprecag.__version__) < parse_version('0.3.0'): self.iface.messageBar().pushMessage("Create t-test analysis tool is not supported in " "pyprecag {}. Upgrade to version 0.3.0+".format( pyprecag.__version__), level=QgsMessageBar.WARNING, duration=15) return """Run method for the Strip trial points dialog""" dlg_tTestAnalysis = tTestAnalysisDialog(self.iface) # Show the dialog dlg_tTestAnalysis.show() if dlg_tTestAnalysis.exec_(): output_folder = dlg_tTestAnalysis.lneOutputFolder.text() import webbrowser try: from urllib import pathname2url # Python 2.x except: from urllib.request import pathname2url # Python 3.x def open_folder(): url = 'file:{}'.format(pathname2url(os.path.abspath(output_folder))) webbrowser.open(url) message = 'Strip trial t-test analysis completed!' # Add hyperlink to messagebar - this works but it places the text on the right, not left. # variation of QGIS-master\python\plugins\db_manager\db_tree.py # msgLabel = QLabel(self.tr('{0} <a href="{1}">{1}</a>'.format(message, output_folder)), self.iface.messageBar()) # msgLabel.linkActivated.connect(open_folder) # self.iface.messageBar().pushWidget(msgLabel,level=QgsMessageBar.SUCCESS, duration=15) # so use a button instead widget = self.iface.messageBar().createMessage('', message) button = QPushButton(widget) button.setText('Open Folder') button.pressed.connect(open_folder) widget.layout().addWidget(button) self.iface.messageBar().pushWidget(widget, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlg_tTestAnalysis.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_kMeansClustering(self): """Run method for the Calculate Image Indices dialog""" dlgKMeansCluster = KMeansClusterDialog(self.iface) # Show the dialog dlgKMeansCluster.show() if dlgKMeansCluster.exec_(): message = 'Zones with k-means clusters completed successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) # LOGGER.info(message) # Close Dialog dlgKMeansCluster.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_calculateImageIndices(self): """Run method for the Calculate Image Indices dialog""" dlgCalcImgIndices = CalculateImageIndicesDialog(self.iface) # Show the dialog dlgCalcImgIndices.show() if dlgCalcImgIndices.exec_(): message = 'Image indices calculated successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgCalcImgIndices.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_resampleImage2Block(self): """Run method for the Resample image to block grid dialog""" dlgResample2Block = ResampleImageToBlockDialog(self.iface) # Show the dialog dlgResample2Block.show() if dlgResample2Block.exec_(): message = 'Resample to block grid completed Successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgResample2Block.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_gridExtract(self): """Run method for the Grid Extract dialog""" dlgGridExtract = GridExtractDialog(self.iface) # Show the dialog dlgGridExtract.show() if dlgGridExtract.exec_(): output_file = dlgGridExtract.lneSaveCSVFile.text() import webbrowser try: from urllib import pathname2url # Python 2.x except: from urllib.request import pathname2url # Python 3.x def open_folder(): url = 'file:{}'.format(pathname2url(os.path.abspath(output_file))) webbrowser.open(url) message = 'Raster statistics for points extracted successfully !' #add a button to open the file outside qgis widget = self.iface.messageBar().createMessage('', message) button = QPushButton(widget) button.setText('Open File') button.pressed.connect(open_folder) widget.layout().addWidget(button) self.iface.messageBar().pushWidget(widget, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgGridExtract.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_generateRandomPixels(self): """Run method for the Generate random pixels dialog""" dlgGenRandomPixel = RandomPixelSelectionDialog(self.iface) # Show the dialog dlgGenRandomPixel.show() if dlgGenRandomPixel.exec_(): message = 'Random pixel selection completed successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgGenRandomPixel.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_rescaleNormalise(self): """Run method for the rescale/normalise dialog""" dlgRescaleNorm = RescaleNormaliseDialog(self.iface) # Show the dialog dlgRescaleNorm.show() if dlgRescaleNorm.exec_(): message = 'Rescale/Normalise completed successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgRescaleNorm.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_preVesper(self): """Run method for preVesper dialog""" dlgPreVesper = PreVesperDialog(self.iface) # show the dialog dlgPreVesper.show() if dlgPreVesper.exec_(): if dlgPreVesper.gbRunVesper.isChecked(): self.queueAddTo(dlgPreVesper.vesp_dict) self.processRunVesper() if len(self.vesper_queue) > 0: self.lblVesperQueue.setText('{} tasks in VESPER queue'.format(len(self.vesper_queue))) # Close Dialog dlgPreVesper.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_postVesper(self): """Run method for importing VESPER results dialog""" dlgPostVesper = PostVesperDialog(self.iface) # show the dialog dlgPostVesper.show() if dlgPostVesper.exec_(): if dlgPostVesper.chkRunVesper.isChecked(): self.queueAddTo(dlgPostVesper.vesp_dict) # if this is the first in the queue then start the processing. self.processRunVesper() if len(self.vesper_queue) > 0: self.lblVesperQueue.setText('{} tasks in VESPER queue'.format(len(self.vesper_queue))) # Close Dialog dlgPostVesper.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_cleanTrimPoints(self): """Run method for cleanTrimPoints dialog""" dlgCleanTrimPoints = CleanTrimPointsDialog(self.iface) # show the dialog dlgCleanTrimPoints.show() if dlgCleanTrimPoints.exec_(): output_folder = os.path.dirname(dlgCleanTrimPoints.lneSaveCSVFile.text()) import webbrowser try: from urllib import pathname2url # Python 2.x except: from urllib.request import pathname2url # Python 3.x def open_folder(): url = 'file:{}'.format(pathname2url(os.path.abspath(output_folder))) webbrowser.open(url) message = 'Cleaned and trimmed points successfully !' widget = self.iface.messageBar().createMessage('', message) button = QPushButton(widget) button.setText('Open Folder') button.pressed.connect(open_folder) widget.layout().addWidget(button) self.iface.messageBar().pushWidget(widget, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgCleanTrimPoints.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_blockGrid(self): """Run method for the block grid dialog""" dlgBlockGrid = BlockGridDialog(self.iface) # Show the dialog dlgBlockGrid.show() if dlgBlockGrid.exec_(): message = 'Block grid completed successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgBlockGrid.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_pointTrailToPolygon(self): """Run method for pointTrailToPolygon dialog""" dlgPointTrailToPolygon = PointTrailToPolygonDialog(self.iface) # show the dialog dlgPointTrailToPolygon.show() if dlgPointTrailToPolygon.exec_(): message = 'On-the-go point trail to polygon completed successfully !' self.iface.messageBar().pushMessage(message, level=QgsMessageBar.SUCCESS, duration=15) LOGGER.info(message) # Close Dialog dlgPointTrailToPolygon.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_rasterSymbology(self): """Run method for the Raster Symbology dialog""" dlgRasterSymbology = RasterSymbologyDialog(self.iface) # Show the dialog dlgRasterSymbology.show() if dlgRasterSymbology.exec_(): pass # Close Dialog dlgRasterSymbology.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_help(self): """Open the help PDF""" webbrowser.open_new('file:///' + os.path.join(PLUGIN_DIR, 'PAT_User_Manual.pdf#pagemode=bookmarks')) def run_about(self): """Run method for the about dialog""" dlgAbout = AboutDialog() if dlgAbout.exec_(): pass dlgAbout.deleteLater() # Refresh QGIS QCoreApplication.processEvents() def run_settings(self): """Run method for the about dialog""" dlgSettings = SettingsDialog() if dlgSettings.exec_(): self.vesper_exe = dlgSettings.vesper_exe self.DEBUG = config.get_debug_mode() dlgSettings.deleteLater()
class LogSParserMain(QMainWindow): """ This is the main class in the application. It's responsible for displaying the log data in a tabular format as well as allowing the user to filter the logs displayed. """ per_column_filter_out_set_list = list() per_column_filter_in_set_list = list() header = list() table_conditional_formatting_config = None def __init__(self): QMainWindow.__init__(self) self.graph_window_dict = {} self.menuFilter = None self.proxy_model = None self.table_data = None self.user_interface = Ui_Siraj() self.user_interface.setupUi(self) self.user_interface.mnuActionOpen.triggered.connect(self.menu_open_file) self.user_interface.mnuActionLoadConfigs.triggered.connect(self.menu_load_configs) self.user_interface.mnuActionExit.triggered.connect(self.menu_exit) self.user_interface.mnuActionAbout.triggered.connect(self.menu_about) self.user_interface.centralwidget.setLayout(self.user_interface.verticalLayout) self.user_interface.dckSourceContents.setLayout(self.user_interface.lytSource) self.user_interface.tblLogData.doubleClicked.connect(self.cell_double_clicked) self.user_interface.tblLogData.clicked.connect(self.cell_left_clicked) self.user_interface.tblLogData.keyPressEvent = self.cell_key_pressed self.user_interface.tblLogData.setContextMenuPolicy(Qt.CustomContextMenu) self.user_interface.tblLogData.customContextMenuRequested.connect(self.cell_right_clicked) self.user_interface.txtSourceFile.setReadOnly(True) self.is_table_visible = True self.is_source_visible = True self.user_interface.tblLogData.resizeColumnsToContents() self.user_interface.tblLogData.resizeRowsToContents() self.setup_context_menu() self.setup_toolbars() self.clipboard = QApplication.clipboard() self.is_filtering_mode_out = True self.matched_row_list = [] self.search_criteria_updated = True self.case_sensitive_search_type = Qt.CaseInsensitive self.is_wrap_search = True self.is_match_whole_word = False self.graph_marker_list = [] self.user_interface.tblLogData.setAcceptDrops(False) self.setAcceptDrops(True) self.load_configuration_file() self.toggle_source_view() def setup_toolbars(self): source_toolbar = self.addToolBar('SourceToolbar') self.user_interface.tbrActionToggleSourceView = QAction('C/C++', self) self.user_interface.tbrActionToggleSourceView.triggered.connect(self.toggle_source_view) self.user_interface.tbrActionToggleSourceView.setToolTip("Toggle source code view") self.user_interface.tbrActionToggleSourceView.setCheckable(True) self.user_interface.tbrActionToggleSourceView.setChecked(True) source_toolbar.addAction(self.user_interface.tbrActionToggleSourceView) search_toolbar = self.addToolBar("SearchToolbar") search_toolbar.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) self.ledSearchBox = QLineEdit() self.ledSearchBox.textChanged.connect(self.invalidate_search_criteria) self.ledSearchBox.keyPressEvent = self.search_box_key_pressed search_toolbar.addWidget(self.ledSearchBox) tbrActionPrevSearchMatch = QAction('<<', self) tbrActionPrevSearchMatch.triggered.connect(functools.partial(self.select_search_match, False)) tbrActionPrevSearchMatch.setToolTip("Go to previous search match") tbrActionNextSearchMatch = QAction('>>', self) tbrActionNextSearchMatch.triggered.connect(functools.partial(self.select_search_match, True)) tbrActionNextSearchMatch.setToolTip("Go to next search match") tbrActionIgnoreCase = QAction('Ignore Case', self) tbrActionIgnoreCase.setCheckable(True) tbrActionIgnoreCase.setChecked(True) tbrActionIgnoreCase.triggered.connect(self.set_search_case_sensitivity, tbrActionIgnoreCase.isChecked()) tbrActionIgnoreCase.setToolTip("Ignore case") tbrActionWrapSearch = QAction('Wrap Search', self) tbrActionWrapSearch.setCheckable(True) tbrActionWrapSearch.setChecked(True) tbrActionWrapSearch.triggered.connect(self.set_search_wrap, tbrActionWrapSearch.isChecked()) tbrActionWrapSearch.setToolTip("Wrap Search") tbrActionMatchWholeWord = QAction('Match Whole Word', self) tbrActionMatchWholeWord.setCheckable(True) tbrActionMatchWholeWord.setChecked(False) tbrActionMatchWholeWord.triggered.connect(self.set_match_whole_word, tbrActionMatchWholeWord.isChecked()) tbrActionMatchWholeWord.setToolTip("Match Whole Word") search_toolbar.addAction(tbrActionPrevSearchMatch) search_toolbar.addAction(tbrActionNextSearchMatch) search_toolbar.addAction(tbrActionIgnoreCase) search_toolbar.addAction(tbrActionMatchWholeWord) search_toolbar.addAction(tbrActionWrapSearch) def set_search_case_sensitivity(self, ignore_case): self.invalidate_search_criteria() if(ignore_case): self.case_sensitive_search_type = Qt.CaseInsensitive else: self.case_sensitive_search_type = Qt.CaseSensitive def set_search_wrap(self, wrap_search): self.invalidate_search_criteria() self.is_wrap_search = wrap_search def set_match_whole_word(self, match_whole_word): self.invalidate_search_criteria() self.is_match_whole_word = match_whole_word def invalidate_search_criteria(self): self.search_criteria_updated = True; self.matched_row_list.clear() def get_matched_row_list(self, key_column, search_criteria, case_sensitivity): search_proxy = QSortFilterProxyModel() search_proxy.setSourceModel(self.user_interface.tblLogData.model()) search_proxy.setFilterCaseSensitivity(case_sensitivity) search_proxy.setFilterKeyColumn(key_column) if self.is_match_whole_word: search_criteria = r"\b{}\b".format(search_criteria) search_proxy.setFilterRegExp(search_criteria) matched_row_list = [] for proxy_row in range(search_proxy.rowCount()): match_index = search_proxy.mapToSource(search_proxy.index(proxy_row, key_column)) matched_row_list.append(match_index.row()) self.search_criteria_updated = False return matched_row_list def select_search_match(self, is_forward): selected_indexes = self.get_selected_indexes() if(len(selected_indexes) == 0): self.display_message_box( "No selection", "Please select a cell from the column you want to search", QMessageBox.Warning) else: index = self.get_selected_indexes()[0] row = index.row() column = index.column() search_criteria = self.ledSearchBox.text() if(self.search_criteria_updated): self.matched_row_list = self.get_matched_row_list(column, search_criteria, self.case_sensitive_search_type) if(len(self.matched_row_list) > 0): is_match_found = False if(is_forward): matched_row_index = bisect_left(self.matched_row_list, row) if((matched_row_index < len(self.matched_row_list) - 1)): if(self.matched_row_list[matched_row_index] == row): matched_row_index += 1 is_match_found = True elif(self.is_wrap_search): matched_row_index = 0 is_match_found = True else: matched_row_index = bisect_right(self.matched_row_list, row) if(matched_row_index > 0): matched_row_index -= 1 if((matched_row_index > 0)): if((self.matched_row_list[matched_row_index] == row)): matched_row_index -= 1 is_match_found = True elif(self.is_wrap_search): matched_row_index = len(self.matched_row_list) - 1 is_match_found = True if(is_match_found): self.select_cell_by_row_and_column(self.matched_row_list[matched_row_index], column) else: self.display_message_box( "No match found", 'Search pattern "{}" was not found in column "{}"'.format(search_criteria, self.header[column]), QMessageBox.Warning) def reset_per_config_file_data(self): self.graph_window_dict.clear() self.reset_per_log_file_data() self.table_data = None self.table_model = None self.proxy_model = None def load_configuration_file(self, config_file_path="siraj_configs.json"): self.reset_per_config_file_data() self.config = LogSParserConfigs(config_file_path) self.log_file_full_path = self.config.get_config_item("log_file_full_path") self.log_trace_regex_pattern = self.config.get_config_item("log_row_pattern") self.time_stamp_column = self.config.get_config_item("time_stamp_column_number_zero_based") self.user_data_column_zero_based = self.config.get_config_item("user_data_column_zero_based") self.external_editor_configs = self.config.get_config_item("external_editor_configs") cross_reference_configs = self.config.get_config_item("source_cross_reference_configs") self.file_column = cross_reference_configs["file_column_number_zero_based"] self.file_column_pattern = cross_reference_configs["file_column_pattern"] self.line_column = cross_reference_configs["line_column_number_zero_based"] self.line_column_pattern = cross_reference_configs["line_column_pattern"] self.graph_configs = self.config.get_config_item("graph_configs") self.root_source_path_prefix = cross_reference_configs["root_source_path_prefix"] self.syntax_highlighting_style = cross_reference_configs["pygments_syntax_highlighting_style"] self.table_conditional_formatting_config = self.config.get_config_item("table_conditional_formatting_configs") self.load_log_file(self.log_file_full_path) def load_graphs(self, graph_configs, table_data): pg.setConfigOption('background', QColor("white")) pg.setConfigOption('foreground', QColor("black")) pg.setConfigOptions(antialias=True) window_dict = graph_configs["window_dict"] series_list = [] for window_name in window_dict: window_handle = pg.GraphicsWindow(title=window_name) self.graph_window_dict[window_name] = window_handle window_handle.show() plot_dict = window_dict[window_name]["plot_dict"] first_plot_name_in_the_window = "" for plot_name in plot_dict: plot_row = plot_dict[plot_name]["row"] # plot_column = plot_dict[plot_name]["column"] # plot_row_span = plot_dict[plot_name]["row_span"] # plot_column_span = plot_dict[plot_name]["column_span"] plot_handle = window_handle.addPlot( name=plot_name, title=plot_name, row=plot_row, col=1,#plot_column, rowspan=1,#plot_row_span, colspan=1)#plot_column_span) plot_handle.addLegend() if first_plot_name_in_the_window == "": first_plot_name_in_the_window = plot_name plot_handle.setXLink(first_plot_name_in_the_window) marker = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen(width=1, color=QColor("red"))) plot_handle.addItem(marker, ignoreBounds=True) self.graph_marker_list.append(marker) plot_handle.scene().sigMouseClicked.connect(functools.partial(self.graph_mouse_clicked, plot_handle)) plot_handle.scene().setClickRadius(50) series_dict = plot_dict[plot_name]["series_dict"] for series_name in series_dict: series_symbol = series_dict[series_name]["symbol"] series_color = series_dict[series_name]["color"] series_pattern = series_dict[series_name]["pattern"] series_list.append((series_name, series_symbol, series_color, series_pattern, [], [], plot_handle)) for row_number, row_data in enumerate(table_data): for (series_name, series_symbol, series_color, series_pattern, x_point_list, y_point_list, plot_handle) in series_list: cell_to_match = row_data[self.user_data_column_zero_based] m = re.search(series_pattern, cell_to_match) if m is not None: x_point_list.append(row_number) y_point_list.append(int(m.group(1))) for (series_name, series_symbol, series_color, series_pattern, x_point_list, y_point_list, plot_handle) in series_list: plot_handle.plot( x_point_list, y_point_list, pen=pg.mkPen(width=1, color=QColor(series_color)), symbol=series_symbol, symbolPen='w', symbolBrush=QColor(series_color), name=series_name) # graphs = list(sorted(graph_configs.keys(), key=lambda k: graph_configs[k]["index"])) # graph_data = [([], [],) for _ in graphs] # # self.graph_marker_list = [] # # for row_number, row_data in enumerate(table_data): # for graph_number, graph_name in enumerate(graphs): # cell_to_match = row_data[graph_configs[graph_name]["column"]] # m = re.search(graph_configs[graph_name]["pattern"], cell_to_match) # if (m is not None): # graph_data[graph_number][0].append(row_number) # X-Axis value # graph_data[graph_number][1].append(int(m.group(1))) # Y-Axis value # # for graph in graphs: # window = None # wnd = graph_configs[graph]["window"] # if (wnd in self.graph_window_dict): # window = self.graph_window_dict[wnd] # window.clear() # # is_new_window = False # first_plot_name = None # for graph_number, graph in enumerate(graphs): # window = None # wnd = graph_configs[graph]["window"] # if (wnd in self.graph_window_dict): # window = self.graph_window_dict[wnd] # is_new_window = False # else: # is_new_window = True # window = pg.GraphicsWindow(title=wnd) # # self.graph_window_dict[wnd] = window # # p = window.addPlot(name=graph, title=graph) # # p.plot(graph_data[graph_number][0], # graph_data[graph_number][1], # pen=pg.mkPen(width=1, color=QColor(graph_configs[graph]["color"])), # symbol=graph_configs[graph]["symbol"], symbolPen='w', # symbolBrush=QColor(graph_configs[graph]["color"]), name=graph) # p.showGrid(x=True, y=True) # if first_plot_name == None: # first_plot_name = graph # p.setXLink(first_plot_name) # marker = pg.InfiniteLine(angle=90, movable=False) # p.addItem(marker, ignoreBounds=True) # self.graph_marker_list.append(marker) # p.scene().sigMouseClicked.connect(functools.partial(self.graph_mouse_clicked, p)) # # window.nextRow() def graph_mouse_clicked(self, plt, evt): point = plt.vb.mapSceneToView(evt.scenePos()) self.select_cell_by_row_and_column(int(round(point.x())), self.user_data_column_zero_based) self.update_graph_markers() def setup_context_menu(self): self.menuFilter = QMenu(self) self.hide_action = QAction('Hide selected values', self) self.show_only_action = QAction('Show only selected values', self) self.clear_all_filters_action = QAction('Clear all filters', self) self.copy_selection_action = QAction('Copy selection', self) self.unhide_menu = QMenu('Unhide item from selected column', self.menuFilter) self.hide_action.triggered.connect(self.hide_rows_based_on_selected_cells) self.show_only_action.triggered.connect(self.show_rows_based_on_selected_cells) self.clear_all_filters_action.triggered.connect(self.clear_all_filters) self.copy_selection_action.triggered.connect(self.prepare_clipboard_text) self.menuFilter.addAction(self.hide_action) self.menuFilter.addMenu(self.unhide_menu) self.menuFilter.addAction(self.show_only_action) self.menuFilter.addAction(self.clear_all_filters_action) self.menuFilter.addSeparator() self.menuFilter.addAction(self.copy_selection_action) self.hide_action.setShortcut('Ctrl+H') self.show_only_action.setShortcut('Ctrl+O') self.clear_all_filters_action.setShortcut('Ctrl+Del') self.copy_selection_action.setShortcut("Ctrl+C") def toggle_source_view(self): self.is_source_visible = not self.is_source_visible self.user_interface.tbrActionToggleSourceView.setChecked(self.is_source_visible) self.user_interface.dckSource.setVisible(self.is_source_visible) logging.info("Source view is now {}".format("Visible" if self.is_source_visible else "Invisible")) def display_message_box(self, title, message, icon): """ Show the about box. """ message_box = QMessageBox(self); message_box.setWindowTitle(title); message_box.setTextFormat(Qt.RichText); message_box.setText(message) message_box.setIcon(icon) message_box.exec_() def menu_about(self): """ Show the about box. """ about_text = """ Copyright 2015 Mohamed Galal El-Din Ebrahim (<a href="mailto:[email protected]">[email protected]</a>) <br> <br> siraj is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License. <br> <br> siraj is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. <br> <br> You should have received a copy of the GNU General Public License along with siraj. If not, see <a href="http://www.gnu.org/licenses">http://www.gnu.org/licenses</a>. """ self.display_message_box("About", about_text, QMessageBox.Information) def menu_exit(self): """ Handles the exit menu clicked event. """ exit(0) def menu_open_file(self): """ Handles the open menu clicked event. """ self.log_file_full_path = QFileDialog.getOpenFileName( self, 'Open Log File', os.getcwd()) if(self.log_file_full_path != ''): self.load_log_file(self.log_file_full_path) def menu_load_configs(self): """ Loads a new configuration file. """ self.config_file_full_path = QFileDialog.getOpenFileName( self, 'Open Config File', os.getcwd()) if(self.config_file_full_path != ''): self.load_configuration_file(self.config_file_full_path) def reset_per_log_file_data(self): self.invalidate_search_criteria() def load_log_file(self, log_file_full_path): """ Loads the given log file into the table. """ self.reset_per_log_file_data() if (log_file_full_path == ""): pass elif (os.path.isfile(log_file_full_path)): with open(log_file_full_path, "r") as log_file_handle: log_file_content_lines = log_file_handle.read().splitlines() pattern = re.compile(self.log_trace_regex_pattern) self.table_data = [] most_recent_valid_table_entry = [] for line in log_file_content_lines: m = pattern.match(line) if(m is not None): most_recent_valid_table_entry = [group.strip() for group in m.groups()] self.table_data.append(list(most_recent_valid_table_entry)) else: if(self.user_data_column_zero_based != -1): temp_list = list(most_recent_valid_table_entry) temp_list[self.user_data_column_zero_based] = line self.table_data.append(temp_list) m = re.search(self.log_trace_regex_pattern, log_file_content_lines[1]) self.header = [group_name for group_name in sorted(m.groupdict().keys(), key=lambda k: m.start(k))] self.table_model = MyTableModel(self.table_data, self.header, self.table_conditional_formatting_config, self) logging.info("Headers: %s", self.header) logging.info("%s has %d lines", self.log_file_full_path, len(self.table_data)) self.proxy_model = MySortFilterProxyModel(self) self.proxy_model.setSourceModel(self.table_model) self.user_interface.tblLogData.setModel(self.proxy_model) if(len(self.per_column_filter_out_set_list) == 0): self.per_column_filter_out_set_list = [set() for column in range(len(self.table_data[0]))] if(len(self.per_column_filter_in_set_list) == 0): self.per_column_filter_in_set_list = [set() for column in range(len(self.table_data[0]))] self.extract_column_dictionaries(self.header, self.table_data) self.load_graphs(self.graph_configs, self.table_data) self.setWindowTitle("Siraj | {}".format(log_file_full_path)) self.select_cell_by_row_and_column(0, self.user_data_column_zero_based) else: self.display_message_box( "File not Found!", "File <b>`{}`</b> was not found. You can either: <br><br>1. Open a log file via the File menu. Or<br>2. Drag a log file from the system and drop it into the application".format(log_file_full_path), QMessageBox.Critical) def extract_column_dictionaries(self, header_vector_list, data_matrix_list): """ This function extracts a dictionary of dictionaries The extracted is a dictionary of columns where key is the column name, and the data is another dictionary. The inner dictionary has a key equal to a specific cell value of the current column, and the value is a list of row number where this value appeared in. This will be used to provide quick navigation through the log. """ column_count = len(header_vector_list) self.columns_dict = {} for column, column_name in enumerate(header_vector_list): self.columns_dict[column] = {} for row, log in enumerate(data_matrix_list): for column, field in enumerate(log): if(log[column] not in self.columns_dict[column]): self.columns_dict[column][log[column]] = [] self.columns_dict[column][log[column]].append(row) def cell_left_clicked(self, index): """ Handles the event of clicking on a table cell. If the clicked column was the the column that contain the source file:line information from the log, the function also populate the the EditView with the source file contents with a marker highlighting the line. This is only done if the source view is visible. """ index = self.proxy_model.mapToSource(index) if(self.is_source_visible): logging.info("cell[%d][%d] = %s", index.row(), index.column(), index.data()) row = index.row() file_matcher = re.search(self.file_column_pattern, self.table_data[row][self.file_column]) line_matcher = re.search(self.line_column_pattern, self.table_data[row][self.line_column]) if((file_matcher is not None) and (line_matcher is not None)): file = file_matcher.group(1) line = line_matcher.group(1) full_path = "{}{}".format(self.root_source_path_prefix, file.strip()) self.load_source_file(full_path, line) self.user_interface.tblLogData.setFocus() self.update_status_bar() self.update_graph_markers() def load_source_file(self, file, line): code = open(file).read() lexer = get_lexer_for_filename(file) formatter = HtmlFormatter( linenos = True, full = True, style = self.syntax_highlighting_style, hl_lines = [line]) result = highlight(code, lexer, formatter) self.user_interface.txtSourceFile.setHtml(result) text_block = self.user_interface.txtSourceFile.document().findBlockByLineNumber(int(line)) text_cursor = self.user_interface.txtSourceFile.textCursor() text_cursor.setPosition(text_block.position()) self.user_interface.txtSourceFile.setTextCursor(text_cursor) self.user_interface.txtSourceFile.ensureCursorVisible() def get_selected_indexes(self): """ Returns a list of the currently selected indexes mapped to the source numbering. mapToSource is needed to retrive the actual row number regardless of whether filtering is applied or not. """ return [self.proxy_model.mapToSource(index) for index in self.user_interface.tblLogData.selectedIndexes()] def update_status_bar(self): """ Updates the status bar with relevant information """ selected_indexes = self.get_selected_indexes() if(len(selected_indexes) == 1): selected_cell_index = selected_indexes[0] number_of_occurances = len(self.columns_dict[selected_cell_index.column()][selected_cell_index.data()]) self.user_interface.statusbar.showMessage( '["{}"] occurred {} time(s) ~ {}%'.format( selected_cell_index.data(), number_of_occurances, number_of_occurances * 100 // len(self.table_data))) elif(len(selected_indexes) == 2): row_1 = selected_indexes[0].row() row_2 = selected_indexes[1].row() time_stamp1 = float(self.table_data[row_1][self.time_stamp_column]) time_stamp2 = float(self.table_data[row_2][self.time_stamp_column]) self.user_interface.statusbar.showMessage("Time difference = {}".format(abs(time_stamp2 - time_stamp1))) else: self.user_interface.statusbar.showMessage("") def cell_right_clicked(self, point): """ Handle the event of right-clicking on a table cell. This function is responsible for showing the context menu for the user to choose from. """ index = self.proxy_model.mapToSource( self.user_interface.tblLogData.indexAt(point)) logging.debug("Cell[%d, %d] was right-clicked. Contents = %s", index.row(), index.column(), index.data()) self.right_clicked_cell_index = index self.populate_unhide_context_menu(index.column()) self.prepare_clipboard_text() self.menuFilter.popup(QCursor.pos()) def populate_unhide_context_menu(self, column): self.unhide_menu.clear() if(self.is_filtering_mode_out): filtered_out_set = self.per_column_filter_out_set_list[column] else: filtered_out_set = set(self.columns_dict[column].keys()) - self.per_column_filter_in_set_list[column] if(len(filtered_out_set) > 0): self.unhide_menu.setEnabled(True) for filtered_string in filtered_out_set: temp_action = QAction(filtered_string, self.unhide_menu) temp_action.triggered.connect(functools.partial(self.unhide_selected_rows_only_based_on_column, self.right_clicked_cell_index.column(), filtered_string)) self.unhide_menu.addAction(temp_action) else: self.unhide_menu.setEnabled(False) def cell_double_clicked(self, index): """ Handles the event of double-clicking on a table cell. If the double clicked cell was at the column of file:line, the function triggers external text editor (currently this is gedit on Linux) and make it point on the corresponding line. """ index = self.proxy_model.mapToSource(index) logging.info("cell[%d][%d] = %s", index.row(), index.column(), index.data()) row = index.row() file_matcher = re.search(self.file_column_pattern, self.table_data[row][self.file_column]) line_matcher = re.search(self.line_column_pattern, self.table_data[row][self.line_column]) if((file_matcher is not None) and (line_matcher is not None)): file = file_matcher.group(1) line = line_matcher.group(1) full_path = "{}{}".format(self.root_source_path_prefix, file.strip()) logging.info("Using external editor (gedit) to open %s at line %s", file, line) editor = self.external_editor_configs["editor"] editor_command_format = self.external_editor_configs["editor_command_format"] editor_command = editor_command_format.format( editor_executable=editor, line_number=line, file_name=full_path) call(editor_command, shell=True) self.user_interface.tblLogData.setFocus() self.update_status_bar() def search_box_key_pressed(self, q_key_event): key = q_key_event.key() if (key in [Qt.Key_Enter, Qt.Key_Return]): if(Qt.ShiftModifier == (int(q_key_event.modifiers()) & (Qt.ShiftModifier))): self.select_search_match(False) else: self.select_search_match(True) else: QLineEdit.keyPressEvent(self.ledSearchBox, q_key_event) def cell_key_pressed(self, q_key_event): """ Handles the event of pressing a keyboard key while on the table. """ logging.warning("A key was pressed!!!") key = q_key_event.key() logging.info("Key = {}".format(key)) if(Qt.ControlModifier == (int(q_key_event.modifiers()) & (Qt.ControlModifier))): if key == Qt.Key_Delete: logging.info("Delete key pressed while in the table. Clear all filters") self.clear_all_filters() elif key == Qt.Key_H: self.hide_rows_based_on_selected_cells() elif key == Qt.Key_O: self.show_rows_based_on_selected_cells() elif key == Qt.Key_Up: # Jump to previous match selected_indexes = self.get_selected_indexes() if(len(selected_indexes) == 1): self.go_to_prev_match(selected_indexes[0]) elif key == Qt.Key_Down: # Jump to next match selected_indexes = self.get_selected_indexes() if(len(selected_indexes) == 1): self.go_to_next_match(selected_indexes[0]) elif key == Qt.Key_PageUp: selected_indexes = self.get_selected_indexes() if(len(selected_indexes) == 1): prev_bookmark_index = self.table_model.getPrevBookmarkIndex(selected_indexes[0]) if(prev_bookmark_index is not None): self.select_cell_by_index(prev_bookmark_index) elif key == Qt.Key_PageDown: selected_indexes = self.get_selected_indexes() if(len(selected_indexes) == 1): next_bookmark_index = self.table_model.getNextBookmarkIndex(selected_indexes[0]) if(next_bookmark_index is not None): self.select_cell_by_index(next_bookmark_index) elif key == Qt.Key_C: selected_indexes = self.get_selected_indexes() self.prepare_clipboard_text() elif key == Qt.Key_B: if(Qt.ShiftModifier == (int(q_key_event.modifiers()) & (Qt.ShiftModifier))): self.table_model.clearAllBookmarks() else: selected_indexes = self.get_selected_indexes() self.table_model.toggleBookmarks(selected_indexes) elif key == Qt.Key_Left: self.select_search_match(is_forward=False) elif key == Qt.Key_Right: self.select_search_match(is_forward=True) elif key == Qt.Key_Home: self.select_cell_by_row_and_column(0, 0); elif key == Qt.Key_End: self.select_cell_by_row_and_column(self.table_model.rowCount(None) - 1, 0); elif key == Qt.Key_F5: self.load_log_file(self.log_file_full_path) else: QTableView.keyPressEvent(self.user_interface.tblLogData, q_key_event) self.update_graph_markers() def update_graph_markers(self): selected_indexes = self.get_selected_indexes() if (len(selected_indexes) == 1): for marker in self.graph_marker_list: marker.setPos(selected_indexes[0].row()) def prepare_clipboard_text(self): """ Copy the cell content to the clipboard if a single cell is selected. Or Copy the whole rows if cells from multiple rows are selected. """ selected_indexes = self.get_selected_indexes() if(len(selected_indexes) == 0): clipboard_text = "" elif(len(selected_indexes) == 1): clipboard_text = self.user_interface.tblLogData.currentIndex().data() else: unique_rows_set = set([index.row() for index in sorted(selected_indexes)]) row_text_list = [str(row) + "," + ",".join([self.proxy_model.index(row, column, QModelIndex()).data() for column in range(self.proxy_model.columnCount())]) for row in sorted(unique_rows_set)] clipboard_text = "\n".join(row_text_list) self.clipboard.setText(clipboard_text) def get_index_by_row_and_column(self, row, column): """ Get the table index value by the given row and column """ index = self.table_model.createIndex(row, column) index = self.proxy_model.mapFromSource(index) return index def select_cell_by_row_and_column(self, row, column): """ Select the cell identified by the given row and column and scroll the table view to make that cell in the middle of the visible part of the table. """ self.user_interface.tblLogData.clearSelection() index = self.get_index_by_row_and_column(row, column) self.user_interface.tblLogData.setCurrentIndex(index) self.user_interface.tblLogData.scrollTo(index, hint = QAbstractItemView.PositionAtCenter) self.user_interface.tblLogData.setFocus() self.update_status_bar() def select_cell_by_index(self, index): """ Select a cell at the given index. """ self.user_interface.tblLogData.clearSelection() index = self.proxy_model.mapFromSource(index) self.user_interface.tblLogData.setCurrentIndex(index) self.user_interface.tblLogData.scrollTo(index, hint = QAbstractItemView.PositionAtCenter) self.user_interface.tblLogData.setFocus() self.update_status_bar() def go_to_prev_match(self, selected_cell): """ Go to the prev cell that matches the currently selected cell in the same column """ matches_list = self.columns_dict[selected_cell.column()][selected_cell.data()] index = matches_list.index(selected_cell.row()) if(index > 0): new_row = matches_list[index - 1] self.select_cell_by_row_and_column(new_row, selected_cell.column()) def go_to_next_match(self, selected_cell): """ Go to the prev cell that matches the currently selected cell in the same column """ matches_list = self.columns_dict[selected_cell.column()][selected_cell.data()] index = matches_list.index(selected_cell.row()) if(index < (len(matches_list) - 1)): new_row = matches_list[index + 1] self.select_cell_by_row_and_column(new_row, selected_cell.column()) def get_top_left_selected_row_index(self): """ This function return the top-left selected index from the selected list. It's used for example to anchor the table view around the top-left selected cell following any change in the visible cells due to filtering """ top_left_index = None selected_indexes = self.get_selected_indexes() if(len(selected_indexes) > 0): selected_indexes = self.get_selected_indexes() top_left_index = selected_indexes[0] row = top_left_index.row() column = top_left_index.column() for index in selected_indexes[1:]: if((index.row() < row) and (index.column() < column)): row = index.row() column = index.column() top_left_index = index return top_left_index def clear_all_filters(self): """ Clears all the current filter and return the table to its initial view. """ top_selected_index = self.get_top_left_selected_row_index() self.per_column_filter_out_set_list = [set() for column in range(len(self.table_data[0]))] self.per_column_filter_in_set_list = [set() for column in range(len(self.table_data[0]))] self.apply_filter(is_filtering_mode_out = True) if(top_selected_index != None): self.select_cell_by_index(top_selected_index) self.update_status_bar() def hide_rows_based_on_selected_cells(self): """ Hides the selected rows and any other rows with matching data. """ selected_indexes = self.get_selected_indexes() for index in selected_indexes: column = index.column() self.per_column_filter_out_set_list[column].add(index.data()) new_selected_row = None min_selected_row = selected_indexes[0].row() max_selected_row = selected_indexes[-1].row() if(min_selected_row != 0): new_selected_row = min_selected_row - 1 elif(max_selected_row != self.table_model.columnCount(None)): new_selected_row = max_selected_row + 1 self.apply_filter(is_filtering_mode_out=True) self.select_cell_by_row_and_column(new_selected_row, selected_indexes[0].column()) self.update_status_bar() def show_rows_based_on_selected_cells(self): """ Shows the selected rows and any other rows with matching data only. """ selected_indexes = self.get_selected_indexes() self.per_column_filter_in_set_list = [set() for column in range(len(self.table_data[0]))] for index in selected_indexes: column = index.column() self.per_column_filter_in_set_list[column].add(index.data()) self.apply_filter(is_filtering_mode_out=False) self.update_status_bar() def unhide_selected_rows_only_based_on_column(self, filter_column, filtered_out_string): """ Unhides the selected rows and any other rows with matching data. The filtering works on one column only. """ top_selected_index = self.get_top_left_selected_row_index() if(self.is_filtering_mode_out): self.per_column_filter_out_set_list[filter_column].remove(filtered_out_string) else: self.per_column_filter_in_set_list[filter_column].add(filtered_out_string) logging.debug("Unhiding: %s", filtered_out_string) self.apply_filter(self.is_filtering_mode_out) if(top_selected_index != None): self.select_cell_by_index(top_selected_index) self.update_status_bar() def apply_filter(self, is_filtering_mode_out): """ Applies the filter based on the given mode. """ self.is_filtering_mode_out = is_filtering_mode_out if(is_filtering_mode_out): self.proxy_model.setFilterOutList(self.per_column_filter_out_set_list) else: self.proxy_model.setFilterInList(self.per_column_filter_in_set_list) # This is just to trigger the proxy model to apply the filter self.proxy_model.setFilterKeyColumn(0) def dragEnterEvent(self, q_drag_enter_event): if(q_drag_enter_event.mimeData().hasFormat("text/uri-list")): q_drag_enter_event.acceptProposedAction(); def dropEvent(self, q_drop_event): url_list = q_drop_event.mimeData().urls() if(len(url_list) == 0): return log_file_list = [url.toLocalFile() for url in url_list] self.log_file_full_path = log_file_list[0] self.load_log_file(self.log_file_full_path) def closeEvent(self, event): app = QApplication([]) # app.closeAllWindows() app.deleteLater() app.closeAllWindows()
class MapToolIndentifyItems(QgsMapToolIdentify): def __init__(self, plugin): super(MapToolIndentifyItems, self).__init__(plugin.mapCanvas()) mToolName = self.tr('Identify feature') self._menu = QMenu(plugin.mapCanvas()) self._menu.hovered.connect(self._highlight) self._actions = [] self._highlights = [] self._plugin = plugin self._vertexMarker = QgsVertexMarker(plugin.mapCanvas()) self._vertexMarker.setIconType(QgsVertexMarker.ICON_CROSS) def collection(self): return self._plugin.project().collection('plan') def deactivate(self): self._reset() super(MapToolIndentifyItems, self).deactivate() def canvasPressEvent(self, e): self._reset() def canvasReleaseEvent(self, e): self._reset() if e.button() != Qt.LeftButton: return mapPoint = self.toMapCoordinates(e.pos()) self._vertexMarker.setCenter(mapPoint) layers = [self.collection().layer('points'), self.collection().layer('lines'), self.collection().layer('polygons')] results = self.identify(e.x(), e.y(), layers, QgsMapToolIdentify.TopDownAll) if (len(results) < 1): return # Build the set of unique items identified items = set() for result in results: feature = result.mFeature siteCode = feature.attribute('site') classCode = feature.attribute('class') itemId = feature.attribute('id') items.add(Item(siteCode, classCode, itemId)) action = QAction('Plan Items', self._menu) action.setData('top') self._menu.addAction(action) site = '' for item in sorted(items): if item.siteCode() != site: site = item.siteCode() self._menu.addSeparator() action = QAction('Site ' + site + ':', self._menu) action.setData('top') self._menu.addAction(action) action = IdentifyItemAction(item, self._plugin, self._menu) action.setData('top') action.zoomToItemSelected.connect(self._zoom) action.panToItemSelected.connect(self._pan) action.filterItemSelected.connect(self._filterItem) action.excludeFilterItemSelected.connect(self._excludeFilterItem) action.highlightItemSelected.connect(self._highlightItem) action.addHighlightItemSelected.connect(self._addHighlightItem) action.editItemSelected.connect(self._editInBuffers) action.deleteItemSelected.connect(self._delete) action.openDrawingsSelected.connect(self._openDrawings) self._actions.append(action) self._menu.addAction(action) self._menu.addSeparator() action = ClipboardAction('Map: ', mapPoint.toString(3), self._menu) action.setData('top') self._menu.addAction(action) if self._plugin.grid().mapTransformer is not None: localPoint = self._plugin.grid().mapTransformer.map(mapPoint) self._menu.addAction(ClipboardAction('Local: ', localPoint.toString(3), self._menu)) menuPos = QPoint(e.globalX() + 100, e.globalY() - 50) selected = self._menu.exec_(menuPos) self._reset(resetVertex=False) def keyPressEvent(self, e): if (e.key() == Qt.Key_Escape): self._reset() self.canvas().unsetMapTool(self) def _reset(self, resetVertex=True): self._menu.clear() del self._highlights[:] del self._actions[:] if resetVertex and self._vertexMarker: self._vertexMarker.setCenter(QgsPointV2()) def _highlight(self, item): if item.data() == 'top': del self._highlights[:] else: return if not isinstance(item, IdentifyItemAction): return request = item.item.featureRequest() for feature in self.collection().layer('polygons').getFeatures(request): self._addHighlight(self._plugin.mapCanvas(), feature.geometry(), self.collection().layer('polygons')) for feature in self.collection().layer('lines').getFeatures(request): self._addHighlight(self._plugin.mapCanvas(), feature.geometry(), self.collection().layer('lines')) for feature in self.collection().layer('points').getFeatures(request): self._addHighlight(self._plugin.mapCanvas(), feature.geometry(), self.collection().layer('points')) def _addHighlight(self, canvas, geometry, layer): hl = QgsHighlight(canvas, geometry, layer) color = QColor(QSettings().value('/Map/highlight/color', QGis.DEFAULT_HIGHLIGHT_COLOR.name(), str)) alpha = QSettings().value('/Map/highlight/colorAlpha', QGis.DEFAULT_HIGHLIGHT_COLOR.alpha(), int) buff = QSettings().value('/Map/highlight/buffer', QGis.DEFAULT_HIGHLIGHT_BUFFER_MM, float) minWidth = QSettings().value('/Map/highlight/minWidth', QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM, float) hl.setColor(color) color.setAlpha(alpha) hl.setFillColor(color) hl.setBuffer(buff) hl.setMinWidth(minWidth) self._highlights.append(hl) def _zoom(self, item): self.project().zoomToItem(item, highlight=True) def _pan(self, item): self.project().moveToItem(item, highlight=True) def _filterItem(self, item): self.project().filterItem(item) def _excludeFilterItem(self, item): self.project().excludeFilterItem(item) def _highlightItem(self, item): self.project().highlightItem(item) def _addHighlightItem(self, item): self.project().addHighlightItem(item) def _openDrawings(self, item): self.project().loadDrawing(item) def _editInBuffers(self, item): self.project().editInBuffers(item) def _delete(self, item): self.project().deleteItem(item)
class OneColumnTree(QTreeWidget): """One-column tree widget with context menu, ...""" def __init__(self, parent): QTreeWidget.__init__(self, parent) self.setItemsExpandable(True) self.setColumnCount(1) self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*,int)'), self.activated) self.connect(self, SIGNAL('itemClicked(QTreeWidgetItem*,int)'), self.clicked) # Setup context menu self.menu = QMenu(self) self.collapse_all_action = None self.collapse_selection_action = None self.expand_all_action = None self.expand_selection_action = None self.common_actions = self.setup_common_actions() self.__expanded_state = None self.connect(self, SIGNAL('itemSelectionChanged()'), self.item_selection_changed) self.item_selection_changed() def activated(self, item): """Double-click event""" raise NotImplementedError def clicked(self, item): pass def set_title(self, title): self.setHeaderLabels([title]) def setup_common_actions(self): """Setup context menu common actions""" self.collapse_all_action = create_action(self, text=_('Collapse all'), icon=get_icon('collapse.png'), triggered=self.collapseAll) self.expand_all_action = create_action(self, text=_('Expand all'), icon=get_icon('expand.png'), triggered=self.expandAll) self.restore_action = create_action( self, text=_('Restore'), tip=_('Restore original tree layout'), icon=get_icon('restore.png'), triggered=self.restore) self.collapse_selection_action = create_action( self, text=_('Collapse selection'), icon=get_icon('collapse_selection.png'), triggered=self.collapse_selection) self.expand_selection_action = create_action( self, text=_('Expand selection'), icon=get_icon('expand_selection.png'), triggered=self.expand_selection) return [ self.collapse_all_action, self.expand_all_action, self.restore_action, None, self.collapse_selection_action, self.expand_selection_action ] def update_menu(self): self.menu.clear() items = self.selectedItems() actions = self.get_actions_from_items(items) if actions: actions.append(None) actions += self.common_actions add_actions(self.menu, actions) def get_actions_from_items(self, items): # Right here: add other actions if necessary # (reimplement this method) return [] def restore(self): self.collapseAll() for item in self.get_top_level_items(): self.expandItem(item) def is_item_expandable(self, item): """To be reimplemented in child class See example in project explorer widget""" return True def __expand_item(self, item): if self.is_item_expandable(item): self.expandItem(item) for index in range(item.childCount()): child = item.child(index) self.__expand_item(child) def expand_selection(self): items = self.selectedItems() if not items: items = self.get_top_level_items() for item in items: self.__expand_item(item) if items: self.scrollToItem(items[0]) def __collapse_item(self, item): self.collapseItem(item) for index in range(item.childCount()): child = item.child(index) self.__collapse_item(child) def collapse_selection(self): items = self.selectedItems() if not items: items = self.get_top_level_items() for item in items: self.__collapse_item(item) if items: self.scrollToItem(items[0]) def item_selection_changed(self): """Item selection has changed""" is_selection = len(self.selectedItems()) > 0 self.expand_selection_action.setEnabled(is_selection) self.collapse_selection_action.setEnabled(is_selection) def get_top_level_items(self): """Iterate over top level items""" return [ self.topLevelItem(_i) for _i in range(self.topLevelItemCount()) ] def get_items(self): """Return items (excluding top level items)""" itemlist = [] def add_to_itemlist(item): for index in range(item.childCount()): citem = item.child(index) itemlist.append(citem) add_to_itemlist(citem) for tlitem in self.get_top_level_items(): add_to_itemlist(tlitem) return itemlist def get_scrollbar_position(self): return (self.horizontalScrollBar().value(), self.verticalScrollBar().value()) def set_scrollbar_position(self, position): hor, ver = position self.horizontalScrollBar().setValue(hor) self.verticalScrollBar().setValue(ver) def get_expanded_state(self): self.save_expanded_state() return self.__expanded_state def set_expanded_state(self, state): self.__expanded_state = state self.restore_expanded_state() def save_expanded_state(self): """Save all items expanded state""" self.__expanded_state = {} def add_to_state(item): user_text = get_item_user_text(item) self.__expanded_state[hash(user_text)] = item.isExpanded() def browse_children(item): add_to_state(item) for index in range(item.childCount()): citem = item.child(index) user_text = get_item_user_text(citem) self.__expanded_state[hash(user_text)] = citem.isExpanded() browse_children(citem) for tlitem in self.get_top_level_items(): browse_children(tlitem) def restore_expanded_state(self): """Restore all items expanded state""" if self.__expanded_state is None: return for item in self.get_items() + self.get_top_level_items(): user_text = get_item_user_text(item) is_expanded = self.__expanded_state.get(hash(user_text)) if is_expanded is not None: item.setExpanded(is_expanded) def sort_top_level_items(self, key): """Sorting tree wrt top level items""" self.save_expanded_state() items = sorted([ self.takeTopLevelItem(0) for index in range(self.topLevelItemCount()) ], key=key) for index, item in enumerate(items): self.insertTopLevelItem(index, item) self.restore_expanded_state() def contextMenuEvent(self, event): """Override Qt method""" self.update_menu() self.menu.popup(event.globalPos())
class QuickMapServices: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale self.translator = QTranslator() self.locale = Locale.get_locale() locale_path = os.path.join( self.plugin_dir, 'i18n', 'QuickMapServices_{}.qm'.format(self.locale)) if os.path.exists(locale_path): self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.custom_translator = CustomTranslator() QCoreApplication.installTranslator(self.custom_translator) # Create the dialog (after translation) and keep reference self.info_dlg = AboutDialog() # Check Contrib and User dirs try: ExtraSources.check_extra_dirs() except: error_message = self.tr('Extra dirs for %s can\'t be created: %s %s') % (PluginSettings.product_name(), sys.exc_type, sys.exc_value) self.iface.messageBar().pushMessage(self.tr('Error'), error_message, level=QgsMessageBar.CRITICAL) # Declare instance attributes self.service_actions = [] self.service_layers = [] # TODO: id and smart remove self._scales_list = None # noinspection PyMethodMayBeStatic def tr(self, message): # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('QuickMapServices', message) def initGui(self): #import pydevd #pydevd.settrace('localhost', port=9921, stdoutToServer=True, stderrToServer=True, suspend=False) # Register plugin layer type self.tileLayerType = TileLayerType(self) QgsPluginLayerRegistry.instance().addPluginLayerType(self.tileLayerType) # Create menu icon_path = self.plugin_dir + '/icons/mActionAddLayer.png' self.menu = QMenu(self.tr(u'QuickMapServices')) self.menu.setIcon(QIcon(icon_path)) self.build_menu_tree() # add to QGIS menu/toolbars self.append_menu_buttons() def _load_scales_list(self): scales_filename = os.path.join(self.plugin_dir, 'scales.xml') scales_list = [] # TODO: remake when fix: http://hub.qgis.org/issues/11915 # QgsScaleUtils.loadScaleList(scales_filename, scales_list, importer_message) xml_root = ET.parse(scales_filename).getroot() for scale_el in xml_root.findall('scale'): scales_list.append(scale_el.get('value')) return scales_list @property def scales_list(self): if not self._scales_list: self._scales_list = self._load_scales_list() return self._scales_list def set_nearest_scale(self): #get current scale curr_scale = self.iface.mapCanvas().scale() #find nearest nearest_scale = sys.maxint for scale_str in self.scales_list: scale = scale_str.split(':')[1] scale_int = int(scale) if abs(scale_int-curr_scale) < abs(nearest_scale - curr_scale): nearest_scale = scale_int #set new scale if nearest_scale != sys.maxint: self.iface.mapCanvas().zoomScale(nearest_scale) def set_tms_scales(self): res = QMessageBox.question( self.iface.mainWindow(), self.tr('QuickMapServices'), self.tr('Set SlippyMap scales for current project? \nThe previous settings will be overwritten!'), QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.Yes: # set scales QgsProject.instance().writeEntry('Scales', '/ScalesList', self.scales_list) # activate QgsProject.instance().writeEntry('Scales', '/useProjectScales', True) # update in main window # ???? no way to update: http://hub.qgis.org/issues/11917 def insert_layer(self): #TODO: need factory! action = self.menu.sender() ds = action.data() if ds.type == KNOWN_DRIVERS.TMS: service_info = TileServiceInfo(self.tr(ds.alias), ds.copyright_text, ds.tms_url) service_info.zmin = ds.tms_zmin or service_info.zmin service_info.zmax = ds.tms_zmax or service_info.zmax if ds.tms_y_origin_top is not None: service_info.yOriginTop = ds.tms_y_origin_top service_info.epsg_crs_id = ds.tms_epsg_crs_id service_info.postgis_crs_id = ds.tms_postgis_crs_id service_info.custom_proj = ds.tms_custom_proj layer = TileLayer(self, service_info, False) if ds.type == KNOWN_DRIVERS.GDAL: layer = QgsRasterLayer(ds.gdal_source_file, self.tr(ds.alias)) if ds.type == KNOWN_DRIVERS.WMS: qgis_wms_uri = u'' if ds.wms_params: qgis_wms_uri += ds.wms_params if ds.wms_layers: layers = ds.wms_layers.split(',') if layers: if ds.wms_turn_over: layers.reverse() qgis_wms_uri += '&layers='+'&layers='.join(layers)+'&styles='*len(layers) qgis_wms_uri += '&url=' + ds.wms_url layer = QgsRasterLayer(qgis_wms_uri, self.tr(ds.alias), KNOWN_DRIVERS.WMS.lower()) if not layer.isValid(): error_message = self.tr('Layer %s can\'t be added to the map!') % ds.alias self.iface.messageBar().pushMessage(self.tr('Error'), error_message, level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage(error_message, level=QgsMessageLog.CRITICAL) else: # Set attribs layer.setAttribution(ds.copyright_text) layer.setAttributionUrl(ds.copyright_link) # Insert to bottom QgsMapLayerRegistry.instance().addMapLayer(layer, False) toc_root = QgsProject.instance().layerTreeRoot() toc_root.insertLayer(len(toc_root.children()), layer) # Save link self.service_layers.append(layer) # Set OTF CRS Transform for map if PluginSettings.enable_otf_3857() and ds.type == KNOWN_DRIVERS.TMS: self.iface.mapCanvas().setCrsTransformEnabled(True) self.iface.mapCanvas().setDestinationCrs(TileLayer.CRS_3857) def unload(self): # remove menu/ self.remove_menu_buttons() # clean vars self.menu = None self.toolbutton = None self.service_actions = None self.ds_list = None self.groups_list = None self.service_layers = None # Unregister plugin layer type QgsPluginLayerRegistry.instance().removePluginLayerType(TileLayer.LAYER_TYPE) def build_menu_tree(self): # Main Menu self.menu.clear() self.groups_list = GroupsList() self.ds_list = DataSourcesList() data_sources = self.ds_list.data_sources.values() data_sources.sort(key=lambda x: x.alias or x.id) ds_hide_list = PluginSettings.get_hide_ds_id_list() for ds in data_sources: if ds.id in ds_hide_list: continue ds.action.triggered.connect(self.insert_layer) gr_menu = self.groups_list.get_group_menu(ds.group) gr_menu.addAction(ds.action) if gr_menu not in self.menu.children(): self.menu.addMenu(gr_menu) # Scales, Settings and About actions self.menu.addSeparator() icon_set_nearest_scale_path = self.plugin_dir + '/icons/mActionSettings.png' # TODO change icon set_nearest_scale_act = QAction(QIcon(icon_set_nearest_scale_path), self.tr('Set proper scale'), self.iface.mainWindow()) set_nearest_scale_act.triggered.connect(self.set_nearest_scale) self.menu.addAction(set_nearest_scale_act) # TODO: uncomment after fix self.service_actions.append(set_nearest_scale_act) icon_scales_path = self.plugin_dir + '/icons/mActionSettings.png' # TODO change icon scales_act = QAction(QIcon(icon_scales_path), self.tr('Set SlippyMap scales'), self.iface.mainWindow()) scales_act.triggered.connect(self.set_tms_scales) #self.menu.addAction(scales_act) # TODO: uncomment after fix self.service_actions.append(scales_act) icon_settings_path = self.plugin_dir + '/icons/mActionSettings.png' settings_act = QAction(QIcon(icon_settings_path), self.tr('Settings'), self.iface.mainWindow()) self.service_actions.append(settings_act) settings_act.triggered.connect(self.show_settings_dialog) self.menu.addAction(settings_act) icon_about_path = self.plugin_dir + '/icons/mActionAbout.png' info_act = QAction(QIcon(icon_about_path), self.tr('About'), self.iface.mainWindow()) self.service_actions.append(info_act) info_act.triggered.connect(self.info_dlg.show) self.menu.addAction(info_act) def remove_menu_buttons(self): """ Remove menus/buttons from all toolbars and main submenu :return: None """ # remove menu if self.menu: self.iface.webMenu().removeAction(self.menu.menuAction()) self.iface.addLayerMenu().removeAction(self.menu.menuAction()) # remove toolbar button if self.tb_action: self.iface.webToolBar().removeAction(self.tb_action) self.iface.layerToolBar().removeAction(self.tb_action) def append_menu_buttons(self): """ Append menus and buttons to appropriate toolbar :return: """ # add to QGIS menu if PluginSettings.move_to_layers_menu(): self.iface.addLayerMenu().addMenu(self.menu) else: # need workaround for WebMenu _temp_act = QAction('temp', self.iface.mainWindow()) self.iface.addPluginToWebMenu("_tmp", _temp_act) self.iface.webMenu().addMenu(self.menu) self.iface.removePluginWebMenu("_tmp", _temp_act) # add to QGIS toolbar toolbutton = QToolButton() toolbutton.setPopupMode(QToolButton.InstantPopup) toolbutton.setMenu(self.menu) toolbutton.setIcon(self.menu.icon()) toolbutton.setText(self.menu.title()) toolbutton.setToolTip(self.menu.title()) if PluginSettings.move_to_layers_menu(): self.tb_action = self.iface.layerToolBar().addWidget(toolbutton) else: self.tb_action = self.iface.webToolBar().addWidget(toolbutton) def show_settings_dialog(self): settings_dlg = SettingsDialog() settings_dlg.exec_() # apply settings # self.remove_menu_buttons() self.build_menu_tree()
class Window(QMainWindow, Ui_Window): loginRequested = QtCore.pyqtSignal(str, str) selectMailboxRequested = QtCore.pyqtSignal(str) uidListRequested = QtCore.pyqtSignal(str, str) newMailsRequested = QtCore.pyqtSignal(int) bodystructureRequested = QtCore.pyqtSignal(int) mailTextRequested = QtCore.pyqtSignal(int, str, str) deleteRequested = QtCore.pyqtSignal(list) saveAttachmentRequested = QtCore.pyqtSignal(int, str, str, unicode) def __init__(self): QMainWindow.__init__(self) QIcon.setThemeName('Adwaita') self.setupUi(self) self.settings = QtCore.QSettings(1, 0, "daaq-mail","daaq", self) self.emails = [unicode(x) for x in self.settings.value('Emails', []).toStringList()] self.types = [unicode(x) for x in self.settings.value('Types', []).toStringList()] self.initUi() self.resize(1024, 714) self.show() # create imap client self.thread = QtCore.QThread(self) self.imapClient = GmailImap() self.thread.finished.connect(self.imapClient.deleteLater) self.imapClient.moveToThread(self.thread) self.imapClient.loggedIn.connect( self.onLogin) self.imapClient.mailboxListLoaded.connect( self.setMailboxes) self.imapClient.mailboxSelected.connect( self.onMailboxSelect) self.imapClient.uidsListed.connect( self.removeDeleted) self.imapClient.insertMailRequested.connect(self.insertNewMail) self.imapClient.bodystructureLoaded.connect(self.setAttachments) self.imapClient.mailTextLoaded.connect( self.setMailText) self.loginRequested.connect( self.imapClient.login) self.selectMailboxRequested.connect( self.imapClient.selectMailbox) self.uidListRequested.connect( self.imapClient.listUids) self.newMailsRequested.connect( self.imapClient.getNewMails) self.bodystructureRequested.connect( self.imapClient.loadBodystructure) self.mailTextRequested.connect( self.imapClient.loadMailText) self.saveAttachmentRequested.connect( self.imapClient.saveAttachment) self.deleteRequested.connect( self.imapClient.deleteMails) self.thread.start() # init variables self.email_id = '' self.passwd = '' self.mailbox = '' self.total_mails = 0 QtCore.QTimer.singleShot(30, self.setupClient) def initUi(self): self.toolMenu = QMenu('Menu', self) self.accountsMenu = QMenu('Select Account', self) self.accountsMenu.triggered.connect(self.accountSelected) self.refreshAccountsMenu() self.toolMenu.addMenu(self.accountsMenu) self.toolMenu.addAction('Manage Accounts', self.manageAccounts) self.menuBtn = QToolButton(self) self.menuBtn.setIcon(QIcon(':/menu.png')) self.menuBtn.setPopupMode(QToolButton.InstantPopup) self.menuBtn.setMenu(self.toolMenu) self.toolBar.insertWidget(self.newMailAction, self.menuBtn) spacer = QWidget(self) spacer.setSizePolicy(1|2|4,1|4) self.toolBar.insertWidget(self.quitAction, spacer) # Create text browser self.textViewer = TextViewer(self) self.verticalLayout.insertWidget(0, self.textViewer, 1) # mailboxTable shows list of Mailboxes self.mailboxTable.setFixedWidth(150) self.mailboxTable.horizontalHeader().setResizeMode(0, QHeaderView.Stretch) self.mailboxTable.itemClicked.connect(self.onMailboxClick) # mailsTable is list of mails for a mailbox self.mailsTable.setFixedWidth(500) self.mailsTable.horizontalHeader().setResizeMode(0, QHeaderView.Stretch) self.mailsTable.cellClicked.connect(self.loadMail) # Attachments table self.tableWidget.horizontalHeader().setResizeMode(0, QHeaderView.Stretch) self.tableWidget.hide() self.tableWidget.cellClicked.connect(self.onAttachmentClick) # Insert Mail Info Frame self.mailInfoFrame = MailInfoFrame(self.widget_2) self.verticalLayout.insertWidget(0, self.mailInfoFrame) # Create actions self.replyAction.triggered.connect(self.replyMail) self.deleteAction.triggered.connect(self.deleteMail) self.printAction.triggered.connect(self.printMail) self.quitAction.triggered.connect(self.close) self.newMailAction.triggered.connect(self.newMail) def setupClient(self): """ Login to email server (IMAP) """ email_id = str(self.settings.value('EmailId', '').toString()) if email_id == '': QMessageBox.warning(self, 'No Account Selected !', 'Please Click on Menu and Select an account to login') return kr = Keyring() passwd = kr.getPassword(email_id) if not passwd : QMessageBox.critical(self, 'Not Found !', 'Password not found for the email %s'%email_id) return print 'login requested' self.loginRequested.emit(email_id, passwd) def onLogin(self, email_id, passwd): self.email_id = email_id self.passwd = passwd if not os.path.exists(ACNT_DIR + email_id + '/mails'): os.makedirs(str(ACNT_DIR + email_id + '/mails')) def setMailboxes(self, mailboxes): #print mailboxes row = 0 for mailbox in mailboxes: #print mailbox mailbox_name = mailbox.split('/')[-1] #print mailbox_name self.mailboxTable.insertRow(row) item = QTableWidgetItem(mailbox_name) item.mailbox_path = mailbox self.mailboxTable.setItem(row, 0, item) row += 1 def onMailboxClick(self, item): mailbox = item.mailbox_path if self.mailbox == mailbox : return # Already selected self.selectMailboxRequested.emit(mailbox) def onMailboxSelect(self, mailbox, total_mails): if self.mailbox != '' : self.saveMailInfo() self.mailbox = mailbox self.total_mails = total_mails self.clearMailViewer() self.mailsTable.clear() self.mailsTable.setRowCount(0) QtCore.QTimer.singleShot(30, self.loadCachedMails) def loadCachedMails(self): """ fetch list of mails (with headers) in a mailbox """ oldest_uid, latest_uid = None, None mailbox_file = ACNT_DIR + self.email_id + '/%s.xml'%self.mailbox[:].replace('/', '_') if os.path.exists(mailbox_file): # load cached mails doc = QDomDocument() doc_file = QtCore.QFile(mailbox_file) if doc_file.open(QtCore.QIODevice.ReadOnly): doc.setContent(doc_file) doc_file.close() mails = doc.firstChild().toElement() mail = mails.firstChild() latest_uid = mail.toElement().attribute('UID') i = 0 while not mail.isNull(): e = mail.toElement() self.mailsTable.insertRow(i) item = MailItem() item.setData(e.attribute('Sender'), e.attribute('Subject'), e.attribute('UID'), e.attribute('Message-ID'), e.attribute('Date'), e.attribute('Cached')) self.mailsTable.setCellWidget(i, 0, item) self.mailsTable.resizeRowsToContents() wait(30) mail = mail.nextSibling() i += 1 oldest_uid = mails.lastChild().toElement().attribute('UID') print 'uids', oldest_uid, latest_uid if latest_uid: self.uidListRequested.emit(oldest_uid, latest_uid) else: self.newMailsRequested.emit(0) def removeDeleted(self, uids, last_index): print 'removing deleted mails' deleted = 0 for i in range(self.mailsTable.rowCount()): item = self.mailsTable.cellWidget(i-deleted,0) if item.uid not in uids: if item.cached : # delete cached files msg_struct = ACNT_DIR + self.email_id + '/mails/' + item.msg_id[:].replace('/', '_') + '.xml' msg_file = ACNT_DIR + self.email_id + '/mails/' + item.msg_id[:].replace('/', '_') + '.txt' if os.path.exists(msg_struct) : os.remove(msg_struct) if os.path.exists(msg_file) : os.remove(msg_file) self.mailsTable.removeRow(i-deleted) deleted += 1 wait(30) self.newMailsRequested.emit(last_index) #print 'mails deleted' def insertNewMail(self, sender, subject, uid, msg_id, date, cached, seen): #print 'insert new mail', uid self.mailsTable.insertRow(0) item = MailItem() item.setData(sender, subject, uid, msg_id, date, cached, seen) self.mailsTable.setCellWidget(0, 0, item) self.mailsTable.resizeRowsToContents() wait(30) def loadMail(self, row, column): """ Fetch body of mail and show them in viewer widget""" rows = self.mailsTable.selectionModel().selectedRows() if len(rows)>1 : return # return when multiple is selected self.clearMailViewer() self.mailInfoFrame.setData(self.mailsTable.cellWidget(row, column)) wait(30) msg_id = self.mailInfoFrame.msg_id[:].replace('/', '_') # Some msg_id contains / character msg_struct = ACNT_DIR + self.email_id + '/mails/' + msg_id + '.xml' if os.path.exists(msg_struct): with open(msg_struct, 'r') as doc_file: xml_doc = doc_file.read() self.setAttachments(xml_doc) else: self.bodystructureRequested.emit(self.total_mails-row) def setAttachments(self, xml_doc): # bodystructure dom msg_id = self.mailInfoFrame.msg_id[:].replace('/', '_') # save bodystructure msg_struct = ACNT_DIR + self.email_id + '/mails/' + msg_id + '.xml' if not os.path.exists(msg_struct): with open(msg_struct, 'w') as fd: fd.write(unicode(xml_doc)) # get attachments text_part_num, txt_enc, attachments = getAttachments(xml_doc) for [part_num, enc, filename, file_size] in attachments: ro = self.tableWidget.rowCount() self.tableWidget.insertRow(ro) item = QTableWidgetItem(QIcon(':/download.png'), filename) item.part_num = part_num item.encoding = enc self.tableWidget.setItem(ro, 0, item) self.tableWidget.setItem(ro, 1, QTableWidgetItem(file_size)) if self.tableWidget.rowCount() > 0: self.tableWidget.show() # load html body msg_file = ACNT_DIR + self.email_id + '/mails/' + msg_id + '.txt' if os.path.exists(msg_file): with open(msg_file, 'rb') as fd: html = fd.read() self.setMailText(unicode(html, 'utf8')) # convert byte string to unicode else: row = self.mailsTable.selectionModel().selectedRows()[0].row() self.mailTextRequested.emit(self.total_mails-row, text_part_num, txt_enc) self.mailsTable.cellWidget(row, 0).cached = True def setMailText(self, html): msg_id = self.mailInfoFrame.msg_id[:].replace('/', '_') msg_file = ACNT_DIR + self.email_id + '/mails/' + msg_id + '.txt' if not os.path.exists(msg_file): with open(msg_file, 'wb') as fd: fd.write(unicode(html).encode('utf8')) # save text file in byte string self.textViewer.setText(html) def onAttachmentClick(self, row, col): if col != 0 : return part_num = self.tableWidget.item(row, col).part_num enc = self.tableWidget.item(row, col).encoding filename = self.tableWidget.item(row, col).text() msg_row = self.mailsTable.selectionModel().selectedRows()[0].row() self.saveAttachmentRequested.emit(self.total_mails-msg_row, part_num, enc, filename) def replyMail(self): name, reply_to = splitEmailAddr(self.mailInfoFrame.sender) if reply_to == '' : reply_to = name dialog = send_mail.SendMailDialog(self.email_id, self.passwd, self) dialog.replyTo(reply_to, self.mailInfoFrame.msg_id, self.mailInfoFrame.subject) dialog.exec_() def deleteMail(self): rows = self.mailsTable.selectionModel().selectedRows() uid_list = [self.mailsTable.cellWidget(row.row(), 0).uid for row in rows] self.deleteRequested.emit(uid_list) def printMail(self): printer = QPrinter(mode=QPrinter.HighResolution) printer.setCreator("Daaq Mail") title = 'Daaq Mail' #title = validateFileName(title) printer.setDocName(title) printer.setOutputFileName(DOC_DIR + title + ".pdf") print_dialog = QPrintPreviewDialog(printer, self) print_dialog.paintRequested.connect(self.textViewer.print_) print_dialog.exec_() def newMail(self): dialog = send_mail.SendMailDialog(self.email_id, self.passwd, self) dialog.exec_() def accountSelected(self, account): #print account.text(), self.email_id if str(account.text()) == self.email_id : return self.settings.setValue('EmailId', account.text()) self.setupClient() def manageAccounts(self): dialog = AccountManagerDialog(self.emails, self.types, self) dialog.exec_() self.emails, self.types = dialog.emails, dialog.types self.settings.setValue('Emails', self.emails) self.settings.setValue('Types', self.types) if self.emails == []: self.settings.setValue('EmailId', '') self.refreshAccountsMenu() def refreshAccountsMenu(self): self.accountsMenu.clear() for each in self.emails: self.accountsMenu.addAction(each) def clearMailViewer(self): self.mailInfoFrame.clear() self.textViewer.clear() self.tableWidget.clear() self.tableWidget.setRowCount(0) self.tableWidget.hide() def saveMailInfo(self): if self.total_mails != self.mailsTable.rowCount(): return if self.total_mails == 0 : return # Cache mails in xml file doc = QDomDocument() root = doc.createElement('mails') doc.appendChild(root) for i in range(self.mailsTable.rowCount()): item = self.mailsTable.cellWidget(i,0) mail = doc.createElement('mail') mail.setAttribute('UID', item.uid) mail.setAttribute('Message-ID', item.msg_id) mail.setAttribute('Sender', item.sender) mail.setAttribute('Subject', unicode(item.subject).encode('utf8')) mail.setAttribute('Date', item.date) mail.setAttribute('Cached', item.cached) root.appendChild(mail) mailbox_file = ACNT_DIR + self.email_id + '/%s.xml'%self.mailbox[:].replace('/', '_') with open(mailbox_file, 'w') as doc_file: doc_file.write(doc.toString()) def closeEvent(self, ev): self.saveMailInfo() loop = QtCore.QEventLoop() self.thread.finished.connect(loop.quit) self.thread.quit() loop.exec_() QMainWindow.closeEvent(self, ev)
class BrowserView(QWidget): """Luma LDAP Browser plugin """ # Custom signals used reloadSignal = QtCore.pyqtSignal(QtCore.QModelIndex) clearSignal = QtCore.pyqtSignal(QtCore.QModelIndex) __logger = logging.getLogger(__name__) def __init__(self, parent=None, configPrefix=None): """ :param configPrefix: defines the location of serverlist. :type configPrefix: string """ super(BrowserView, self).__init__(parent) self.__logger = logging.getLogger(__name__) self.setObjectName("PLUGIN_BROWSER") self.templateList = TemplateList() # The serverlist used self.serverList = ServerList(configPrefix) self.serversChangedMessage = QtGui.QErrorMessage() self.mainLayout = QtGui.QHBoxLayout(self) self.splitter = QtGui.QSplitter(self) # Create the model self.ldaptreemodel = LDAPTreeItemModel(self.serverList, self) self.ldaptreemodel.workingSignal.connect(self.setBusy) # Set up the entrylist (uses the model) self.__setupEntryList() # The editor for entries self.tabWidget = QtGui.QTabWidget(self) #self.tabWidget.setDocumentMode(True) self.tabWidget.setMovable(True) self.setMinimumWidth(200) self.tabWidget.setTabsClosable(True) self.tabWidget.tabCloseRequested.connect(self.tabCloseClicked) self.tabWidget.setUsesScrollButtons(True) sizePolicy = self.tabWidget.sizePolicy() sizePolicy.setHorizontalStretch(1) self.tabWidget.setSizePolicy(sizePolicy) # Remember and looks up open tabs self.openTabs = {} self.splitter.addWidget(self.entryList) self.splitter.addWidget(self.tabWidget) self.mainLayout.addWidget(self.splitter) # Used to signal the ldaptreemodel with a index # which needs processing (reloading, clearing) self.reloadSignal.connect(self.ldaptreemodel.reloadItem) self.clearSignal.connect(self.ldaptreemodel.clearItem) eventFilter = BrowserPluginEventFilter(self) self.installEventFilter(eventFilter) self.__createContextMenu() self.retranslateUi() self.progress = QMessageBox(1, self.str_PLEASE_WAIT, self.str_PLEASE_WAIT_MSG, QMessageBox.Ignore, parent=self) # For testing ONLY # AND ONLY ON SMALL LDAP-SERVERS SINCE IT LOADS BASICALLY ALL ENTIRES #import modeltest #self.modeltest = modeltest.ModelTest(self.ldaptreemodel, self); def setBusy(self, status): """ Helper-method. """ if status == True: self.progress.show() qApp.setOverrideCursor(Qt.WaitCursor) else: if not self.progress.isHidden(): self.progress.hide() qApp.restoreOverrideCursor() def __setupEntryList(self): # The view for server-content self.entryList = QtGui.QTreeView(self) self.entryList.setMinimumWidth(200) #self.entryList.setMaximumWidth(400) #self.entryList.setAlternatingRowColors(True) # Somewhat cool, but should be removed if deemed too taxing self.entryList.setAnimated(True) self.entryList.setUniformRowHeights(True) # MAJOR optimalization #self.entryList.setExpandsOnDoubleClick(False) self.entryList.setModel(self.ldaptreemodel) self.entryList.setMouseTracking(True) self.entryList.viewport().setMouseTracking(True) # For right-clicking in the tree self.entryList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.entryList.customContextMenuRequested.connect(self.rightClick) # When something is activated (doubleclick, <enter> etc.) self.entryList.activated.connect(self.viewItem) self.delegate = LoadingDelegate(self.entryList) self.entryList.setItemDelegate(self.delegate) self.entryList.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) def __createContextMenu(self): """Creates the context menu for the tree view. """ self.contextMenu = QMenu() self.contextMenuServerSettings = QAction(self) self.contextMenu.addAction(self.contextMenuServerSettings) self.contextMenu.addSeparator() self.contextMenuOpen = QAction(self) self.contextMenu.addAction(self.contextMenuOpen) self.contextMenuReload = QAction(self) self.contextMenu.addAction(self.contextMenuReload) self.contextMenuClear = QAction(self) self.contextMenu.addAction(self.contextMenuClear) self.contextMenuFilter = QAction(self) self.contextMenu.addAction(self.contextMenuFilter) self.contextMenuLimit = QAction(self) self.contextMenu.addAction(self.contextMenuLimit) self.contextMenu.addSeparator() self.contextMenuAdd = QMenu() self.contextMenu.addMenu(self.contextMenuAdd) self.contextMenuDelete = QMenu() self.contextMenu.addMenu(self.contextMenuDelete) self.contextMenuExport = QMenu() self.contextMenu.addMenu(self.contextMenuExport) # Connect the context menu actions to the correct slots self.contextMenuServerSettings.triggered.connect( self.editServerSettings) self.contextMenuOpen.triggered.connect(self.openChoosen) self.contextMenuReload.triggered.connect(self.reloadChoosen) self.contextMenuClear.triggered.connect(self.clearChoosen) self.contextMenuFilter.triggered.connect(self.filterChoosen) self.contextMenuLimit.triggered.connect(self.limitChoosen) def rightClick(self, point): """ Called when the view is right-clicked. Displays a context menu with possible actions. :param point: contains the global screen coordinates for the right-click that generated this call. :type potin: QPoint """ # This is a list of QModelIndex objects, which will be used by # the various context menu slots. # We therfore store it as a class member self.selection = self.entryList.selectedIndexes() openSupport = True reloadSupport = True clearSupport = True filterSupport = True limitSupport = True addSupport = True deleteSupport = True exportSupport = True editServerSupport = True # The number of selected items is used for naming of the actions # added to the submenues numselected = len(self.selection) # View disabled menu if nothing selected self.contextMenu.setEnabled(True) # Remember to enable if a selection if not numselected > 0: # If nothing is selected self.contextMenu.setEnabled(False) # Disable self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # Show return # Iterate through the list of selected indexes, and # validate what operations are supported. That is, # if one of the selected indexes do not support an # operation, we cannot allow to apply that operation # on the whole selection for index in self.selection: item = index.internalPointer() operations = item.getSupportedOperations() if not AbstractLDAPTreeItem.SUPPORT_OPEN & operations: openSupport = False if not AbstractLDAPTreeItem.SUPPORT_RELOAD & operations: reloadSupport = False if not AbstractLDAPTreeItem.SUPPORT_CLEAR & operations: clearSupport = False if not AbstractLDAPTreeItem.SUPPORT_FILTER & operations: filterSupport = False if not AbstractLDAPTreeItem.SUPPORT_LIMIT & operations: limitSupport = False if not AbstractLDAPTreeItem.SUPPORT_ADD & operations: addSupport = False if not AbstractLDAPTreeItem.SUPPORT_DELETE & operations: deleteSupport = False if not AbstractLDAPTreeItem.SUPPORT_EXPORT & operations: exportSupport = False if index.internalPointer().getParentServerItem() == None: editServerSupport = False # Now we just use the *Support variables to enable|disable # the context menu actions. self.contextMenuOpen.setEnabled(openSupport) self.contextMenuReload.setEnabled(reloadSupport) self.contextMenuClear.setEnabled(clearSupport) self.contextMenuFilter.setEnabled(filterSupport) self.contextMenuLimit.setEnabled(limitSupport) self.contextMenuServerSettings.setEnabled(editServerSupport) # For the submenues in the context menu, we add appropriate # actions, based on single|multi selection, or disable the menu # altogether if there is no support for the operation. if (limitSupport or filterSupport or openSupport) \ and not numselected == 1: self.contextMenuLimit.setEnabled(False) self.contextMenuFilter.setEnabled(False) self.contextMenuOpen.setEnabled(False) if addSupport and numselected == 1: self.contextMenuAdd.setEnabled(True) # template templateMenu = QMenu(self.str_TEMPLATE) self.contextMenuAdd.addMenu(templateMenu) index = self.selection[0] for template in self.templateList.getTable(): sO = index.internalPointer().smartObject() if template.server == sO.serverMeta.name: method = lambda name=template.templateName, i=index: self.addTemplateChoosen( name, i) templateMenu.addAction(template.templateName, method) else: self.contextMenuAdd.setEnabled(False) if numselected != 1: self.contextMenuServerSettings.setEnabled(False) if deleteSupport: self.contextMenuDelete.setEnabled(True) if numselected == 1: self.contextMenuDelete.addAction(self.str_ITEM, self.deleteSelection) self.contextMenuDelete.addAction(self.str_SUBTREE_ONLY, self.deleteSubtree) #self.contextMenuDelete.addAction( # self.str_SUBTREE_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.addAction(self.str_ITEMS, self.deleteSelection) self.contextMenuDelete.addAction(self.str_SUBTREES, self.deleteSubtree) #self.contextMenuDelete.addAction( # self.str_SUBTREES_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.setEnabled(False) if exportSupport: self.contextMenuExport.setEnabled(True) if numselected == 1: self.contextMenuExport.addAction(self.str_ITEM, self.exportItems) self.contextMenuExport.addAction(self.str_SUBTREE, self.exportSubtrees) self.contextMenuExport.addAction(self.str_SUBTREE_PARENTS, self.exportSubtreeWithParents) else: self.contextMenuExport.addAction(self.str_ITEMS, self.exportItems) self.contextMenuExport.addAction(self.str_SUBTREES, self.exportSubtrees) self.contextMenuExport.addAction(self.str_SUBTREES_PARENTS, self.exportSubtreeWithParents) else: self.contextMenuExport.setEnabled(False) # Finally we execute the context menu self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # We need to clear all the submenues after each right click # selection, if not; the submenu actions will be added and # thus duplicated for every selection the user makes. # FIXME: Find a better way of handling this issue. self.contextMenuAdd.clear() self.contextMenuDelete.clear() self.contextMenuExport.clear() """ Following methods are called from a context-menu. """ def openChoosen(self): if len(self.selection) == 1: self.viewItem(self.selection[0]) def reloadChoosen(self): for index in self.selection: self.reloadSignal.emit(index) def clearChoosen(self): for index in self.selection: self.clearSignal.emit(index) def limitChoosen(self): # Have the item set the limit for us, the reload for index in self.selection: ok = index.internalPointer().setLimit() if ok: self.reloadSignal.emit(index) def filterChoosen(self): # Have the item set the filter, then reload for index in self.selection: ok = index.internalPointer().setFilter() if ok: self.reloadSignal.emit(index) def addTemplateChoosen(self, templateName, index): serverMeta = index.internalPointer().smartObject().serverMeta baseDN = index.internalPointer().smartObject().getDN() template = self.templateList.getTemplateObject(templateName) smartO = template.getDataObject(serverMeta, baseDN) self.addNewEntry(index, smartO, template) def addNewEntry(self, parentIndex, defaultSmartObject=None, template=None): tmp = NewEntryDialog(parentIndex, defaultSmartObject, entryTemplate=template) if tmp.exec_(): ret = QMessageBox.question( self, QtCore.QCoreApplication.translate("BrowserView", "Add"), QtCore.QCoreApplication.translate( "BrowserView", "Do you want to reload to show the changes?"), QMessageBox.Yes | QMessageBox.No) if ret == QMessageBox.Yes: self.ldaptreemodel.reloadItem(self.selection[0]) """ Utility-methods """ def isOpen(self, smartObject): rep = self.getRepForSmartObject(smartObject) # The {}.has_key() method will be removed in the future version # of Python. Use the 'in' operation instead. [PEP8] #if self.openTabs.has_key(str(rep)): if str(rep) in self.openTabs: return True else: return False def getRepForSmartObject(self, smartObject): serverName = smartObject.getServerAlias() dn = smartObject.getDN() return (serverName, dn) def viewItem(self, index): """Opens items for viewing. """ item = index.internalPointer() supports = item.getSupportedOperations() # If we can't open this item, then don't if not supports & AbstractLDAPTreeItem.SUPPORT_OPEN: self.__logger.debug("Item didn't support open.") return smartObject = index.internalPointer().smartObject() rep = self.getRepForSmartObject(smartObject) # If the smartobject is already open, switch to it if self.isOpen(smartObject): x = self.openTabs[str(rep)] self.tabWidget.setCurrentWidget(x) return # Saves a representation of the opened entry to avoid opening duplicates # and open it x = AdvancedObjectWidget(QtCore.QPersistentModelIndex(index)) x.initModel(smartObject) self.openTabs[str(rep)] = x self.tabWidget.addTab(x, smartObject.getPrettyRDN()) self.tabWidget.setCurrentWidget(x) def deleteIndex(self, index): # Remember the smartObject for later sO = index.internalPointer().smartObject() # Try to delete (success, message) = self.ldaptreemodel.deleteItem(index) if success: # Close open edit-windows if any self.__closeTabIfOpen(sO) # Notify success return (True, message) else: # Notify fail return (False, message) def __closeTabIfOpen(self, sO): if self.isOpen(sO): rep = self.getRepForSmartObject(sO) x = self.openTabs.pop(str(rep)) i = self.tabWidget.indexOf(x) if i != -1: self.tabWidget.removeTab(i) def deleteSelection(self, subTree=False): """Slot for the context menu. Opens the DeleteDialog with the selected entries, giving the user the option to validate the selection before deleting. This is for deleting the item + possibly it's subtree. See deleteOnlySubtreeOfSelection() for only subtree. """ # Only a single item if len(self.selection) == 1 and not subTree: # Confirmation-message ok = QMessageBox.question(self, self.str_DELETE, self.str_REALLY_DELETE, QMessageBox.Yes | QMessageBox.No) if ok == QMessageBox.No: return index = self.selection[0] (status, message) = self.deleteIndex(index) if not status: QMessageBox.critical( self, self.str_ERROR, self.str_ERROR_MSG.format(index.data().toPyObject(), message)) return # Make persistent indexes and list of smartObjects to be deleted persistenSelection = [] sOList = [] for x in self.selection: persistenSelection.append(QPersistentModelIndex(x)) sOList.append(x.internalPointer().smartObject()) # Create gui self.setBusy(True) deleteDialog = DeleteDialog(sOList, subTree) self.setBusy(False) status = deleteDialog.exec_() if status: # the dialog was not canceled if subTree: # Reload the items whos subtree was deleted for x in self.selection: self.ldaptreemodel.reloadItem(x) return # If all rows were removed successfully, just call # removeRows on all selected items (reloading all items of # the parent can be expensive) if deleteDialog.passedItemsWasDeleted: for x in persistenSelection: if x.isValid: i = x.sibling(x.row(), 0) # QModelIndex self.__closeTabIfOpen( i.internalPointer().smartObject()) self.ldaptreemodel.removeRow(x.row(), x.parent()) return # If not, call reload on the parent of all the items? else: tmp = QMessageBox.question(self, self.str_DELETION, self.str_DELETION_MSG, buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.Yes) if tmp == QMessageBox.Yes: for x in persistenSelection: # index might not be valid if the parent was # reloaded by a previous item if x.isValid(): self.ldaptreemodel.reloadItem(x.parent()) return # Was cancelled so do nothing else: pass def deleteSubtree(self): self.deleteSelection(subTree=True) def exportItems(self): """Slot for the context menu. """ self.__exportSelection(scope=0) def exportSubtrees(self): """Slot for the context menu. """ self.__exportSelection(scope=1) def exportSubtreeWithParents(self): """Slot for the context menu. """ self.__exportSelection(scope=2) def __exportSelection(self, scope=0): """Slot for the context menu. Opens the ExportDialog with the selected entries, giving the user the option to validate the selection before exporting. :param scope: The scope selection. 0 = SCOPE_BASE -> Item(s), 1 = SCOPE_ONELEVEL -> Subtree(s); 2 = SCOPE_SUBTREE -> Subtree(s) with parent :type scope: int """ exportObjects = [] msg = '' self.setBusy(True) for index in self.selection: smartObject = index.internalPointer().smartObject() serverName = smartObject.getServerAlias() dn = smartObject.getDN() serverObject = self.serverList.getServerObject(serverName) con = LumaConnectionWrapper(serverObject, self) # For both subtree and subtree with parent, we fetch the # whole subtree including the parent, with a basic sync # search operation. Then, if only the subtree is to be # exported, we remove the smartObject(s) selected. if scope > 0: pass # Do a search on the whole subtree # 2 = ldap.SCOPE_SUBTREE #elif scope == 2: success, e = con.bindSync() if not success: self.__logger.error(str(e)) continue success, result, e = con.searchSync(base=dn, scope=2) if success: exportObjects.extend(result) else: self.__logger.error(str(e)) # If only the subtree is to be selected, we remove # the parent, which happens to be the smartObject(s) # initialy selected. if scope == 1: exportObjects.remove(smartObject) # For scope == 0 we need not do any LDAP search operation # because we already got what we need else: exportObjects.append(smartObject) # Initialize the export dialog # and give it the items for export dialog = ExportDialog(msg) dialog.setExportItems(exportObjects) self.setBusy(False) dialog.exec_() def editServerSettings(self): """Slot for the context menu. Opens the ServerDialog with the selected server. """ try: items = self.selection serverItem = items[0].internalPointer().getParentServerItem() serverName = serverItem.serverMeta.name serverDialog = ServerDialog(serverName) r = serverDialog.exec_() if r: self.serversChangedMessage.showMessage( self.str_SERVER_CHANGED_MSG) except Exception, e: self.__logger.error(str(e)) QMessageBox.information(self, self.str_ERROR, self.str_SEE_LOG_DETAILS)