def showContextMenu(self, point): 'Show the Columns context menu' if self.model() is None: return # If we are viewing a proxy model, skip to the source model mdl = self.model() while isinstance(mdl, QAbstractProxyModel): mdl = mdl.sourceModel() if mdl is None or not hasattr(mdl, 'columns'): return # Generate and show the Menu m = QMenu() for i in range(len(mdl.columns)): c = mdl.columns[i] if c.internal: continue a = QAction(mdl.headerData(i, Qt.Horizontal, Qt.DisplayRole), m) a.setCheckable(True) a.setChecked(not self.isColumnHidden(i)) a.triggered.connect(partial(self.showHideColumn, c=i, s=self.isColumnHidden(i))) m.addAction(a) m.exec_(self.header().mapToGlobal(point))
def note_context_menu(self, pos): menu = QMenu(self.ui.notesList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'), self.edit_note) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_note) menu.exec_(self.ui.notesList.mapToGlobal(pos))
def loadContextMenu(self, *args): menu = QMenu(self.w_tree) path = self.w_tree.currentItem().text(0) if os.path.isdir(path): dirPath = path else: dirPath = os.path.dirname(path) if os.path.exists(dirPath): menu.addAction(unicode("Open Folder", errors='replace'), self.openFolder) menu.addAction(unicode("Copy Path", errors='replace'), self.copyPath) separator = QAction(self.w_tree) separator.setSeparator(True) menu.addAction(separator) menu.addAction(unicode("Replace Path", errors='replace'), self.replacePath) separator = QAction(self.w_tree) separator.setSeparator(True) menu.addAction(separator) if self.unusedExists(): menu.addAction(unicode("Remove Unused Files", errors='replace'), self.removeUnused) pos = QCursor.pos() point = QtCore.QPoint(pos.x() + 10, pos.y()) menu.exec_(point)
def tag_context_menu(self, pos): index = self.ui.tagsList.currentIndex() item = self.tagsModel.itemFromIndex(index) if hasattr(item, 'tag'): menu = QMenu(self.ui.tagsList) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_tag) menu.exec_(self.ui.tagsList.mapToGlobal(pos))
def __menu(self, pos): u"""Slot d'apparition du menu contextuel""" menu = QMenu() menu.addAction("Supprimer", self, SLOT("supprimer()"), QKeySequence.Delete) menu.addAction("Nouveau", self, SLOT("nouveau()"), QKeySequence.New) menu.exec_(self._ui.tv.mapToGlobal(pos))
def click(self, res, event): """Open resource""" button = event.button() if button == Qt.LeftButton: subprocess.Popen(['xdg-open', res.file_path]) elif button == Qt.RightButton: menu = QMenu(self.parent) menu.addAction( self.app.translate('ResourceEdit', 'Put to Content'), Slot()(partial( self.to_content, res=res, )), ) if not self.parent.note_edit.in_content(res): menu.addAction( self.app.translate('ResourceEdit', 'Remove Resource'), Slot()(partial( self.remove, res=res, ))) menu.addAction(self.app.translate('ResourceEdit', 'Save As'), Slot()(partial( self.save, res=res, ))) menu.exec_(event.globalPos())
def click(self, res, event): """Open resource""" button = event.button() if button == Qt.LeftButton: subprocess.Popen(['xdg-open', res.file_path]) elif button == Qt.RightButton: menu = QMenu(self.parent) if res.mime.find('image') == 0: menu.addAction( self.parent.tr('Put to Content'), Slot()(partial( self.to_content, res=res, )), ) if not self.parent.note_edit.in_content(res): menu.addAction( self.parent.tr('Remove Resource'), Slot()(partial( self.remove, res=res, )) ) menu.addAction( self.parent.tr('Save As'), Slot()(partial( self.save, res=res, )) ) menu.exec_(event.globalPos())
def notebook_context_menu(self, pos): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): menu = QMenu(self.ui.notebooksList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_notebook) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_notebook) menu.exec_(self.ui.notebooksList.mapToGlobal(pos))
class HierarchyTreeView(QTreeView): def __init__(self): super(HierarchyTreeView, self).__init__() #ukljucuje kontekstni meni self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.openMenu) def openMenu(self, position): self.contextMenu = QMenu() newMenu = QMenu("New") self.contextMenu.addMenu(newMenu) actionNewProj = QAction("NewProject", None) actionNewProj.triggered.connect(self.addNode) actionRename = QAction("Rename", None) actionRename.triggered.connect(self.renameNode) actionRemProj = QAction("Delete", None) actionRemProj.triggered.connect(self.removeNode) newMenu.addAction(actionNewProj) self.contextMenu.addAction(actionRename) self.contextMenu.addAction(actionRemProj) #prikaz kontekstnog menija self.contextMenu.exec_(self.viewport().mapToGlobal(position)) def addNode(self): model = self.model() node = Node("NoviCvor") if not self.currentIndex().isValid(): model.insertRow(model.rowCount(self.currentIndex()), node) else: model.insertRow(model.rowCount(self.currentIndex()), node, self.currentIndex()) self.expand(self.currentIndex()) def removeNode(self): model = self.model() model.removeRow(self.currentIndex().internalPointer().getIndex(), self.currentIndex().parent()) def renameNode(self): self.currentIndex().internalPointer().setName("NOVO") def mousePressEvent(self, event): if(self.selectionMode() == QAbstractItemView.SingleSelection): self.clearSelection() self.setCurrentIndex(QModelIndex()) super(HierarchyTreeView, self).mousePressEvent(event)
def mousePressEvent(self, mouse_event): if mouse_event.button() == Qt.RightButton: if self._spec_index is None: print("spec_index is None") print(self) print(self._running_providers) return pos = mouse_event.pos() items = [ item for item in self.items(pos) if isinstance(item, NodeItem) and item._label.text() ] if len(items) != 1: print("wrong number of things", [x._label.text() for x in items]) return name = items[0]._label.text().rstrip('(default)').strip() if name not in self._spec_index.provider_names: print(name, "Not in list of providers") return provider = self._spec_index.providers[name] def start_trigger(): # TODO: replace 'capability_server' with user provided server name service_name = '/{0}/start_capability'.format( 'capability_server') rospy.wait_for_service(service_name) start_capability = rospy.ServiceProxy(service_name, StartCapability) start_capability(provider.implements, name) def stop_trigger(): # TODO: replace 'capability_server' with user provided server name service_name = '/{0}/stop_capability'.format( 'capability_server') rospy.wait_for_service(service_name) stop_capability = rospy.ServiceProxy(service_name, StopCapability) stop_capability(provider.implements) if name not in self._running_providers: trigger = start_trigger msg = "start => " else: trigger = stop_trigger msg = "stop => " menu = QMenu() action = menu.addAction(msg + name) action.triggered.connect(trigger) pos = mouse_event.globalPos() pos = QPoint(pos.x(), pos.y()) menu.exec_(pos) else: InteractiveGraphicsView.mousePressEvent(self, mouse_event)
def __LobbyListMenu( self, position ): lobby_menu = QMenu() rm_from_lobby = QAction( self ) rm_from_lobby.setText( "Remove player from lobby" ) rm_from_lobby.triggered.connect( self._RemoveFromLobbyListAction ) lobby_menu.addAction( rm_from_lobby ) lobby_menu.exec_( self.window.lobby_lst.viewport().mapToGlobal( position ) )
def _handle_context_menu(self, pos): index = self.ui.tree_view.indexAt(pos) if index.isValid(): pos = self.ui.tree_view.viewport().mapToGlobal(pos) menu = QMenu() menu.addAction(self.ui.action_mark_watched) menu.addAction(self.ui.action_mark_unwatched) menu.addSeparator() menu.addAction(self.ui.action_remove) menu.exec_(pos)
def click(self, res, event): """Open resource""" button = event.button() if button == Qt.LeftButton: subprocess.Popen(["xdg-open", res.file_path]) elif button == Qt.RightButton: menu = QMenu(self.parent) if not res.in_content: menu.addAction(self.parent.tr("Remove Resource"), Slot()(partial(self.remove, res=res))) menu.addAction(self.parent.tr("Save As"), Slot()(partial(self.save, res=res))) menu.exec_(event.globalPos())
def contextMenuEvent(self, event): """ Handles the ``contextMenuEvent`` event for :class:`CmdPromptInput`. :param `event`: a `QContextMenuEvent` event to be processed. """ menu = QMenu(self) menu.addSeparator() # TODO: Extra stuff menu.addAction(QAction("TODO: Add Extra stuff", self)) menu.exec_(event.globalPos())
def loadContextMenu(self): def cmd_loadSelected(): selObjects = pymel.core.ls(sl=1) if not selObjects: return cuItem = self.treeWidget.currentItem() cuItem.setText(1, selObjects[0].name()) menu = QMenu(self.treeWidget) menu.addAction("Load Object", cmd_loadSelected) pos = QCursor.pos() point = QtCore.QPoint(pos.x() + 10, pos.y()) menu.exec_(point)
def send_to_popup(self): drive_option=QMenu(self.parent()) opts=config.SEND_TO[0:(len(config.SEND_TO)-1)] for cnf in opts: k=QAction(QIcon(appicon("explore")),cnf,self ) k.triggered.connect(self.click_event) drive_option.addAction(k) drive_option.exec_(QtGui.QCursor.pos())
def __PlayersListMenu( self, position ): list_menu = QMenu() add_player_to_lobby = QAction( self ) add_player_to_lobby.setText( "Add player to lobby" ) add_player_to_lobby.triggered.connect( self._AddToLobbyAction ) rm_player = QAction( self ) rm_player.setText( "Remove player" ) rm_player.triggered.connect( self._RemoveFromPlayersListAction ) list_menu.addAction( rm_player ) list_menu.addAction( add_player_to_lobby ) list_menu.exec_( self.window.players_lst.viewport().mapToGlobal( position ) )
def mousePressEvent(self, mouse_event): if mouse_event.button() == Qt.RightButton: if self._spec_index is None: print("spec_index is None") print(self) print(self._running_providers) return pos = mouse_event.pos() items = [item for item in self.items(pos) if isinstance(item, NodeItem) and item._label.text()] if len(items) != 1: print("wrong number of things", [x._label.text() for x in items]) return name = items[0]._label.text().rstrip('(default)').strip() if name not in self._spec_index.provider_names: print(name, "Not in list of providers") return provider = self._spec_index.providers[name] def start_trigger(): # TODO: replace 'capability_server' with user provided server name service_name = '/{0}/start_capability'.format('capability_server') rospy.wait_for_service(service_name) start_capability = rospy.ServiceProxy(service_name, StartCapability) start_capability(provider.implements, name) def stop_trigger(): # TODO: replace 'capability_server' with user provided server name service_name = '/{0}/stop_capability'.format('capability_server') rospy.wait_for_service(service_name) stop_capability = rospy.ServiceProxy(service_name, StopCapability) stop_capability(provider.implements) if name not in self._running_providers: trigger = start_trigger msg = "start => " else: trigger = stop_trigger msg = "stop => " menu = QMenu() action = menu.addAction(msg + name) action.triggered.connect(trigger) pos = mouse_event.globalPos() pos = QPoint(pos.x(), pos.y()) menu.exec_(pos) else: InteractiveGraphicsView.mousePressEvent(self, mouse_event)
def contextMenuEvent(self, event): ''' When user right-clicks: display context menu ''' png_action = 'Export branch to PNG, SVG or PDF' hl_action = 'Hyperlink' my_menu = QMenu(png_action) if not hasattr(self, '_no_hyperlink'): my_menu.addAction(hl_action) my_menu.addAction(png_action) action = my_menu.exec_(event.screenPos()) if action: if action.text() == png_action: # Save a picture of the selected symbol and all its children filename = QFileDialog.getSaveFileName(self.window(), 'Export picture', '.', 'Picture (*.png, *.svg, *.pdf)')[0] if not filename: return save_fmt = filename.split(os.extsep)[-1] if save_fmt not in ('png', 'svg', 'pdf'): return self.scene().export_branch_to_picture(self, filename, save_fmt) elif action.text() == hl_action: if self.text: self.hyperlink_dialog.setParent( self.scene().views()[0], Qt.Dialog) self.hlink_field.setText(self.text.hyperlink) self.hyperlink_dialog.show()
def popup_menu(self, position): selected_row = self.view.rowAt(position.y()) if selected_row >= 0 and self._used_categories and len( self._used_categories) > 1: category_menu = QMenu(_("Categories")) selected_doc = self.model.object_at(selected_row) category_actions = [] for category in self._used_categories: a = QAction(category.full_name, category_menu) a.setData(category) a.setEnabled(selected_doc.document_category_id != category.document_category_id) category_menu.addAction(a) category_actions.append(a) action = category_menu.exec_(QCursor.pos()) if action: new_category = action.data() if selected_doc.document_category_id != new_category.document_category_id: selected_doc.document_category_id = new_category.document_category_id self.model.signal_object_change(selected_doc)
def showContextMenu(self, pos): """ Shows a context menu to add a node in the graph widget """ gpos = self.graphicsView.mapToGlobal(pos) menu = QMenu() actionAddNode = menu.addAction("Add Node") QAction = menu.exec_(gpos) if (actionAddNode == QAction): (text, ok) = QInputDialog.getText(self.graphicsView, "Insert Node Name", "Please insert a name for the node") if ok: if text not in self.nodesToQNodes: #User clicked on ok. Otherwise do nothing self.gv.add_node(text) node = self.gv.get_node(text) qnode = self.createQtNode(node, 0, 0, QColor(204, 255, 255)) self.graphicsView.scene().addItem(qnode) qnode.setPos(self.graphicsView.mapToScene(gpos)) qnode.setPos(qnode.x(), qnode.y() - 200) self.nodesToQNodes[node] = qnode else: msg = QMessageBox() msg.setText("The node already exists.") msg.exec_() self.searchNode(text)
def popup(self,pos): menu = QMenu() saveRepAction = QAction(self) saveRepAction.setText('Save representation...') saveRepAction.triggered.connect(lambda: self.saveRep(self.indexAt(pos))) menu.addAction(saveRepAction) action = menu.exec_(self.mapToGlobal(pos))
def contextMenuEvent(self, e): ''' calls context menu if right click: - set server - trigger server (storts or stops the server) in case of righ click on point, a plot is added ''' i = 0 for pp in self.points: i += 1 if (e.pos()-pp).manhattanLength() > 5: continue else: self.addPlot(str(i)) return menu = QMenu() menu.addAction("Set Server", self.setServer) menu.addAction("Trigger Server", self.stream) menu.exec_(e.globalPos())
def _customMenu(self): selectedItems = self.pushupsListWidget.selectedItems() if selectedItems is not None : selectedItem = selectedItems[0] if selectedItem.parent() is not None : # Child Item selected menu = QMenu() delete = QAction(self.pushupsListWidget) delete.setText("Delete this pushup") delete.triggered.connect(self._emitDeleteSignal) menu.addAction(delete) menu.exec_(QCursor.pos()) else : # Top level Item selected menu = QMenu() delete = QAction(self.pushupsListWidget) delete.setText("Delete this day and all of its exercises") delete.triggered.connect(self._emitDeleteDaySignal) menu.addAction(delete) menu.exec_(QCursor.pos())
def _customMenu(self): selectedItems = self.pushupsListWidget.selectedItems() if selectedItems is not None: selectedItem = selectedItems[0] if selectedItem.parent() is not None: # Child Item selected menu = QMenu() delete = QAction(self.pushupsListWidget) delete.setText("Delete this pushup") delete.triggered.connect(self._emitDeleteSignal) menu.addAction(delete) menu.exec_(QCursor.pos()) else: # Top level Item selected menu = QMenu() delete = QAction(self.pushupsListWidget) delete.setText("Delete this day and all of its exercises") delete.triggered.connect(self._emitDeleteDaySignal) menu.addAction(delete) menu.exec_(QCursor.pos())
class ColorWidget(QFrame): color_changed = Signal(int) def __init__(self, parent=None): self.color = QColor(0, 0, 0) super().__init__(parent) self.setFrameStyle(QFrame.Panel | QFrame.Raised) dial = QColorDial(self) dial.sliderMoved.connect(self.set_color) action = QWidgetAction(self) action.setDefaultWidget(dial) self.menu = QMenu() self.menu.addAction(action) #self.addAction(action) #self.setContextMenuPolicy(Qt.ActionsContextMenu) def paintEvent(self, event): super().paintEvent(event) painter = QPainter(self) painter.fillRect(event.rect(), self.color) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.menu.exec_(self.mapToGlobal(event.pos())) def sizeHint(self): return QSize(24, 20) def set_color(self, color): self.color = QColor(color, color, color) self.color_changed.emit(color) self.update()
class ColorWidget(QFrame): color_changed = Signal(int) def __init__(self, parent=None): self.color = QColor(0, 0, 0) super().__init__(parent) self.setFrameStyle(QFrame.Panel|QFrame.Raised) dial = QColorDial(self) dial.sliderMoved.connect(self.set_color) action = QWidgetAction(self) action.setDefaultWidget(dial) self.menu = QMenu() self.menu.addAction(action) #self.addAction(action) #self.setContextMenuPolicy(Qt.ActionsContextMenu) def paintEvent(self, event): super().paintEvent(event) painter = QPainter(self) painter.fillRect(event.rect(), self.color) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.menu.exec_(self.mapToGlobal(event.pos())) def sizeHint(self): return QSize(24, 20) def set_color(self, color): self.color = QColor(color, color, color) self.color_changed.emit(color) self.update()
def _tree_customContextMenuRequesssted(self, pos): idx = self.tv.indexAt(pos) if not idx.isValid(): return addr = idx.data(role=self.ADDR_ROLE) if not addr: return name_idx = idx.sibling(idx.row(), 1) old_name = name_idx.data() menu = QMenu() rename_action = menu.addAction('Rename `%s`...' % old_name) rename_action.setShortcut('n') action = menu.exec_(self.tv.mapToGlobal(pos)) if action == rename_action: return self._rename_ea_requested(addr, name_idx)
def showAttributeMenu(self, attribute): m = QMenu() m.addAction('Overlay/Filter') c = m.addMenu('Compare to') for h in self.headers: c.addAction(h) m.addAction('Sort...') choice = m.exec_(QCursor.pos()) if choice != None: choice = choice.text() if choice == 'Overlay/Filter': self.changeOverlay(attribute) elif choice == 'Sort...': # TODO pass else: self.showScatterplot(choice,attribute)
def open_new_shader_menu(): """ Open menu using QMenu with all maps :return: """ try: from PySide.QtGui import QMenu, QAction, QCursor except: from PySide2.QtGui import QCursor from PySide2.QtWidgets import QMenu, QAction maps = SuperShader._get_maps() if not maps: return menu = QMenu(hou.ui.mainQtWindow()) menu.setStyleSheet(hou.ui.qtStyleSheet()) for m in maps: if m.get('allow_creation'): menu.addAction(QAction(m['name'], menu)) act = menu.exec_(QCursor.pos()) if not act: return new_shader_map = ([x for x in maps if x['name'] == act.text()] or [None])[0] return new_shader_map
class CodeEdit(QPlainTextEdit, StyledObject): """ The code editor text edit. This is a specialized QPlainTextEdit made to expose additional signals, styling and methods. It also provides a custom context menu and methods to add actions, separators and sub-menus. Most of the code editor functionnalities are provided by installing modes on the PCEF instance. Additional signals: - dirtyChanged(bool) - focusedIn(QFocusEvent) - keyPressed(QKeyEvent) - keyReleased(QKeyEvent) - mousePressed(QMouseEvent) - mouseReleased(QMouseEvent) - newTextSet() - prePainting(QPaintEvent) - postPainting(QPaintEvent) - visibleBlocksChanged() """ QSS = """QPlainTextEdit { background-color: %(b)s; color: %(t)s; selection-background-color: %(bs)s; selection-color: %(ts)s; border: none; border-radius: 0px; } """ #--------------------------------------------------------------------------- # Signals #--------------------------------------------------------------------------- #: Signal emitted when the dirty state of the document changed dirtyChanged = Signal(bool) #: Signal emitted when a key is pressed keyPressed = Signal(QKeyEvent) #: Signal emitted when a key is released keyReleased = Signal(QKeyEvent) #: Signal emitted when a mouse button is pressed mousePressed = Signal(QMouseEvent) #: Signal emitted when a mouse button is released mouseReleased = Signal(QMouseEvent) #: Signal emitted on a wheel event mouseWheelActivated = Signal(QWheelEvent) #: Signal emitted before painting the core widget prePainting = Signal(QPaintEvent) #: Signal emitted after painting the core widget postPainting = Signal(QPaintEvent) #: Signal emitted at the end of the keyPressed event postKeyPressed = Signal(QKeyEvent) #: Signal emitted when the list of visible blocks changed visibleBlocksChanged = Signal() #: Signal emitted when setPlainText is invoked newTextSet = Signal() #: Signal emitted when focusInEvent is is called focusedIn = Signal(QFocusEvent) #: Signal emitted when the text is saved with pcef.saveFileFromEditor. # The signal is emitted with the complete file path textSaved = Signal(str) #--------------------------------------------------------------------------- # Properties #--------------------------------------------------------------------------- def __get_dirty(self): return self.__dirty def __set_dirty(self, dirty): if dirty != self.__dirty: self.__dirty = dirty self.dirtyChanged.emit(dirty) @property def contextMenu(self): return self.__context_menu #: Tells whether the editor is dirty(changes have been made to the document) dirty = property(__get_dirty, __set_dirty) #--------------------------------------------------------------------------- # Methods #--------------------------------------------------------------------------- def __init__(self, parent=None): """ Creates the widget. :param parent: Optional parent widget """ QPlainTextEdit.__init__(self, parent) StyledObject.__init__(self) #: Tag member used to remeber the filename of the edited text if any self.tagFilename = None #: Tag member used to remeber the filename of the edited text if any self.tagEncoding = 'utf8' #: Weakref to the editor self.editor = None self.__originalText = "" #: dirty flag self.__dirty = False #: our custom context menu self.__context_menu = QMenu() #: The list of active extra-selections (TextDecoration) self.__selections = [] self.__numBlocks = -1 self.visible_blocks = [] #: Shortcut to the fontMetrics self.fm = self.fontMetrics() self.textChanged.connect(self.__onTextChanged) self.blockCountChanged.connect(self.__onBlocksChanged) self.verticalScrollBar().valueChanged.connect(self.__onBlocksChanged) self.newTextSet.connect(self.__onBlocksChanged) self.cursorPositionChanged.connect(self.__onBlocksChanged) self.setLineWrapMode(QPlainTextEdit.NoWrap) self.setMouseTracking(True) self._onStyleChanged() def addAction(self, action): """ Adds an action to the text edit context menu :param action: QAction """ QTextEdit.addAction(self, action) self.__context_menu.addAction(action) def addSeparator(self): """ Adds a separator to the context menu """ self.__context_menu.addSeparator() def addDecoration(self, decoration): """ Add a text decoration :param decoration: Text decoration :type decoration: pcef.core.TextDecoration """ self.__selections.append(decoration) self.__selections = sorted(self.__selections, key=lambda sel: sel.draw_order) self.setExtraSelections(self.__selections) def removeDecoration(self, decoration): """ Remove text decoration. :param decoration: The decoration to remove :type decoration: pcef.core.TextDecoration """ try: self.__selections.remove(decoration) self.setExtraSelections(self.__selections) except ValueError: pass def setShowWhitespaces(self, show): """ Shows/Hides whitespaces. :param show: True to show whitespaces, False to hide them :type show: bool """ doc = self.document() options = doc.defaultTextOption() if show: options.setFlags(options.flags() | QTextOption.ShowTabsAndSpaces) else: options.setFlags(options.flags() & ~QTextOption.ShowTabsAndSpaces) doc.setDefaultTextOption(options) def indent(self, size): """ Indent current line or selection :param size: indent size in spaces :type size: int """ cursor = self.textCursor() cursor.beginEditBlock() sel_start = cursor.selectionStart() sel_end = cursor.selectionEnd() has_selection = True if not cursor.hasSelection(): cursor.select(QTextCursor.LineUnderCursor) has_selection = False nb_lines = len(cursor.selection().toPlainText().splitlines()) cursor.setPosition(cursor.selectionStart()) for i in range(nb_lines): cursor.movePosition(QTextCursor.StartOfLine) cursor.insertText(" " * size) cursor.movePosition(QTextCursor.EndOfLine) cursor.setPosition(cursor.position() + 1) cursor.setPosition(sel_start + size) if has_selection: cursor.setPosition(sel_end + (nb_lines * size), QTextCursor.KeepAnchor) cursor.endEditBlock() self.setTextCursor(cursor) def unIndent(self, size): """ Un-indent current line or selection by tab_size """ cursor = self.textCursor() assert isinstance(cursor, QTextCursor) cursor.beginEditBlock() pos = cursor.position() sel_start = cursor.selectionStart() sel_end = cursor.selectionEnd() has_selection = True if not cursor.hasSelection(): cursor.select(QTextCursor.LineUnderCursor) has_selection = False nb_lines = len(cursor.selection().toPlainText().splitlines()) cursor.setPosition(cursor.selectionStart()) cpt = 0 for i in range(nb_lines): cursor.select(QTextCursor.LineUnderCursor) if cursor.selectedText().startswith(" " * size): cursor.movePosition(QTextCursor.StartOfLine) [cursor.deleteChar() for _ in range(size)] pos = pos - size cpt += 1 else: cursor.clearSelection() # next line cursor.movePosition(QTextCursor.EndOfLine) cursor.setPosition(cursor.position() + 1) if cpt: cursor.setPosition(sel_start - size) else: cursor.setPosition(sel_start) if has_selection: cursor.setPosition(sel_end - (cpt * size), QTextCursor.KeepAnchor) cursor.endEditBlock() self.setTextCursor(cursor) def updateOriginalText(self): self.__originalText = self.toPlainText() def _onStyleChanged(self): """ Updates widget style when style changed. """ self.setFont(QFont(self.currentStyle.fontName, self.currentStyle.fontSize)) self.fm = self.fontMetrics() qss = self.QSS % { 'b': self.currentStyle.backgroundColor, 't': self.currentStyle.tokenColor(Token), "bs": self.currentStyle.selectionBackgroundColor, "ts": self.currentStyle.selectionTextColor} self.setShowWhitespaces(self.currentStyle.showWhitespaces) self.setStyleSheet(qss) def paintEvent(self, event): """ Emits prePainting and postPainting signals :param event: QPaintEvent """ self.prePainting.emit(event) QPlainTextEdit.paintEvent(self, event) self.postPainting.emit(event) def keyPressEvent(self, event): """ Performs indentation if tab key presed, else emits the keyPressed signal :param event: QKeyEvent """ # assert isinstance(event, QKeyEvent) event.stop = False # replace tabs by space if event.key() == Qt.Key_Tab: cursor = self.textCursor() assert isinstance(cursor, QTextCursor) if not cursor.hasSelection(): # insert tab at cursor pos cursor.insertText(" " * self.editor().TAB_SIZE) else: # indent whole selection self.indent(self.editor().TAB_SIZE) event.stop = True self.keyPressed.emit(event) if not event.stop: QPlainTextEdit.keyPressEvent(self, event) self.postKeyPressed.emit(event) def keyReleaseEvent(self, event): """ Performs indentation if tab key pressed, else emits the keyPressed signal :param event: QKeyEvent """ assert isinstance(event, QKeyEvent) event.stop = False self.keyReleased.emit(event) if not event.stop: QPlainTextEdit.keyReleaseEvent(self, event) def focusInEvent(self, event): """ Emits the focusedIn signal :param event: :return: """ self.focusedIn.emit(event) QPlainTextEdit.focusInEvent(self, event) def mousePressEvent(self, event): """ Emits mousePressed signal :param event: QMouseEvent """ event.stop = False self.mousePressed.emit(event) if not event.stop: QPlainTextEdit.mousePressEvent(self, event) def mouseReleaseEvent(self, event): """ Emits mouseReleased signal. :param event: QMouseEvent """ event.stop = False self.mouseReleased.emit(event) if not event.stop: QPlainTextEdit.mouseReleaseEvent(self, event) def wheelEvent(self, event): """ Emits the mouseWheelActivated signal. :param event: QMouseEvent """ event.stop = False self.mouseWheelActivated.emit(event) if not event.stop: QPlainTextEdit.wheelEvent(self, event) def mouseMoveEvent(self, event): assert isinstance(event, QMouseEvent) c = self.cursorForPosition(event.pos()) for sel in self.__selections: if sel.containsCursor(c) and sel.tooltip: QToolTip.showText(self.mapToGlobal(event.pos()), sel.tooltip, self) break QPlainTextEdit.mouseMoveEvent(self, event) def contextMenuEvent(self, event): """ Shows our own context menu """ self.__context_menu.exec_(event.globalPos()) def resizeEvent(self, event): """ Updates visible blocks on resize """ self.__onBlocksChanged() QPlainTextEdit.resizeEvent(self, event) def __onTextChanged(self): """ Sets dirty to true """ if self.toPlainText() != self.__originalText: # self.__originalText = self.toPlainText() self.dirty = True else: self.dirty = False def setPlainText(self, txt): """ Sets the text edit content and emits newTextSet signal. :param txt: New text to display """ self.__originalText = txt QPlainTextEdit.setPlainText(self, txt) self.newTextSet.emit() def __onBlocksChanged(self): """ Updates the list of visible blocks and emits visibleBlocksChanged signal. """ visible_blocks = [] block = self.firstVisibleBlock() row = block.blockNumber() + 1 width = self.width() w = width - 2 h = self.fm.height() bbox = self.blockBoundingGeometry(block) top = bbox.translated(self.contentOffset()).top() bottom = top + bbox.height() zoneTop = 0 # event.rect().top() zoneBottom = self.height() # event.rect().bottom() visible_blocks_append = visible_blocks.append while block.isValid() and top <= zoneBottom: if block.isVisible() and bottom >= zoneTop: visible_blocks_append( VisibleBlock(row, block, (0, top, w, h)) ) block = block.next() row += 1 top = bottom bottom = top + self.blockBoundingRect(block).height() self.visible_blocks = visible_blocks self.visibleBlocksChanged.emit() def fold(self, start, end, fold=True): """ Fold/Unfold a block of text delimitted by start/end line numbers :param start: Start folding line (this line is not fold, only the next ones) :param end: End folding line. :param fold: True to fold, False to unfold """ doc = self.document() assert isinstance(doc, QTextDocument) for i in range(start + 1, end): block = self.document().findBlockByNumber(i) assert isinstance(block, QTextBlock) block.setVisible(not fold) doc.markContentsDirty(block.position(), block.length()) self.update() self.viewport().repaint() self.__onBlocksChanged()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.current_profileid = "" self.setupUi(self) self.logger = get_logger('desuratools', 'desuratools.log') self.logger.info('Logger Created') boldfont = QApplication.font() boldfont.setBold(True) self.addToSteam_action = self.action_factory("Add to Steam", self.add_to_steam) self.addToSteam_action.setFont(boldfont) self.installGame_action = self.action_factory("Install", self.install_game) self.installGame_action.setFont(boldfont) self.desuraPage_action = self.action_factory("View Profile", self.open_desura_page) self.uninstallGame_action = self.action_factory("Uninstall", self.uninstall_game) self.verifyGame_action = self.action_factory("Verify", self.verify_game) self.ownedGames_menu = QMenu(self) self.ownedGames_menu.addActions([ self.installGame_action, self.desuraPage_action ]) self.installedGames_menu = QMenu(self) self.installedGames_menu.addActions([ self.addToSteam_action, self.desuraPage_action, self.uninstallGame_action, self.verifyGame_action ]) self.selectAllButton.clicked.connect(self.select_all_games) self.desuraAccountName_verify.clicked.connect(self.populate_owned_games) self.installButton.clicked.connect(self.process_install_button) self.generateDesuraReport_action.activated.connect(self.generate_report) self.tabWidget.currentChanged.connect(self.swap_tabs) self.ownedGames_list.itemSelectionChanged.connect(self.update_gameinfo) self.installedGames_list.itemSelectionChanged.connect(self.update_gameinfo) self.refreshButton.clicked.connect(self.refresh_list) self.refreshLists_action.activated.connect(self.refresh_all) self.installedGames_list.customContextMenuRequested.connect(self.show_game_context) self.installedGames_list.doubleClicked.connect(self.add_to_steam) self.installedGames_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.ownedGames_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.steamID_input.addItems(steamutils.get_customurls_on_machine()) self.ownedGames_list.addItem("Verify your Desura username to see your owned games") QApplication.processEvents() self.loading_dialog = ProgressBarDialog() self.loading_dialog.show() QApplication.processEvents() self.populate_installed_games() self.load_data() self.loading_dialog.close() self.raise_() def verify_user(self, profileid=None): if profileid is None: profileid=self.current_profileid if len(profileid) == 0: return False try: username = gameslist.username_from_profile_id(profileid) except gameslist.NoSuchProfileError: return False if windows.desura_running(username): return True verify_dialog = QMessageBox() verify_dialog.setText("<b>Verify your identity</b><br />Sign in to Desura to continue with account <b>{0}</b> to confirm your identity".format(username)) verify_dialog.setInformativeText("<i>Waiting for Desura sign-in...</i>") verify_dialog.setWindowTitle("Sign into Desura to continue") verify_dialog.setStandardButtons(QMessageBox.Cancel) verify_dialog.setIcon(QMessageBox.Information) verify_dialog.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint) desurawaiter = DesuraWaiter(username) desurawaiter.finished.connect(verify_dialog.close) desurawaiter.start() verify_dialog.exec_() if windows.desura_running(username): return True else: desurawaiter.terminate() return False def load_data(self): try: with open(os.path.join(windows.data_dir(), 'desuratools.json'), 'r') as savefile: try: data = json.loads(savefile.read()) except Exception: self.desuraAccountName_input.setText("") return self.desuraAccountName_input.setText(data['desuraname']) if data['desuraname'] != "": self.populate_owned_games() steamid = self.steamID_input.findText(data['steamname']) self.steamID_input.setCurrentIndex(steamid) except IOError: pass def closeEvent(self, *args, **kwargs): self.logger.info("Saving to file") savefile = open(os.path.join(windows.data_dir(), 'desuratools.json'), 'w') savefile.write( json.dumps({ 'desuraname': self.current_profileid, 'steamname': self.steamID_input.currentText() }) ) savefile.close() QApplication.quit() def populate_qlistwidget(self, game, qlistwidget, iconurls=False): if iconurls: itemicon = self.qpixmap_from_url(game.icon) QApplication.processEvents() else: itemicon = QPixmap(game.icon) QApplication.processEvents() item = QListWidgetItem(itemicon, game.name, qlistwidget) item.setData(Qt.UserRole, game) qlistwidget.addItem(item) def populate_owned_games(self): self.statusBar.showMessage("Waiting for Desura... Please Wait") try: if not self.set_current_account(): if len(self.desuraAccountName_input.text()) > 0: self.statusBar.showMessage("Invalid Desura Account") else: self.statusBar.showMessage("") return self.ownedGames_list.clear() self.loading_dialog.setAccount(gameslist.username_from_profile_id(self.current_profileid)) QApplication.processEvents() self.loading_dialog.setMaximum(len(gameslist.GamesList(self.current_profileid).get_games())) QApplication.processEvents() for game in gameslist.GamesList(self.current_profileid).get_games(): self.populate_qlistwidget(game, self.ownedGames_list, True) QApplication.processEvents() self.loading_dialog.increment(1, game.name) QApplication.processEvents() self.logger.info("Added Game {0}".format(game.name)) self.statusBar.showMessage("Added Game {0}".format(game.name)) except gameslist.PrivateProfileError: self.logger.error("Failed to load games - Private Desura Profile") self.statusBar.showMessage("Failed to load games - Private Desura Profiles not supported") error_message( "The Desura Profile {0} is set to Private. <br/>DesuraTools works only with public Desura Profiles." .format(self.current_profileid) ).exec_() return except gameslist.NoSuchProfileError: self.logger.error("Failed to load games - Desura account not found") self.statusBar.showMessage("Failed to load games - Desura account not found") return except gameslist.InvalidDesuraProfileError: self.logger.error("Failed to load games - Desura Profile ID invalid") self.ownedGames_list.customContextMenuRequested.connect(self.show_game_context) self.ownedGames_list.doubleClicked.connect(self.install_game) self.statusBar.showMessage("All owned Desura games loaded for account {0}".format( gameslist.username_from_profile_id(self.current_profileid)) ) self.logger.info("All owned Desura games loaded for Desura profile id {0}".format(self.current_profileid)) def set_current_account(self, profileid=None): if profileid is None: profileid = self.desuraAccountName_input.text() if not self.verify_user(profileid): return False try: gameslist.GamesList(profileid) except gameslist.InvalidDesuraProfileError: raise self.current_profileid = profileid self.logger.info("Set current profileid to {0}".format(self.current_profileid)) self.setWindowTitle("DesuraTools - {0}".format(gameslist.username_from_profile_id(self.current_profileid))) return True def populate_installed_games(self): for game in installedgames.get_games(): self.populate_qlistwidget(game, self.installedGames_list) def swap_tabs(self): gamelist = self.get_current_list() if gamelist[1] == 0: self.installButton.setText("Add Selected to Steam") if gamelist[1] == 1: self.installButton.setText("Install Selected") self.gameIcon_label.clear() self.gameName_label.clear() self.gameShortName_label.clear() def show_game_context(self): gamelist = self.get_current_list() if gamelist[0].itemAt(gamelist[0].mapFromGlobal(QCursor.pos())) is gamelist[0].currentItem(): if gamelist[1] == 0: self.installedGames_menu.exec_(QCursor.pos()) else: self.ownedGames_menu.exec_(QCursor.pos()) def process_install_button(self): gamelist = self.get_current_list() if gamelist[1] == 1: self.install_game() if gamelist[1] == 0: self.add_to_steam() self.get_steam_manager().save() def install_game(self): self.statusBar.showMessage("Sign into Desura to install") if not self.verify_user(): self.statusBar.showMessage("Failed to verify user") return gamelist = self.get_current_list() if gamelist[1] == 1: for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) self.logger.info(' '.join(["Installing", game.name])) self.statusBar.showMessage(' '.join(["Installing", game.name])) game.install() def uninstall_game(self): self.statusBar.showMessage("Sign into Desura to uninstall") if not self.verify_user(): self.statusBar.showMessage("Failed to verify user") return gamelist = self.get_current_list() if gamelist[1] == 0: if len(gamelist[0].selectedItems()) > 1: confirm_uninstall = user_choice( "Are you sure you want to uninstall {0} games?".format(len(gamelist[0].selectedItems())), "Confirm batch uninstallation", QMessageBox.Information, acceptbutton="Uninstall" ) result = confirm_uninstall.exec_() if result is not QMessageBox.AcceptRole: self.logger.info("Uninstall {0} games canceled".format(len(gamelist[0].selectedItems()))) self.statusBar.showMessage("Uninstall {0} games canceled".format(len(gamelist[0].selectedItems()))) return for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) self.logger.info(' '.join(["Uninstalling", game.name])) self.statusBar.showMessage(' '.join(["Uninstalling", game.name])) game.uninstall() def verify_game(self): self.statusBar.showMessage("Sign into Desura to verify") if not self.verify_user(): self.statusBar.showMessage("Failed to verify user") return gamelist = self.get_current_list() if gamelist[1] == 0: for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) self.logger.info(' '.join(["Verifying", game.name])) self.statusBar.showMessage(' '.join(["Verifying", game.name])) game.verify() def add_to_steam(self): if not self.check_if_steam_running(): return gamelist = self.get_current_list() if gamelist[1] == 0: for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) if 'ID64:' in self.steamID_input.currentText(): steamid = long(self.steamID_input.currentText().replace('ID64:', '')) else: steamid = steam_user_manager.communityid64_from_name(self.steamID_input.currentText()) QApplication.processEvents() if not steamutils.check_steam_version(steamid, game.name): if not steamutils.shortcut_exists(self.get_steam_manager(), game.name): steamutils.insert_shortcut(self.get_steam_manager(), game.name, game.exe, icons.choose_icon(game)) self.statusBar.showMessage("Added {0} to the Steam library".format(game.name)) QApplication.processEvents() else: self.statusBar.showMessage("{0} already exists in the Steam library".format(game.name)) QApplication.processEvents() else: self.statusBar.showMessage("Steam account {0} already owns the Steam version of {1}".format( self.steamID_input.currentText(), game.name) ) QApplication.processEvents() def get_steam_manager(self): steamid = steam_user_manager.communityid32_from_name(self.steamID_input.currentText()) vdf = steam_user_manager.shortcuts_file_for_user_id(steamid) return steam_shortcut_manager.SteamShortcutManager(vdf) def update_gameinfo(self): gamelist = self.get_current_list() if len(gamelist[0].selectedItems()) == 1: game = gamelist[0].currentItem().data(Qt.UserRole) self.gameName_label.setText(game.name) self.gameShortName_label.setText(game.shortname) if gamelist[1] == 0: self.gameIcon_label.setPixmap(QPixmap(game.icon)) if gamelist[1] == 1: self.gameIcon_label.setPixmap(self.qpixmap_from_url(game.icon)) else: self.gameName_label.setText("{0} Items Selected".format(str(len(gamelist[0].selectedItems())))) self.gameIcon_label.clear() self.gameShortName_label.clear() def refresh_list(self): gamelist = self.get_current_list() gamelist[0].clear() if gamelist[1] == 0: self.populate_installed_games() if gamelist[1] == 1: self.populate_owned_games() def refresh_all(self): self.installedGames_list.clear() self.ownedGames_list.clear() self.populate_installed_games() self.populate_owned_games() def select_all_games(self): self.get_current_list()[0].selectAll() def open_desura_page(self): gamelist = self.get_current_list() for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) game.storepage() def get_current_list(self): if self.tabWidget.currentIndex() == 0: return self.installedGames_list, 0 if self.tabWidget.currentIndex() == 1: return self.ownedGames_list, 1 def check_if_steam_running(self): if windows.steam_running(): self.statusBar.showMessage("Steam is currently running") ask_close_steam = user_choice( "<b>Steam is currently running</b><br />Please close Steam before adding a game", "Close Steam before continuing", QMessageBox.Warning, acceptbutton="Close Steam" ) result = ask_close_steam.exec_() if result == QMessageBox.AcceptRole: self.logger.info("Waiting for Steam to close") self.statusBar.showMessage("Waiting for Steam to close.. Please wait") windows.close_steam() return True else: self.logger.error("Could not add game to Steam - Steam still running") self.statusBar.showMessage("Could not add game to Steam - Steam still running") return False else: return True def generate_report(self): if len(self.current_profileid) == 0: self.statusBar.showMessage("Please enter your Desura username") return self.logger.info("Generating Report") self.statusBar.showMessage("Generating Report") webbrowser.open(str(DesuraReport(self.current_profileid))) def action_factory(self, text, connect): action = QAction(text, self) action.activated.connect(connect) return action @staticmethod def qpixmap_from_url(url): img_data = urllib.urlopen(url).read() itemicon = QPixmap() itemicon.loadFromData(img_data) return itemicon
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.current_profileid = "" self.setupUi(self) self.logger = get_logger('desuratools', 'desuratools.log') self.logger.info('Logger Created') boldfont = QApplication.font() boldfont.setBold(True) self.addToSteam_action = self.action_factory("Add to Steam", self.add_to_steam) self.addToSteam_action.setFont(boldfont) self.installGame_action = self.action_factory("Install", self.install_game) self.installGame_action.setFont(boldfont) self.desuraPage_action = self.action_factory("View Profile", self.open_desura_page) self.uninstallGame_action = self.action_factory( "Uninstall", self.uninstall_game) self.verifyGame_action = self.action_factory("Verify", self.verify_game) self.ownedGames_menu = QMenu(self) self.ownedGames_menu.addActions( [self.installGame_action, self.desuraPage_action]) self.installedGames_menu = QMenu(self) self.installedGames_menu.addActions([ self.addToSteam_action, self.desuraPage_action, self.uninstallGame_action, self.verifyGame_action ]) self.selectAllButton.clicked.connect(self.select_all_games) self.desuraAccountName_verify.clicked.connect( self.populate_owned_games) self.installButton.clicked.connect(self.process_install_button) self.generateDesuraReport_action.activated.connect( self.generate_report) self.tabWidget.currentChanged.connect(self.swap_tabs) self.ownedGames_list.itemSelectionChanged.connect(self.update_gameinfo) self.installedGames_list.itemSelectionChanged.connect( self.update_gameinfo) self.refreshButton.clicked.connect(self.refresh_list) self.refreshLists_action.activated.connect(self.refresh_all) self.installedGames_list.customContextMenuRequested.connect( self.show_game_context) self.installedGames_list.doubleClicked.connect(self.add_to_steam) self.installedGames_list.setSelectionMode( QAbstractItemView.ExtendedSelection) self.ownedGames_list.setSelectionMode( QAbstractItemView.ExtendedSelection) self.steamID_input.addItems(steamutils.get_customurls_on_machine()) self.ownedGames_list.addItem( "Verify your Desura username to see your owned games") QApplication.processEvents() self.loading_dialog = ProgressBarDialog() self.loading_dialog.show() QApplication.processEvents() self.populate_installed_games() self.load_data() self.loading_dialog.close() self.raise_() def verify_user(self, profileid=None): if profileid is None: profileid = self.current_profileid if len(profileid) == 0: return False try: username = gameslist.username_from_profile_id(profileid) except gameslist.NoSuchProfileError: return False if windows.desura_running(username): return True verify_dialog = QMessageBox() verify_dialog.setText( "<b>Verify your identity</b><br />Sign in to Desura to continue with account <b>{0}</b> to confirm your identity" .format(username)) verify_dialog.setInformativeText( "<i>Waiting for Desura sign-in...</i>") verify_dialog.setWindowTitle("Sign into Desura to continue") verify_dialog.setStandardButtons(QMessageBox.Cancel) verify_dialog.setIcon(QMessageBox.Information) verify_dialog.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint) desurawaiter = DesuraWaiter(username) desurawaiter.finished.connect(verify_dialog.close) desurawaiter.start() verify_dialog.exec_() if windows.desura_running(username): return True else: desurawaiter.terminate() return False def load_data(self): try: with open(os.path.join(windows.data_dir(), 'desuratools.json'), 'r') as savefile: try: data = json.loads(savefile.read()) except Exception: self.desuraAccountName_input.setText("") return self.desuraAccountName_input.setText(data['desuraname']) if data['desuraname'] != "": self.populate_owned_games() steamid = self.steamID_input.findText(data['steamname']) self.steamID_input.setCurrentIndex(steamid) except IOError: pass def closeEvent(self, *args, **kwargs): self.logger.info("Saving to file") savefile = open(os.path.join(windows.data_dir(), 'desuratools.json'), 'w') savefile.write( json.dumps({ 'desuraname': self.current_profileid, 'steamname': self.steamID_input.currentText() })) savefile.close() QApplication.quit() def populate_qlistwidget(self, game, qlistwidget, iconurls=False): if iconurls: itemicon = self.qpixmap_from_url(game.icon) QApplication.processEvents() else: itemicon = QPixmap(game.icon) QApplication.processEvents() item = QListWidgetItem(itemicon, game.name, qlistwidget) item.setData(Qt.UserRole, game) qlistwidget.addItem(item) def populate_owned_games(self): self.statusBar.showMessage("Waiting for Desura... Please Wait") try: if not self.set_current_account(): if len(self.desuraAccountName_input.text()) > 0: self.statusBar.showMessage("Invalid Desura Account") else: self.statusBar.showMessage("") return self.ownedGames_list.clear() self.loading_dialog.setAccount( gameslist.username_from_profile_id(self.current_profileid)) QApplication.processEvents() self.loading_dialog.setMaximum( len(gameslist.GamesList(self.current_profileid).get_games())) QApplication.processEvents() for game in gameslist.GamesList( self.current_profileid).get_games(): self.populate_qlistwidget(game, self.ownedGames_list, True) QApplication.processEvents() self.loading_dialog.increment(1, game.name) QApplication.processEvents() self.logger.info("Added Game {0}".format(game.name)) self.statusBar.showMessage("Added Game {0}".format(game.name)) except gameslist.PrivateProfileError: self.logger.error("Failed to load games - Private Desura Profile") self.statusBar.showMessage( "Failed to load games - Private Desura Profiles not supported") error_message( "The Desura Profile {0} is set to Private. <br/>DesuraTools works only with public Desura Profiles." .format(self.current_profileid)).exec_() return except gameslist.NoSuchProfileError: self.logger.error( "Failed to load games - Desura account not found") self.statusBar.showMessage( "Failed to load games - Desura account not found") return except gameslist.InvalidDesuraProfileError: self.logger.error( "Failed to load games - Desura Profile ID invalid") self.ownedGames_list.customContextMenuRequested.connect( self.show_game_context) self.ownedGames_list.doubleClicked.connect(self.install_game) self.statusBar.showMessage( "All owned Desura games loaded for account {0}".format( gameslist.username_from_profile_id(self.current_profileid))) self.logger.info( "All owned Desura games loaded for Desura profile id {0}".format( self.current_profileid)) def set_current_account(self, profileid=None): if profileid is None: profileid = self.desuraAccountName_input.text() if not self.verify_user(profileid): return False try: gameslist.GamesList(profileid) except gameslist.InvalidDesuraProfileError: raise self.current_profileid = profileid self.logger.info("Set current profileid to {0}".format( self.current_profileid)) self.setWindowTitle("DesuraTools - {0}".format( gameslist.username_from_profile_id(self.current_profileid))) return True def populate_installed_games(self): for game in installedgames.get_games(): self.populate_qlistwidget(game, self.installedGames_list) def swap_tabs(self): gamelist = self.get_current_list() if gamelist[1] == 0: self.installButton.setText("Add Selected to Steam") if gamelist[1] == 1: self.installButton.setText("Install Selected") self.gameIcon_label.clear() self.gameName_label.clear() self.gameShortName_label.clear() def show_game_context(self): gamelist = self.get_current_list() if gamelist[0].itemAt(gamelist[0].mapFromGlobal( QCursor.pos())) is gamelist[0].currentItem(): if gamelist[1] == 0: self.installedGames_menu.exec_(QCursor.pos()) else: self.ownedGames_menu.exec_(QCursor.pos()) def process_install_button(self): gamelist = self.get_current_list() if gamelist[1] == 1: self.install_game() if gamelist[1] == 0: self.add_to_steam() self.get_steam_manager().save() def install_game(self): self.statusBar.showMessage("Sign into Desura to install") if not self.verify_user(): self.statusBar.showMessage("Failed to verify user") return gamelist = self.get_current_list() if gamelist[1] == 1: for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) self.logger.info(' '.join(["Installing", game.name])) self.statusBar.showMessage(' '.join(["Installing", game.name])) game.install() def uninstall_game(self): self.statusBar.showMessage("Sign into Desura to uninstall") if not self.verify_user(): self.statusBar.showMessage("Failed to verify user") return gamelist = self.get_current_list() if gamelist[1] == 0: if len(gamelist[0].selectedItems()) > 1: confirm_uninstall = user_choice( "Are you sure you want to uninstall {0} games?".format( len(gamelist[0].selectedItems())), "Confirm batch uninstallation", QMessageBox.Information, acceptbutton="Uninstall") result = confirm_uninstall.exec_() if result is not QMessageBox.AcceptRole: self.logger.info("Uninstall {0} games canceled".format( len(gamelist[0].selectedItems()))) self.statusBar.showMessage( "Uninstall {0} games canceled".format( len(gamelist[0].selectedItems()))) return for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) self.logger.info(' '.join(["Uninstalling", game.name])) self.statusBar.showMessage(' '.join( ["Uninstalling", game.name])) game.uninstall() def verify_game(self): self.statusBar.showMessage("Sign into Desura to verify") if not self.verify_user(): self.statusBar.showMessage("Failed to verify user") return gamelist = self.get_current_list() if gamelist[1] == 0: for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) self.logger.info(' '.join(["Verifying", game.name])) self.statusBar.showMessage(' '.join(["Verifying", game.name])) game.verify() def add_to_steam(self): if not self.check_if_steam_running(): return gamelist = self.get_current_list() if gamelist[1] == 0: for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) if 'ID64:' in self.steamID_input.currentText(): steamid = long(self.steamID_input.currentText().replace( 'ID64:', '')) else: steamid = steam_user_manager.communityid64_from_name( self.steamID_input.currentText()) QApplication.processEvents() if not steamutils.check_steam_version(steamid, game.name): if not steamutils.shortcut_exists(self.get_steam_manager(), game.name): steamutils.insert_shortcut(self.get_steam_manager(), game.name, game.exe, icons.choose_icon(game)) self.statusBar.showMessage( "Added {0} to the Steam library".format(game.name)) QApplication.processEvents() else: self.statusBar.showMessage( "{0} already exists in the Steam library".format( game.name)) QApplication.processEvents() else: self.statusBar.showMessage( "Steam account {0} already owns the Steam version of {1}" .format(self.steamID_input.currentText(), game.name)) QApplication.processEvents() def get_steam_manager(self): steamid = steam_user_manager.communityid32_from_name( self.steamID_input.currentText()) vdf = steam_user_manager.shortcuts_file_for_user_id(steamid) return steam_shortcut_manager.SteamShortcutManager(vdf) def update_gameinfo(self): gamelist = self.get_current_list() if len(gamelist[0].selectedItems()) == 1: game = gamelist[0].currentItem().data(Qt.UserRole) self.gameName_label.setText(game.name) self.gameShortName_label.setText(game.shortname) if gamelist[1] == 0: self.gameIcon_label.setPixmap(QPixmap(game.icon)) if gamelist[1] == 1: self.gameIcon_label.setPixmap(self.qpixmap_from_url(game.icon)) else: self.gameName_label.setText("{0} Items Selected".format( str(len(gamelist[0].selectedItems())))) self.gameIcon_label.clear() self.gameShortName_label.clear() def refresh_list(self): gamelist = self.get_current_list() gamelist[0].clear() if gamelist[1] == 0: self.populate_installed_games() if gamelist[1] == 1: self.populate_owned_games() def refresh_all(self): self.installedGames_list.clear() self.ownedGames_list.clear() self.populate_installed_games() self.populate_owned_games() def select_all_games(self): self.get_current_list()[0].selectAll() def open_desura_page(self): gamelist = self.get_current_list() for item in gamelist[0].selectedItems(): game = item.data(Qt.UserRole) game.storepage() def get_current_list(self): if self.tabWidget.currentIndex() == 0: return self.installedGames_list, 0 if self.tabWidget.currentIndex() == 1: return self.ownedGames_list, 1 def check_if_steam_running(self): if windows.steam_running(): self.statusBar.showMessage("Steam is currently running") ask_close_steam = user_choice( "<b>Steam is currently running</b><br />Please close Steam before adding a game", "Close Steam before continuing", QMessageBox.Warning, acceptbutton="Close Steam") result = ask_close_steam.exec_() if result == QMessageBox.AcceptRole: self.logger.info("Waiting for Steam to close") self.statusBar.showMessage( "Waiting for Steam to close.. Please wait") windows.close_steam() return True else: self.logger.error( "Could not add game to Steam - Steam still running") self.statusBar.showMessage( "Could not add game to Steam - Steam still running") return False else: return True def generate_report(self): if len(self.current_profileid) == 0: self.statusBar.showMessage("Please enter your Desura username") return self.logger.info("Generating Report") self.statusBar.showMessage("Generating Report") webbrowser.open(str(DesuraReport(self.current_profileid))) def action_factory(self, text, connect): action = QAction(text, self) action.activated.connect(connect) return action @staticmethod def qpixmap_from_url(url): img_data = urllib.urlopen(url).read() itemicon = QPixmap() itemicon.loadFromData(img_data) return itemicon
def showIndividualContextMenu(self, person): actionLookup = {} m = QMenu() actionLookup['Show Details'] = m.addAction('Show Details') # TODO: Add to / remove from path targets m.addSeparator() a = m.addMenu('Set A as') b = m.addMenu('Set B as') for label1 in AppState.ADDITION_ITERATOR_ORDER: a2 = a.addMenu(label1) b2 = b.addMenu(label1) for label2 in AppState.LEVEL_OPTION_ORDER: actionLookup[('Set A as', self.additionIterators[label1], AppState.LEVEL_OPTIONS[label2])] = a2.addAction(label2) actionLookup[('Set B as', self.additionIterators[label1], AppState.LEVEL_OPTIONS[label2])] = b2.addAction(label2) m.addSeparator() actionLookup['Trim Parents'] = m.addAction('Trim Parents') actionLookup['Trim Spouses'] = m.addAction('Trim Spouses') actionLookup['Trim Children'] = m.addAction('Trim Children') if not person in self.aSet and not person in self.bSet: actionLookup['Trim Parents'].setDisabled(True) actionLookup['Trim Spouses'].setDisabled(True) actionLookup['Trim Children'].setDisabled(True) m.addSeparator() for label1 in AppState.ADDITION_ITERATOR_ORDER: temp = m.addMenu('Expand '+label1) for label2 in AppState.LEVEL_OPTION_ORDER: actionLookup[('Expand', self.additionIterators[label1], AppState.LEVEL_OPTIONS[label2])] = temp.addAction(label2) choice = m.exec_(QCursor.pos()) if choice != None: for menus,action in actionLookup.iteritems(): if action == choice: if menus == 'Show Details': self.showIndividualDetails(person) elif menus == 'Trim Parents': self.snip(person, [self.ped.CHILD_TO_PARENT]) elif menus == 'Trim Spouses': self.snip(person, [self.ped.HUSBAND_TO_WIFE, self.ped.WIFE_TO_HUSBAND]) elif menus == 'Trim Children': self.snip(person, [self.ped.PARENT_TO_CHILD]) else: assert isinstance(menus,tuple) newSet = set(menus[1](person,level=menus[2])) if menus[0] == 'Set A as': historyID = self.addPedigree(newSet) self.changePedigreeA(historyID) elif menus[0] == 'Set B as': historyID = self.addPedigree(newSet) self.changePedigreeB(historyID) elif menus[0] == 'Expand': self.expand(person, newSet) break
class PresenceOverviewWidget(HorsePanel): @Slot(QModelIndex) def cell_entered(self, ndx): employee_id = self._employee_id_on_row(ndx) if not employee_id: self._show_totals_day_off(None) elif employee_id: chrono.chrono_start() employee = None for i in self.employees: if i.employee_id == employee_id: employee = i break self.detail_subframe.set_title(employee.fullname) self._show_totals_day_off(employee_id) d = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, ndx.column()))) chrono.chrono_click("Retrieveing data") tars = dao.task_action_report_dao.get_reports_for_employee_id_on_date( employee_id, d) work_timetracks = dao.timetrack_dao.all_work_for_employee_date_manual( employee_id, d) presence_timetracks = dao.timetrack_dao.all_presence_for_employee_date_managed_by_code_full( employee_id, d) # employee = dao.employee_dao.find_by_id(employee_id) special_activities = dao.special_activity_dao.find_on_day( employee_id, d) chrono.chrono_click("Redrawing") self.time_report_view.redraw(datetime(d.year, d.month, d.day, 6, 0), tars, employee_id, work_timetracks, presence_timetracks, special_activities, view_title=_("Work on {}").format( date_to_dmy(d, full_month=True))) session().close( ) # FIXME Put his one line above; but that's tough ! SQLA doesn't help us much here ! chrono.chrono_click("Session closed") # for event_type, duration in self.day_event_totals[employee_id].items(): # mainlog.debug("{}{}".format(event_type, duration)) self._toggle_days_off_actions() def _employee_id_on_row(self, row_or_ndx): r = row_or_ndx if type(r) != int: r = row_or_ndx.row() return self._table_model.data(self._table_model.index(r, 0), Qt.UserRole) DAY_EVENT_PALETTE = { DayEventType.holidays: (Qt.GlobalColor.white, Qt.GlobalColor.red), DayEventType.day_off: (Qt.GlobalColor.white, Qt.GlobalColor.darkRed), DayEventType.unpaid_day_off: (Qt.GlobalColor.black, Qt.GlobalColor.magenta), DayEventType.free_day: (Qt.GlobalColor.white, Qt.GlobalColor.darkMagenta), DayEventType.overtime: (Qt.GlobalColor.black, Qt.GlobalColor.green), DayEventType.recuperation: (Qt.GlobalColor.white, Qt.GlobalColor.darkGreen), DayEventType.unemployment: (Qt.GlobalColor.white, Qt.GlobalColor.blue), DayEventType.unemployment_short: (Qt.GlobalColor.white, Qt.GlobalColor.darkBlue), DayEventType.work_accident: (Qt.GlobalColor.black, Qt.GlobalColor.yellow), DayEventType.sick_leave: (Qt.GlobalColor.white, Qt.GlobalColor.darkYellow) } MONTH_EVENT_COLUMN = 2 YEAR_EVENT_COLUMN = 3 def _make_total_days_off_panel(self): widget = QFrame() widget.setObjectName('HorseRegularFrame') widget.setFrameShape(QFrame.Panel) widget.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout() #layout.addWidget(QLabel(_("Days off to date"))) self.day_off_total_duration_labels = dict() self.day_off_month_duration_labels = dict() self.day_off_labels = dict() self._day_off_table_model = QStandardItemModel(10, 3) self._day_off_table_model.setHorizontalHeaderLabels( [None, None, _("This\nmonth"), _("Before")]) self.day_off_table_view = QTableView(None) self.day_off_table_view.setModel(self._day_off_table_model) # self.day_off_table_view.setHorizontalHeader(self.headers_view) self.day_off_table_view.verticalHeader().hide() self.day_off_table_view.setAlternatingRowColors(True) self.day_off_table_view.setEditTriggers( QAbstractItemView.NoEditTriggers) self.day_off_table_view.hide() row = 0 for det in DayEventType.symbols(): ndx = self._day_off_table_model.index(row, 0) self._day_off_table_model.setData(ndx, det.description, Qt.DisplayRole) ndx = self._day_off_table_model.index(row, 1) fg, bg = self.DAY_EVENT_PALETTE[det] self._day_off_table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole) self._day_off_table_model.setData(ndx, QBrush(fg), Qt.TextColorRole) self._day_off_table_model.setData(ndx, DayEventType.short_code(det), Qt.DisplayRole) row += 1 layout.addWidget(self.day_off_table_view) grid = QGridLayout() self.days_off_layout = grid grid.setColumnStretch(3, 1) row = 0 grid.addWidget(QLabel(_('Year')), row, self.YEAR_EVENT_COLUMN) grid.addWidget(QLabel(_('Month')), row, self.MONTH_EVENT_COLUMN) row += 1 for det in DayEventType.symbols(): self.day_off_total_duration_labels[det] = QLabel("-") self.day_off_month_duration_labels[det] = QLabel("-") self.day_off_labels[det] = QLabel(det.description) hlayout = QHBoxLayout() sl = QLabel() fg, bg = self.DAY_EVENT_PALETTE[det] def to_html_rgb(color): i = color.red() * 256 * 256 + color.green() * 256 + color.blue( ) return "#{:06X}".format(i) p = QPalette() p.setColor(QPalette.Window, QColor(bg)) p.setColor(QPalette.WindowText, QColor(fg)) sl.setPalette(p) sl.setAlignment(Qt.AlignCenter) sl.setStyleSheet("border: 2px solid black; background: {}".format( to_html_rgb(QColor(bg)))) t = DayEventType.short_code(det) mainlog.debug(t) sl.setAutoFillBackground(True) sl.setText(t) grid.addWidget(sl, row, 0) grid.addWidget(self.day_off_labels[det], row, 1) grid.addWidget(self.day_off_total_duration_labels[det], row, self.YEAR_EVENT_COLUMN) grid.addWidget(self.day_off_month_duration_labels[det], row, self.MONTH_EVENT_COLUMN) hlayout.addStretch() row += 1 layout.addLayout(grid) layout.addStretch() self.day_off_table_view.resizeColumnsToContents() # self.day_off_table_view.setMinimumWidth( self.day_off_table_view.width()) # self.day_off_table_view.resize( self.day_off_table_view.minimumWidth(), # self.day_off_table_view.minimumHeight(),) widget.setLayout(layout) return widget def _show_totals_day_off(self, employee_id): mainlog.debug("_show_totals_day_off : {}".format(employee_id)) def form_layout_row_set_visible(layout, row_ndx, is_visible): for i in range(layout.columnCount()): l = layout.itemAtPosition(row_ndx, i) if l and l.widget(): l.widget().setVisible(is_visible) det_to_show = dict() row = 0 for det in DayEventType.symbols(): yearly = 0 if employee_id in self.all_events_in_year: if det in self.all_events_in_year[employee_id]: yearly = nice_round( self.all_events_in_year[employee_id][det]) monthly = 0 if employee_id in self.day_event_totals: if det in self.day_event_totals[employee_id]: monthly = nice_round( self.day_event_totals[employee_id][det]) # ndx = self._day_off_table_model.index(row,self.YEAR_EVENT_COLUMN) # self._day_off_table_model.setData(ndx, v, Qt.DisplayRole) if yearly or monthly: det_to_show[det] = {'monthly': monthly, 'yearly': yearly} if det_to_show: # If there are some days spent on some counters, then we display # those counters *only* mainlog.debug("_show_totals_day_off : showing some events ".format( str(det_to_show))) row = 0 for det in DayEventType.symbols(): if det in det_to_show: monthly = det_to_show[det]['monthly'] yearly = det_to_show[det]['yearly'] form_layout_row_set_visible(self.days_off_layout, row + 1, True) self.day_off_total_duration_labels[det].setText(yearly or '-') self.day_off_month_duration_labels[det].setText(monthly or '-') else: form_layout_row_set_visible(self.days_off_layout, row + 1, False) else: # If there are no days spent on any counter, then we display # all counters at the 0 mark. mainlog.debug("_show_totals_day_off : showing no event") row = 0 for det in DayEventType.symbols(): form_layout_row_set_visible(self.days_off_layout, row + 1, True) row += 1 # self.day_off_table_view.resizeColumnsToContents() self.days_off_panel.parent().update() @Slot() def refresh_action(self): global dao # mainlog.debug("refresh action started") self.hours_per_pers_subframe.set_title(date_to_my( self.base_date, True)) chrono.chrono_start() all_events_in_month = people_admin_service.events_for_month( self.base_date) employee_with_events = [ event.employee_id for event in all_events_in_month ] # mainlog.debug(all_events_in_month) self.all_events_in_year = people_admin_service.events_for_year( self.base_date.year) self.all_presences = all_presences = dao.employee_dao.presence_overview_for_month( self.base_date) all_correction_times = dict() for s in dao.month_time_synthesis_dao.load_all_synthesis( self.base_date.year, self.base_date.month): all_correction_times[s.employee_id] = s.correction_time special_activities = dao.special_activity_dao.find_on_month( self.base_date) employees = list( filter( lambda e: e.is_active or e.employee_id in all_presences or e. employee_id in all_correction_times or e.employee_id in special_activities or e.employee_id in employee_with_events, dao.employee_dao.list_overview())) self.employees = employees chrono.chrono_click() day_max = calendar.monthrange(self.base_date.year, self.base_date.month)[1] t_start = datetime(self.base_date.year, self.base_date.month, 1) t_end = datetime(self.base_date.year, self.base_date.month, day_max, 23, 59, 59, 999999) self._table_model.setRowCount(len(employees)) self._table_model.setColumnCount(1 + day_max + 3) headers = QStandardItemModel(1, 1 + day_max + 3) headers.setHeaderData(0, Qt.Orientation.Horizontal, _("Employee")) for i in range(day_max): headers.setHeaderData(i + 1, Qt.Orientation.Horizontal, "{}".format(i + 1)) headers.setHeaderData(day_max + 1, Qt.Orientation.Horizontal, _("Correction")) headers.setHeaderData(day_max + 2, Qt.Orientation.Horizontal, _("Total")) headers.setHeaderData(day_max + 3, Qt.Orientation.Horizontal, _("Days off")) self.headers_view.setModel( headers) # qt's doc : The view does *not* take ownership self.header_model = headers self.headers_view.setModel( self.header_model) # qt's doc : The view does *not* take ownership # Compute all mondays indices monday = 0 if t_start.weekday() > 0: monday = 7 - t_start.weekday() all_mondays = [] while monday < day_max: all_mondays.append(monday) monday += 7 today = date.today() # mainlog.debug("Running on employees") for row in range(self._table_model.rowCount()): # Clear the line for col in range(0, 32): ndx = self._table_model.index(row, col) self._table_model.setData(ndx, None, Qt.BackgroundRole) self._table_model.setData(ndx, QBrush(Qt.GlobalColor.black), Qt.TextColorRole) self._table_model.setData(ndx, None, Qt.DisplayRole) self._table_model.setData(ndx, None, Qt.UserRole) self._table_model.setData(ndx, None, Qt.UserRole + 1) # else: # self._table_model.setData(ndx,None,Qt.BackgroundRole) # else: # self._table_model.setData(ndx,None,Qt.DisplayRole) # self._table_model.setData(ndx,None,Qt.BackgroundRole) # Mark mondays for col in all_mondays: # col + 1 to account for the employee column self._table_model.setData( self._table_model.index(row, col + 1), QBrush(QColor(230, 230, 255)), Qt.BackgroundRole) # Mark today if today.month == self.base_date.month and today.year == self.base_date.year: self._table_model.setData( self._table_model.index(row, today.day), QBrush(QColor(255, 255, 128)), Qt.BackgroundRole) row = 0 for employee in employees: # employees are sorted self._table_model.setData(self._table_model.index(row, 0), employee.fullname, Qt.DisplayRole) # FIXME Use a delegate self._table_model.setData(self._table_model.index(row, 0), employee.employee_id, Qt.UserRole) # FIXME Use a delegate correction = 0 if employee.employee_id in all_correction_times: correction = all_correction_times[employee.employee_id] self._table_model.setData( self._table_model.index(row, day_max + 1), duration_to_hm(correction, short_unit=True), Qt.DisplayRole) else: self._table_model.setData( self._table_model.index(row, day_max + 1), None, Qt.DisplayRole) presence = 0 if employee.employee_id in all_presences and len( all_presences[employee.employee_id]) > 0: import functools presence = functools.reduce( lambda acc, s: acc + s, all_presences[employee.employee_id], 0) presence += correction if presence != 0: self._table_model.setData(ndx, QBrush(Qt.GlobalColor.black), Qt.TextColorRole) self._table_model.setData( self._table_model.index(row, day_max + 2), duration_to_hm(presence, short_unit=True), Qt.DisplayRole) else: self._table_model.setData( self._table_model.index(row, day_max + 2), None, Qt.DisplayRole) if employee.employee_id in all_presences and len( all_presences[employee.employee_id]) > 0: for b in range(len(all_presences[employee.employee_id])): ndx = self._table_model.index(row, b + 1) p = all_presences[employee.employee_id][b] if p > 0: self._table_model.setData( ndx, duration_to_hm(p, short_unit=True), Qt.DisplayRole) self._table_model.setData(ndx, p, Qt.UserRole) if p >= 4 and p <= 8: # Regular work load self._table_model.setData( ndx, QBrush(QColor(192, 255, 192)), Qt.BackgroundRole) elif p > 8 or (p < 4 and p > 0): # Problematic work load self._table_model.setData( ndx, QBrush(QColor(255, 192, 192)), Qt.BackgroundRole) if employee.employee_id in special_activities: sa_of_employee = special_activities[employee.employee_id] for sa in sa_of_employee: start = max(t_start, sa.start_time) end = min(t_end, sa.end_time) for i in range(start.day, end.day + 1): ndx = self._table_model.index(row, i) self._table_model.setData(ndx, QBrush(QColor(255, 128, 0)), Qt.BackgroundRole) # self._table_model.setData(self._table_model.index(row,b+1),Qt.AlignRight | Qt.AlignVCenter,Qt.TextAlignmentRole) row += 1 # Display day events employee_id_to_row = dict() # little accelerator for row in range(len(employees)): employee_id_to_row[employees[row].employee_id] = row # Compute days off totals and show them self.day_event_totals = dict([(e.employee_id, dict()) for e in employees]) for day_event in all_events_in_month: # mainlog.debug("employee_id = {}".format(day_event.employee_id)) # if day_event.employee_id not in self.day_event_totals: # mainlog.debug(self.day_event_totals) t = self.day_event_totals[day_event.employee_id] if day_event.event_type not in t: t[day_event.event_type] = day_event.duration else: t[day_event.event_type] += day_event.duration for employee in employees: # employees are sorted t = self.day_event_totals[employee.employee_id] mainlog.debug(t) total_off = sum(t.values()) mainlog.debug(total_off) row = employee_id_to_row[employee.employee_id] mainlog.debug(row) if total_off: self._table_model.setData( self._table_model.index(row, day_max + 3), nice_round(total_off), Qt.DisplayRole) else: self._table_model.setData( self._table_model.index(row, day_max + 3), None, Qt.DisplayRole) # Show days off for day_event in all_events_in_month: row = employee_id_to_row[day_event.employee_id] col = day_event.date.day fg = bg = None if day_event.event_type in self.DAY_EVENT_PALETTE: fg, bg = self.DAY_EVENT_PALETTE[day_event.event_type] else: fg, bg = Qt.GlobalColor.red, Qt.GlobalColor.gray ndx = self._table_model.index(row, col) self._table_model.setData(ndx, day_event.day_event_id, Qt.UserRole + 1) # The problem here is to nicely blend the fact # that you can have a day event mixed with actual work # the very same day. Here's a poor man solution. active_time = self._table_model.data(ndx, Qt.UserRole) if not active_time: self._table_model.setData( ndx, DayEventType.short_code(day_event.event_type), Qt.DisplayRole) self._table_model.setData(ndx, QBrush(fg), Qt.TextColorRole) self._table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole) else: self._table_model.setData(ndx, QBrush(fg), Qt.TextColorRole) self._table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole) self._table_model.setData( ndx, duration_to_hm(active_time, short_unit=True) + DayEventType.short_code(day_event.event_type), Qt.DisplayRole) chrono.chrono_click() #for i in range(len(all_mondays)): self.table_view.resizeColumnsToContents() # mainlog.debug("Reset selection") ndx = self.table_view.currentIndex() self.table_view.selectionModel().clear() # self.table_view.selectionModel().clearSelection() # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select) # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select) self.table_view.selectionModel().setCurrentIndex( self.table_view.model().index(ndx.row(), ndx.column()), QItemSelectionModel.Select) self.cell_entered(self.table_view.currentIndex()) @Slot() def edit_tars(self): employee_id = self._employee_id_on_row(self.table_view.currentIndex()) d = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, ndx.column()))) dialog = TimeReportingScannerDialog(self) dialog.set_data(datetime(d.year, d.month, d.day, 6, 0), employee_id) dialog.exec_() if dialog.result() == QDialog.Accepted: # pub.sendMessage('time_report.changed') self.timetrack_changed.emit() self.refresh_action() @Slot() def show_actions(self): button = self.action_menu.parent() p = button.mapToGlobal(QPoint(0, button.height())) self.action_menu.exec_(p) @Slot() def delete_holidays(self): ndx = self.table_view.currentIndex() employee_id = self._employee_id_on_row(ndx) d = date(self.base_date.year, self.base_date.month, ndx.column()) if dao.special_activity_dao.delete_by_employee_and_date( employee_id, d): self.refresh_panel() @Slot() def create_holidays(self): employee_id = self._employee_id_on_row(self.table_view.currentIndex()) left_col = 1000 right_col = 0 for ndx in self.table_view.selectionModel().selectedIndexes(): c = ndx.column() left_col = min(c, left_col) right_col = max(c, right_col) d_start = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, left_col))) d_end = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, right_col))) dialog = HolidaysDialog(self) sa = SpecialActivity() sa.employee_id = employee_id sa.reporter_id = user_session.user_id sa.encoding_date = date.today() sa.start_time = datetime(d_start.year, d_start.month, d_start.day, 6, 0) sa.end_time = datetime(d_end.year, d_end.month, d_end.day, 14, 0) dialog.setup(sa, dao.employee_dao.find_by_id(employee_id).fullname) # dialog.set_data(employee,self.base_date,c) dialog.exec_() if dialog.result() == QDialog.Accepted: dao.special_activity_dao.save(sa) self.refresh_action() @Slot() def edit_month_correction(self): employee_id = self._employee_id_on_row(self.table_view.currentIndex()) if employee_id: employee = dao.employee_dao.find_by_id(employee_id) c = dao.month_time_synthesis_dao.load_correction_time( employee_id, self.base_date.year, self.base_date.month) dialog = MonthTimeCorrectionDialog(self) dialog.set_data(employee.fullname, self.base_date, c) dialog.exec_() if dialog.result() == QDialog.Accepted: c = dao.month_time_synthesis_dao.save(employee_id, self.base_date.year, self.base_date.month, dialog.correction_time) self.refresh_action() @Slot() def month_today(self): self.base_date = date.today() self.refresh_action() @Slot() def month_before(self): m = self.base_date.month if m > 1: self.base_date = date(self.base_date.year, m - 1, 1) else: self.base_date = date(self.base_date.year - 1, 12, 1) self.refresh_action() @Slot() def month_after(self): m = self.base_date.month if self.base_date.year < date.today().year + 1 \ or m < date.today().month: if m < 12: self.base_date = date(self.base_date.year, m + 1, 1) else: self.base_date = date(self.base_date.year + 1, 1, 1) self.refresh_action() @Slot() def edit_timetrack_no_ndx(self): ndx = self.table_view.currentIndex() if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0: self.edit_timetrack(ndx) else: showWarningBox(_("Can't edit"), _("You must first select a day/person.")) timetrack_changed = Signal() @Slot(QModelIndex) def edit_timetrack(self, ndx): global dao global user_session if ndx.column() >= 1: edit_date = date( self.base_date.year, self.base_date.month, ndx.column()) # +1 already in because of employee's names employee_id = self._employee_id_on_row(ndx) tars = dao.task_action_report_dao.get_reports_for_employee_id_on_date( employee_id, edit_date) if len(tars) == 0: d = EditTimeTracksDialog(self, dao, edit_date) d.set_employee_and_date(employee_id, edit_date) d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() self.timetrack_changed.emit() d.deleteLater() else: edit_date = datetime(self.base_date.year, self.base_date.month, ndx.column(), hour=6) from koi.TimeReportingScanner import TimeReportingScannerDialog d = TimeReportingScannerDialog(self) d.set_data(edit_date, employee_id) # or 16 d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() self.timetrack_changed.emit() d.deleteLater() @Slot() def editTaskActionReports(self): if not user_session.has_any_roles(['TimeTrackModify']): return m = self.base_date.month ndx = self.table_view.currentIndex() if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0: edit_date = date( self.base_date.year, m, ndx.column()) # +1 already in because of employee's names employee = self._table_model.data( self._table_model.index(ndx.row(), 0), Qt.UserRole) # FIXME Use a delegate d = EditTaskActionReportsDialog(dao, self, edit_date) d.set_employee_date(employee, edit_date) d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() d.deleteLater() else: showWarningBox(_("Can't edit"), _("You must first select a day/person.")) # @Slot(QModelIndex) # def timetrack_changed(self,ndx): # selected_timetrack = self.controller.model.object_at(ndx) # # Update the colors in the timetrack views # # to show what action reports correspond to the # # selected timetrack # self.controller_actions.model.current_timetrack = selected_timetrack # self.controller_actions.model.beginResetModel() # self.controller_actions.model.endResetModel() # # Make sure the first of the action reports is shown in the # # table # action_reports = self.controller_actions.model.objects # for i in range(len(action_reports)-1,-1,-1): # if action_reports[i] and action_reports[i].timetrack == selected_timetrack: # self.controller_actions.view.scrollTo(self.controller_actions.model.index(i,0)) # break def _make_table_header(self): pass def __init__(self, parent, find_order_action_slot): super(PresenceOverviewWidget, self).__init__(parent) self.set_panel_title(_("Presence overview")) self.base_date = date.today() headers = QStandardItemModel(1, 31 + 3) self._table_model = QStandardItemModel(1, 31 + 3, None) self.headers_view = QHeaderView(Qt.Orientation.Horizontal, self) self.header_model = headers self.headers_view.setResizeMode(QHeaderView.ResizeToContents) self.headers_view.setModel( self.header_model) # qt's doc : The view does *not* take ownership self.table_view = TableViewSignaledEvents(None) self.table_view.setModel(self._table_model) self.table_view.setHorizontalHeader(self.headers_view) self.table_view.verticalHeader().hide() self.table_view.setAlternatingRowColors(True) self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_view.setContextMenuPolicy(Qt.CustomContextMenu) self.table_view.customContextMenuRequested.connect( self.popup_context_menu) self.copy_action = QAction(_("Copy order parts"), self.table_view) self.copy_action.triggered.connect(self.copy_slot) self.copy_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_C)) self.copy_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.table_view.addAction(self.copy_action) self.select_all_action = QAction(_("Select all"), self.table_view) self.select_all_action.triggered.connect(self.select_all_slot) self.select_all_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_A)) self.select_all_action.setShortcutContext( Qt.WidgetWithChildrenShortcut) self.table_view.addAction(self.select_all_action) # self.table_view.setSelectionBehavior(QAbstractItemView.SelectItems) # self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) navbar = NavBar(self, [(_("Month before"), self.month_before), (_("Today"), self.month_today), (_("Action"), self.show_actions), (_("Month after"), self.month_after), (_("Find"), find_order_action_slot)]) self.action_menu = QMenu(navbar.buttons[2]) navbar.buttons[2].setObjectName("specialMenuButton") navbar.buttons[4].setObjectName("specialMenuButton") self._make_days_off_menu_and_action_group() list_actions = [ # (_("Edit"),self.edit_tars, None, None), (_("Edit"), self.edit_timetrack_no_ndx, None, None), (_("Month correction"), self.edit_month_correction, None, [RoleType.modify_monthly_time_track_correction]), (self.days_off_menu, None), (self.copy_action, None), (self.select_all_action, None) ] # (_("Insert holidays"),self.create_holidays, None, None), # (_("Delete holidays"),self.delete_holidays, None, None) ] populate_menu(self.action_menu, self, list_actions) # mainlog.debug("tile widget") self.title_box = TitleWidget(_("Presence Overview"), self, navbar) self.vlayout = QVBoxLayout(self) self.vlayout.setObjectName("Vlayout") self.vlayout.addWidget(self.title_box) self.hours_per_pers_subframe = SubFrame(_("Overview"), self.table_view, self) self.vlayout.addWidget(self.hours_per_pers_subframe) self.time_report_view = TimeReportView(self) self.days_off_panel = self._make_total_days_off_panel() vbox = QVBoxLayout() vbox.addWidget(self.days_off_panel) vbox.addStretch() vbox.setStretch(0, 0) vbox.setStretch(1, 1) hlayout = QHBoxLayout() hlayout.addWidget(self.time_report_view) hlayout.addLayout(vbox) hlayout.setStretch(0, 1) self.detail_subframe = SubFrame(_("Day"), hlayout, self) self.vlayout.addWidget(self.detail_subframe) self.setLayout(self.vlayout) # dbox = QVBoxLayout() # dbox.addWidget(QLabel("kjkljkj")) # self.total_active_hours = LabeledValue(_("Total activity")) # dbox.addWidget(self.total_active_hours) # hbox = QHBoxLayout() # hbox.addWidget(self.table_view) # hbox.addLayout(dbox) # self.selection_model = self.table_view.selectionModel() # mainlog.debug(m) #sm = QItemSelectionModel(self.table_view.model()) #sm.setModel(self.table_view.model()) # self.table_view.setSelectionModel(self.selection_model) self.table_view.selectionModel().currentChanged.connect( self.cell_entered) self.table_view.doubleClickedCell.connect(self.edit_timetrack) def _selection_to_period(self): left_col = 1000 right_col = 0 for ndx in self.table_view.selectionModel().selectedIndexes(): c = ndx.column() left_col = min(c, left_col) right_col = max(c, right_col) d_start = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, left_col))) d_end = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, right_col))) return d_start, d_end def _toggle_days_off_actions(self): day_max = calendar.monthrange(self.base_date.year, self.base_date.month)[1] ndx = self.table_view.currentIndex() can_add = can_remove = False if ndx.column() >= 1 and ndx.column() <= day_max: day_event_id = ndx.data(Qt.UserRole + 1) can_add = True # if not day_event_id: # day = ndx.column() - 1 # # employee_id = self._employee_id_on_row(ndx) # if employee_id in self.all_presences: # if not self.all_presences[employee_id][day]: # can_add = True # else: # can_add = True # else: # can_add = True can_remove = day_event_id is not None self.days_off_add_submenu.setEnabled(can_add) for actions in self.days_off_action_group.actions(): actions.setEnabled(can_add) self.day_off_remove_action.setEnabled(can_remove) def _add_day_off(self, action): if action.data() != 'Remove': day_event_type, day_event_duration = action.data() day_event_type = DayEventType.from_str(day_event_type) mainlog.debug("selected action {} {}".format( day_event_type, day_event_duration)) ndx = self.table_view.currentIndex() day_event_id = ndx.data(Qt.UserRole + 1) # if day_event_id: # showWarningBox(_("There's already a day off here")) # return employee_id = self._employee_id_on_row(ndx) if employee_id in self.all_presences: day = ndx.column() - 1 mainlog.debug("_add_day_off : employee_id={}, day={}".format( employee_id, day)) mainlog.debug(self.all_presences[employee_id]) mainlog.debug(type(self.all_presences[employee_id])) # if self.all_presences[employee_id][day]: # showWarningBox(_("One can't add day off where there is activity")) # return else: mainlog.debug( "_add_day_off : employee_id={} not yet known".format( employee_id)) day_event = DayEvent() day_event.employee_id = employee_id day_event.event_type = day_event_type day, last_day = self._selection_to_period() if day_event_duration in (0.5, 1): days_durations = [(day, day_event_duration)] else: days_durations = [] day, last_day = self._selection_to_period() while day <= last_day: days_durations.append( (day, 1)) # One full work day on the day day += timedelta(1) # mainlog.debug(days_durations) mainlog.debug("Creating day event of type {}".format( day_event.event_type)) try: people_admin_service.set_event_on_days(day_event, days_durations) except ServerException as ex: showErrorBox(ex.translated_message) self.refresh_action() def _remove_day_off(self): # Grab all the selected events event_ids = [] for ndx in self.table_view.selectionModel().selectedIndexes(): day_event_id = ndx.data(Qt.UserRole + 1) if day_event_id: mainlog.debug("Removing event: {}".format(day_event_id)) event_ids.append(day_event_id) # Remove them if event_ids: people_admin_service.remove_events(event_ids) self.refresh_action() def _make_days_off_menu_and_action_group(self): # We use a action group to be able to use the data() of actions # when action is tigerred # Call this ONLY ONCE because there are signal/slot connections. self.days_off_menu = QMenu(_("Day off")) self.days_off_add_submenu = QMenu(_("Set day off")) self.days_off_action_group = QActionGroup(self) for det in DayEventType.symbols(): a_one = QAction(_("Set one day"), self.days_off_action_group) a_one.setData((det.value, 1)) a_half = QAction(_("Set half day"), self.days_off_action_group) a_half.setData((det.value, 0.5)) a_period = QAction(_("Set period"), self.days_off_action_group) a_period.setData((det.value, 2)) self.days_off_action_group.addAction(a_one) self.days_off_action_group.addAction(a_half) self.days_off_action_group.addAction(a_period) m = QMenu(_("Set time off for {}").format(det.description)) m.addAction(a_one) m.addAction(a_half) m.addAction(a_period) self.days_off_add_submenu.addMenu(m) self.days_off_action_group.triggered.connect(self._add_day_off) self.day_off_remove_action = QAction(_("Remove day off"), self) self.day_off_remove_action.triggered.connect(self._remove_day_off) # Now we have the actions, we build the menu self.days_off_menu.addMenu(self.days_off_add_submenu) self.days_off_menu.addAction(self.day_off_remove_action) @Slot(QPoint) def popup_context_menu(self, position): self.days_off_menu.exec_(QCursor.pos()) @Slot() def select_all_slot(self): m = self.table_view.model() all = QItemSelection(m.index(0, 0), m.index(m.rowCount() - 1, m.columnCount() - 1)) self.table_view.selectionModel().select(all, QItemSelectionModel.Select) @Slot() def copy_slot(self): # Collect the rows indices indices = self.table_view.selectedIndexes() if not indices: return min_row = max_row = indices[0].row() min_col = max_col = indices[0].column() def min_max(minimum, v, maximum): if v < minimum: return v, maximum elif v > maximum: return minimum, v else: return minimum, maximum for ndx in self.table_view.selectedIndexes(): min_row, max_row = min_max(min_row, ndx.row(), max_row) min_col, max_col = min_max(min_col, ndx.column(), max_col) mainlog.debug("Copy from {},{} to {},{}".format( min_row, min_col, max_row, max_col)) day_max = calendar.monthrange(self.base_date.year, self.base_date.month)[1] s = "" for r in range(min_row, max_row + 1): d = [] for c in range(min_col, max_col + 1): ndx = self._table_model.item(r, c) if c == 0 or c > day_max: d.append(ndx.data(Qt.DisplayRole) or "") else: t = ndx.data(Qt.UserRole + 1) # Day off if t: t = ndx.data(Qt.DisplayRole) else: hours = ndx.data(Qt.UserRole) # Activity hours if hours is not None: t = str(hours).replace('.', ',') else: t = "" d.append(t) s += "\t".join(d) + u"\n" QApplication.clipboard().setText(s)
def handleEvents(self, event, signals): att = self.axisOrder[self.findAxisIndex(event.x)] # context menu if event.contextRequested: contextMenu = QMenu(self) contextMenu.addAction(u'Select all') contextMenu.addAction(u'Select none') contextMenu.addSeparator() act = QAction(u'Hide Axis', self) contextMenu.addAction(act) if len(self.axisOrder) <= 1: act.setEnabled(False) axesMenu = QMenu(u'Show/Hide Axes', self) axesActions = QActionGroup(self) for a in self.axisOrder: act = QAction(a,self,checkable=True) if self.axes[a].visible: act.toggle() if len(self.axisOrder) <= 1: act.setEnabled(False) axesActions.addAction(act) for act in axesActions.actions(): axesMenu.addAction(act) contextMenu.addMenu(axesMenu) contextMenu.addSeparator() contextMenu.addAction(u'Use as X axis') contextMenu.addAction(u'Use as Y axis') resultAction = contextMenu.exec_(QCursor.pos()) if resultAction != None and resultAction != 0: # Select all if resultAction.text() == u'Select all': self.app.intMan.newOperation(operation.ALL,att=att.dataAxis) # Select none if resultAction.text() == u'Select none': self.app.intMan.newOperation(operation.NONE,att=att.dataAxis) # Hide axis if resultAction.text() == u'Hide Axis': self.toggleVisible(att) # Toggle axis if resultAction.actionGroup() == axesActions: self.toggleVisible(resultAction.text()) # X axis if resultAction.text() == u'Use as X axis': if self.app.currentYattribute != self.app.currentXattribute: self.axes[self.app.currentXattribute].visAxis.handle.background.setAttribute('fill',self.normalHandleColor) self.axes[self.app.currentXattribute].visAxis.handle.originalBackgroundColor = self.normalHandleColor self.axes[att].visAxis.handle.background.setAttribute('fill',self.activeHandleColor) self.axes[att].visAxis.handle.originalBackgroundColor = self.activeHandleColor self.app.notifyAxisChange(att,xAxis=True) # Y axis if resultAction.text() == u'Use as Y axis': if self.app.currentXattribute != self.app.currentYattribute: self.axes[self.app.currentYattribute].visAxis.handle.background.setAttribute('fill',self.normalHandleColor) self.axes[self.app.currentYattribute].visAxis.handle.originalBackgroundColor = self.normalHandleColor self.axes[att].visAxis.handle.background.setAttribute('fill',self.activeHandleColor) self.axes[att].visAxis.handle.originalBackgroundColor = self.activeHandleColor self.app.notifyAxisChange(att,xAxis=False) #if linesMoved: # self.highlightedLayer.refreshLines(self.app.highlightedRsNumbers) return signals
def layout_list_contextMenu(self, pos): menu = QMenu() default_action = menu.addAction(self.tr("Reset Layout")) default_action.triggered.connect(self.on_default_layout) menu.exec_(QCursor.pos())
class MenuNode(object): _currentMenuContext = None """docstring for MenuNode""" def __init__(self, option, parent, menubar = None): if isinstance(option ,(str,unicode)): blobs = option.split('|') _option={ 'label':blobs[0] } l = len(blobs) if l>1: _option['shortcut'] = blobs[1] if l>2: _option['help'] = blobs[2] option = _option self.qtmenubar = menubar self.qtaction = None self.qtmenu = None # self.qtaction = None self.owner = None self.parent = parent signal = option.get( 'signal', None ) self.setSignal(signal) self.mgr = parent and parent.mgr self.owner = parent and parent.owner self.children = [] self.label = option.get('label', 'UNNAMED') self.name = option.get('name',self.label.replace('&','').replace(' ','_')) self.name = self.name.lower() self.shortcut = option.get( 'shortcut', False ) self.help = option.get( 'help', '' ) self.priority = option.get( 'priority', 0 ) self.itemType = option.get( 'type', False ) self.onClick = option.get( 'on_click', None ) self.cmd = option.get( 'command', None ) self.cmdArgs = option.get( 'command_args', None ) self.link = None self.menuType = self.qtmenubar and 'menubar' or 'item' children = option.get( 'children', None ) link = option.get( 'link', None ) if children or self.itemType == 'menu': if self.menuType != 'menubar': self.menuType = 'menu' self.itemType = False elif link: self.link = link if self.menuType != 'menubar': self.menuType = 'link' elif parent and parent.menuType == 'menubar': self.menuType = 'menu' if self.menuType == 'menu' : self.qtmenu = QMenu(self.label) if not parent or parent.menuType == 'root': return parent.addChildControl(self) if self.itemType == 'check': checked = option.get('checked',False) self.setValue(checked or False) if children: for data in children: self.addChild(data) # self.mgr.addNodeIndex(self) def getFullName(self): if parent: return parent.getFullName()+'/'+self.name return self.name def addChild( self, option, owner = None ): if option=='----': if self.qtmenu: self.qtmenu.addSeparator() elif isinstance(option, list): output=[] for data in option: n = self.addChild(data) if n : output.append(n) if owner: n.owner = owner return output else: node = MenuNode(option, self) if owner: node.owner = owner self.children.append(node) return node def addChildControl(self, child): childType = child.menuType selfType = self.menuType if selfType=='menu': if childType=='menu': child.qtaction = self.qtmenu.addMenu(child.qtmenu) elif child.link: qtmenu = child.link.qtmenu child.qtaction = self.qtmenu.addMenu(qtmenu) else: action = QtGui.QAction(child.label, None, shortcut = child.shortcut, statusTip = child.help, checkable = child.itemType=='check', triggered = child.handleEvent ) self.qtmenu.addAction(action) child.qtaction = action elif selfType=='menubar': if childType=='menu': self.qtmenubar.addMenu(child.qtmenu) child.qtaction = child.qtmenu.menuAction() else: logging.warning('attempt to add menuitem/link to a menubar') return else: logging.warning('menuitem has no child') def setEnabled(self, enabled): #todo: set state of linked item selfType = self.menuType if selfType == 'menubar': self.qtmenubar.setEnable(enabled) return if self.qtmenu: self.qtmenu.setEnabled(enabled) else: self.qtaction.setEnabled(enabled) def remove(self): self.clear() self.parent.children.remove(self) selfType = self.menuType if not self.parent: return if selfType=='menubar': return parentType = self.parent.menuType if parentType == 'menu': self.parent.qtmenu.removeAction( self.qtaction ) elif parentType == 'menubar': self.parent.qtmenubar.removeAction( self.qtaction ) logging.info('remove menunode:' + self.name ) def clear( self ): if self.menuType in [ 'menu', 'menubar' ]: for node in self.children[:]: node.remove() def findChild(self,name): name = name.lower() for c in self.children: if c.name==name: return c return None def getValue(self): if self.itemType in ('check','radio'): return self.qtaction.isChecked() return True def setValue(self, v): if self.itemType in ('check','radio'): self.qtaction.setChecked(v and True or False) def setSignal(self, signal): if isinstance(signal, (str, unicode)): signal = signals.get(signal) self.signal = signal def popUp( self, **option ): if self.qtmenu: context = option.get( 'context', None ) MenuNode._currentMenuContext = context self.qtmenu.exec_(QtGui.QCursor.pos()) def getContext( self ): return MenuNode._currentMenuContext def setOnClick(self, onClick): self.onClick = onClick def handleEvent(self): itemtype = self.itemType value = self.getValue() logging.debug( 'menu event:' + self.name ) if self.owner: if hasattr( self.owner, 'onMenu' ): self.owner.onMenu( self ) if self.signal: self.signal(value) if self.onClick != None: self.onClick(value) if self.cmd: args = self.cmdArgs or {} app.doCommand( self.cmd, **args ) MenuNode._currentMenuContext = None
def handleInspectorMenu(self, pos): menu = QMenu() menu.addAction('Add') menu.addAction('Delete') menu.exec_(QCursor.pos())
def contextMenuEvent(self, event): """ Handles the ``contextMenuEvent`` event for :class:`StatusBarButton`. :param `event`: a `QContextMenuEvent` event to be processed. """ QApplication.setOverrideCursor(Qt.ArrowCursor) menu = QMenu(self) objName = self.objectName() themeDir = gIconDir + os.sep + '..' + os.sep + self.mainWin.getSettingsGeneralIconTheme() + os.sep s = "&Settings..." if objName == "StatusBarButtonSNAP": settingsSnapAction = QAction(QIcon(themeDir + "gridsnapsettings.png"), s, menu) settingsSnapAction.triggered.connect(self.settingsSnap) menu.addAction(settingsSnapAction) elif objName == "StatusBarButtonGRID": settingsGridAction = QAction(QIcon(themeDir + "gridsettings.png"), s, menu) settingsGridAction.triggered.connect(self.settingsGrid) menu.addAction(settingsGridAction) elif objName == "StatusBarButtonRULER": settingsRulerAction = QAction(QIcon(themeDir + "rulersettings.png"), s, menu) settingsRulerAction.triggered.connect(self.settingsRuler) menu.addAction(settingsRulerAction) elif objName == "StatusBarButtonORTHO": settingsOrthoAction = QAction(QIcon(themeDir + "orthosettings.png"), s, menu) settingsOrthoAction.triggered.connect(self.settingsOrtho) menu.addAction(settingsOrthoAction) elif objName == "StatusBarButtonPOLAR": settingsPolarAction = QAction(QIcon(themeDir + "polarsettings.png"), s, menu) settingsPolarAction.triggered.connect(self.settingsPolar) menu.addAction(settingsPolarAction) elif objName == "StatusBarButtonQSNAP": settingsQSnapAction = QAction(QIcon(themeDir + "qsnapsettings.png"), s, menu) settingsQSnapAction.triggered.connect(self.settingsQSnap) menu.addAction(settingsQSnapAction) elif objName == "StatusBarButtonQTRACK": settingsQTrackAction = QAction(QIcon(themeDir + "qtracksettings.png"), s, menu) settingsQTrackAction.triggered.connect(self.settingsQTrack) menu.addAction(settingsQTrackAction) elif objName == "StatusBarButtonLWT": gview = self.mainWin.activeView() if gview: enableRealAction = QAction(QIcon(themeDir + "realrender.png"), "&RealRender On", menu) enableRealAction.setEnabled(not gview.isRealEnabled()) enableRealAction.triggered.connect(self.enableReal) menu.addAction(enableRealAction) disableRealAction = QAction(QIcon(themeDir + "realrender.png"), "&RealRender Off", menu) disableRealAction.setEnabled(gview.isRealEnabled()) disableRealAction.triggered.connect(self.disableReal) menu.addAction(disableRealAction) settingsLwtAction = QAction(QIcon(themeDir + "lineweightsettings" + ".png"), s, menu) settingsLwtAction.triggered.connect(self.settingsLwt) menu.addAction(settingsLwtAction) menu.exec_(event.globalPos()) QApplication.restoreOverrideCursor() self.statusbar.clearMessage()
class ImageViewer(QWidget): """A custom widget to display an image in Gui.""" # Inspired by : # https://doc.qt.io/qt-6/qtwidgets-widgets-imageviewer-example.html # https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/widgets/imageviewer def __init__(self, parent=None): """Initialize Widget.""" super().__init__(parent) self.setLayout(QVBoxLayout()) self.imglabel = QLabel() self.imglabel.setBackgroundRole(QPalette.Base) self.imglabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.imglabel.setScaledContents(True) # Resize pixmap along with label self.imglabel.setAlignment(Qt.AlignCenter) self.imglabel.setText("(No image yet)") self.namelabel = QLabel() self.scrollarea = QScrollArea() self.scrollarea.setWidget(self.imglabel) self.scrollarea.setWidgetResizable(False) self.scrollarea.setAlignment(Qt.AlignCenter) self.layout().addWidget(self.scrollarea) self.layout().addWidget(self.namelabel) self.scale_factor = 1.0 self._initial_size = QSize(0, 0) self._img_path = "" self.setContextMenuPolicy(Qt.CustomContextMenu) # pylint: disable=no-member self.customContextMenuRequested.connect(self.show_context_menu) self.menu = QMenu() self.add_actions_to_menu(self.menu) def add_actions_to_menu(self, menu): """Add widget actions to a given menu. Args: menu -- the menu to add the actions (QMenu) """ zoom_in_act = menu.addAction("Zoom In (25%)") zoom_in_act.triggered.connect(self.zoom_in) zoom_out_act = menu.addAction("Zoom Out (25%)") zoom_out_act.triggered.connect(self.zoom_out) zoom_normal_act = menu.addAction("Normal Size") zoom_normal_act.triggered.connect(self.normal_size) menu.addSeparator() copy_filename_act = menu.addAction("Copy Image to Clipboard") copy_filename_act.triggered.connect(self.copy_image) copy_filename_act = menu.addAction("Copy Filename to Clipboard") copy_filename_act.triggered.connect(self.copy_filename) def load_image(self, img_path): """Load an image in widget from a file. Args: img_path -- Path of image file to load (str) """ img_path = str(img_path) pixmap = QPixmap(img_path) self.imglabel.setPixmap(pixmap) self.imglabel.resize(pixmap.size()) self.namelabel.setText(f"File: {img_path}") self._img_path = img_path self._initial_size = pixmap.size() def resize_image(self, new_size=None): """Resize embedded image to target size. Args: new_size -- target size to resize image to (QSize) """ if not new_size: new_size = self._initial_size self.scale_factor = 1.0 new_size = QSize(new_size) self.imglabel.setMinimumSize(new_size) self.imglabel.setMaximumSize(new_size) def scale_image(self, factor): """Rescale embedded image applying a factor. Factor is applied relatively to current scale. Args: factor -- Factor to apply (float) """ self.scale_factor *= float(factor) new_size = self.scale_factor * self._initial_size self.resize_image(new_size) @Slot() def zoom_in(self): """Zoom embedded image in (slot).""" self.scale_image(1.25) @Slot() def zoom_out(self): """Zoom embedded image out (slot).""" self.scale_image(0.8) @Slot() def normal_size(self): """Set embedded image scale to 1:1 (slot).""" self.resize_image() @Slot() def copy_filename(self): """Copy file name to clipboard (slot).""" QGuiApplication.clipboard().setText(self._img_path) @Slot() def copy_image(self): """Copy embedded image to clipboard (slot).""" QGuiApplication.clipboard().setImage(self.imglabel.pixmap().toImage()) @Slot(QPoint) def show_context_menu(self, pos): """Show context menu.""" self.menu.exec_(self.mapToGlobal(pos))
def contextMenuEvent(self, event): """ Handles the ``contextMenuEvent`` event for :class:`StatusBarButton`. :param `event`: A `QContextMenuEvent`_ to be processed. """ QApplication.setOverrideCursor(Qt.ArrowCursor) menu = QMenu(self) objName = self.objectName() themeDir = gIconDir + os.sep + '..' + os.sep + self.mainWin.getSettingsGeneralIconTheme( ) + os.sep s = "&Settings..." if objName == "StatusBarButtonSNAP": settingsSnapAction = QAction( QIcon(themeDir + "gridsnapsettings.png"), s, menu) settingsSnapAction.triggered.connect(self.settingsSnap) menu.addAction(settingsSnapAction) elif objName == "StatusBarButtonGRID": settingsGridAction = QAction(QIcon(themeDir + "gridsettings.png"), s, menu) settingsGridAction.triggered.connect(self.settingsGrid) menu.addAction(settingsGridAction) elif objName == "StatusBarButtonRULER": settingsRulerAction = QAction( QIcon(themeDir + "rulersettings.png"), s, menu) settingsRulerAction.triggered.connect(self.settingsRuler) menu.addAction(settingsRulerAction) elif objName == "StatusBarButtonORTHO": settingsOrthoAction = QAction( QIcon(themeDir + "orthosettings.png"), s, menu) settingsOrthoAction.triggered.connect(self.settingsOrtho) menu.addAction(settingsOrthoAction) elif objName == "StatusBarButtonPOLAR": settingsPolarAction = QAction( QIcon(themeDir + "polarsettings.png"), s, menu) settingsPolarAction.triggered.connect(self.settingsPolar) menu.addAction(settingsPolarAction) elif objName == "StatusBarButtonQSNAP": settingsQSnapAction = QAction( QIcon(themeDir + "qsnapsettings.png"), s, menu) settingsQSnapAction.triggered.connect(self.settingsQSnap) menu.addAction(settingsQSnapAction) elif objName == "StatusBarButtonQTRACK": settingsQTrackAction = QAction( QIcon(themeDir + "qtracksettings.png"), s, menu) settingsQTrackAction.triggered.connect(self.settingsQTrack) menu.addAction(settingsQTrackAction) elif objName == "StatusBarButtonLWT": gview = self.mainWin.activeView() if gview: enableRealAction = QAction(QIcon(themeDir + "realrender.png"), "&RealRender On", menu) enableRealAction.setEnabled(not gview.isRealEnabled()) enableRealAction.triggered.connect(self.enableReal) menu.addAction(enableRealAction) disableRealAction = QAction(QIcon(themeDir + "realrender.png"), "&RealRender Off", menu) disableRealAction.setEnabled(gview.isRealEnabled()) disableRealAction.triggered.connect(self.disableReal) menu.addAction(disableRealAction) settingsLwtAction = QAction( QIcon(themeDir + "lineweightsettings" + ".png"), s, menu) settingsLwtAction.triggered.connect(self.settingsLwt) menu.addAction(settingsLwtAction) menu.exec_(event.globalPos()) QApplication.restoreOverrideCursor() self.statusbar.clearMessage()
def handleInspectorMenu(self, pos): menu = QMenu() menu.addAction("Add") menu.addAction("Delete") menu.exec_(QCursor.pos())