''' @author: olivier.massot ''' from PyQt5 import uic, QtCore from PyQt5.Qt import Qt, QEvent, QGraphicsScene, QPointF, QFileDialog, \ QApplication, QMessageBox, QTreeWidgetItem, \ QGraphicsTextItem, QGraphicsItem, QGraphicsRectItem, \ QBrush, QColor, QGraphicsLineItem, QLineF, \ QPen, QPainter, QSvgGenerator, QSize, QRect, QGraphicsItemGroup, \ QGraphicsColorizeEffect, QFont, QDialog, QTableWidgetItem, \ QListWidgetItem, QInputDialog, QIcon, QPixmap, pyqtSignal, QObject, \ QTreeWidgetItemIterator from PyQt5.QtWidgets import QMainWindow, QGraphicsView from path import Path from core import ModuleObject import core Ui_window, _ = uic.loadUiType(Path(__file__).parent / 'qt_viewer.ui') Ui_details, _ = uic.loadUiType(Path(__file__).parent / 'qt_details.ui') Ui_select, _ = uic.loadUiType(Path(__file__).parent / 'qt_select_object.ui') palette = { "Table": QColor(240, 240, 20), "Query": QColor(210, 50, 210), "Module": QColor(220, 10, 33), "Relation": QColor(122, 50, 209), "Report": QColor(25, 10, 220), "Form": QColor(10, 180, 220), "Macro": QColor(40, 220, 10), } v_spacing = 120 cell_width = 150 cell_spacing = 25 class GraphicsObject(QGraphicsItemGroup): items = [] def __init__(self, obj, parent=None): super(GraphicsObject, self).__init__(parent=parent) self.obj = obj self.links = [] self._x = 0 self._y = 0 self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.setAcceptHoverEvents(True) self.label = QGraphicsTextItem() self.label.setTextWidth(cell_width) self.label.setZValue(2) self.setText() self.addToGroup(self.label) pen = QPen(palette[self.obj.type_].darker(150)) pen.setWidth(2) self.rect = QGraphicsRectItem(self.label.boundingRect()) self.rect.setPen(pen) self.rect.setBrush(palette[self.obj.type_].lighter(150)) self.rect.setZValue(0) self.addToGroup(self.rect) self.topAnchorCoords = ((self.boundingRect().topLeft().x() + self.boundingRect().topRight().x() / 2), self.boundingRect().topLeft().y()) self.bottomAnchorCoords = ((self.boundingRect().topLeft().x() + self.boundingRect().topRight().x() / 2), self.boundingRect().bottomLeft().y()) self.top_anchor = QGraphicsRectItem(self.topAnchorCoords[0] - 3, self.topAnchorCoords[1] - 3, 6, 6) self.bottom_anchor = QGraphicsRectItem(self.bottomAnchorCoords[0] - 3, self.bottomAnchorCoords[1] - 3, 6, 6) for anchor in (self.top_anchor, self.bottom_anchor): anchor.setBrush(QBrush(QColor(255, 153, 51))) anchor.setZValue(1) self.addToGroup(anchor) effect = QGraphicsColorizeEffect() effect.setColor(QColor(255, 255, 255)) effect.setStrength(0.35) effect.setEnabled(False) self.rect.setGraphicsEffect(effect) self.setToolTip("[{}] '{}'".format(self.obj.type_, self.obj.name_)) GraphicsObject.items.append(self) def setText(self): self.label.setHtml(self.html()) def html(self): return "[{}]
{}".format(self.obj.type_, self.obj.name_) def topAnchorCenter(self): return self.mapToScene(QPointF(*self.topAnchorCoords)) def bottomAnchorCenter(self): return self.mapToScene(QPointF(*self.bottomAnchorCoords)) def update(self): if self.pos() is not QPointF(self._x, self._y): self.setPos(QPointF(self._x, self._y)) for l in self.links: l.update() def paint(self, *args, **kwargs): super(GraphicsObject, self).paint(*args, **kwargs) self._x, self._y = self.pos().x(), self.pos().y() self.update() def setShining(self, active): self.rect.graphicsEffect().setEnabled(active) def hoverEnterEvent(self, _): self.setShining(True) def hoverLeaveEvent(self, _): self.setShining(False) def mouseDoubleClickEvent(self, _): self.scene().itemDoubleClicked.emit(self) class GraphicsRootObject(GraphicsObject): def __init__(self, obj, parent=None): super(GraphicsRootObject, self).__init__(obj, parent=parent) self.level = 0 self.deps = [] self.refs = [] self._dep_emprise = 0 self._ref_emprise = 0 pen = QPen(QColor("red")) pen.setWidth(2) self.rect.setPen(pen) def xleft(self): return 0 def compute_coords(self): return 0, 0 def dep_emprise(self): if not self._dep_emprise: self._dep_emprise = sum([d.dep_emprise() for d in self.deps]) if self.deps else (cell_width + 2 * cell_spacing) return self._dep_emprise def ref_emprise(self): if not self._ref_emprise: self._ref_emprise = sum([r.ref_emprise() for r in self.refs]) if self.refs else (cell_width + 2 * cell_spacing) return self._ref_emprise class GraphicsDepObject(GraphicsObject): def __init__(self, obj, parentItem, parent=None): super(GraphicsDepObject, self).__init__(obj, parent=parent) self.deps = [] self.parentItem = parentItem if parentItem: parentItem.deps.append(self) self.level = parentItem.level + 1 self._dep_emprise = 0 def xleft(self): x0 = sum([n.dep_emprise() for n in self.parentItem.deps[0:self.parentItem.deps.index(self)]]) return self.parentItem.xleft() + x0 def compute_coords(self): x0 = sum([n.dep_emprise() for n in self.parentItem.deps[0:self.parentItem.deps.index(self)]]) x = self.parentItem.xleft() + (0.5 * self.dep_emprise() - (cell_width / 2 + cell_spacing)) + x0 y = v_spacing * self.level return x, y def dep_emprise(self): if not self._dep_emprise: self._dep_emprise = sum([d.dep_emprise() for d in self.deps]) if self.deps else (cell_width + 2 * cell_spacing) return self._dep_emprise class GraphicsCloneDepObject(GraphicsDepObject): def __init__(self, obj, childItem, parent=None): super(GraphicsCloneDepObject, self).__init__(obj, childItem, parent) self.bottom_anchor.setBrush(QColor("red")) self.clone_of = next((item for item in GraphicsObject.items if item.obj is obj and (type(item) in (GraphicsRootObject, GraphicsDepObject, GraphicsRefObject)))) self.setToolTip("L'objet [{}] '{}' a déjà été placé sur l'arbre".format(self.obj.type_, self.obj.name_)) def html(self): return "* Clone *
" + super(GraphicsCloneDepObject, self).html() @property def deps(self): return [] @deps.setter def deps(self, value): pass def hoverEnterEvent(self, _): self.setShining(True) self.clone_of.setShining(True) def hoverLeaveEvent(self, _): self.setShining(False) self.clone_of.setShining(False) class GraphicsRefObject(GraphicsObject): def __init__(self, obj, childItem, parent=None): super(GraphicsRefObject, self).__init__(obj, parent=parent) self.refs = [] self.childItem = childItem if childItem: childItem.refs.append(self) self.level = childItem.level - 1 self._ref_emprise = 0 def xleft(self): x0 = sum([n.ref_emprise() for n in self.childItem.refs[0:self.childItem.refs.index(self)]]) return self.childItem.xleft() + x0 def compute_coords(self): x0 = sum([n.ref_emprise() for n in self.childItem.refs[0:self.childItem.refs.index(self)]]) return self.childItem.xleft() + (0.5 * self.ref_emprise() - (cell_width / 2 + cell_spacing)) + x0, v_spacing * self.level def ref_emprise(self): if not self._ref_emprise: self._ref_emprise = sum([d.ref_emprise() for d in self.refs]) if self.refs else (cell_width + 2 * cell_spacing) return self._ref_emprise class GraphicsCloneRefObject(GraphicsRefObject): def __init__(self, obj, childItem, parent=None): super(GraphicsCloneRefObject, self).__init__(obj, childItem, parent) self.top_anchor.setBrush(QColor("red")) self.clone_of = next((item for item in GraphicsObject.items if item.obj is obj and (type(item) in (GraphicsRootObject, GraphicsDepObject, GraphicsRefObject)))) self.setToolTip("L'objet [{}] '{}' a déjà été placé sur l'arbre".format(self.obj.type_, self.obj.name_)) def html(self): return "* Clone *
" + super(GraphicsCloneRefObject, self).html() @property def refs(self): return [] @refs.setter def refs(self, value): pass def hoverEnterEvent(self, _): self.setShining(True) self.clone_of.setShining(True) def hoverLeaveEvent(self, _): self.setShining(False) self.clone_of.setShining(False) class GraphicsLink(QGraphicsLineItem): items = [] def __init__(self, topGraphicsObject, bottomGraphicsObject, parent=None): self.topGraphicsObject = topGraphicsObject self.topGraphicsObject.links.append(self) self.bottomGraphicsObject = bottomGraphicsObject self.bottomGraphicsObject.links.append(self) super(QGraphicsLineItem, self).__init__(parent=parent) self.update() def update(self): line = QLineF(self.mapToScene(self.topGraphicsObject.bottomAnchorCenter()), self.mapToScene(self.bottomGraphicsObject.topAnchorCenter())) self.setLine(line) class GraphicsScene(QGraphicsScene): itemDoubleClicked = pyqtSignal(object) class Viewer(QMainWindow): def __init__(self): super (Viewer, self).__init__() self.createWidgets() def createWidgets(self): self.ui = Ui_window() self.ui.setupUi(self) self.ui.progressBar.setVisible(False) self.ui.stackedWidget.setCurrentIndex(0) self.ui.stackedView.setCurrentIndex(0) self.ui.btn_select.clicked.connect(self.selectSourceDir) self.ui.btn_zoom_plus.clicked.connect(self.zoom_plus) self.ui.btn_zoom_minus.clicked.connect(self.zoom_minus) self.ui.btn_zoom_view.clicked.connect(self.fit_in_view) self.ui.btn_zoom_center.clicked.connect(self.zoom_center) self.ui.btn_svg.clicked.connect(self.to_svg) self.ui.btn_save.clicked.connect(self.save) self.ui.btn_load.clicked.connect(self.open) self.ui.treeWidget.itemClicked.connect(self.treeItemSelected) self.ui.btn_edit_item.clicked.connect(self.edit_selected_item) self.ui.btn_graphic_view.clicked.connect(self.switch_graphic_view) self.ui.btn_table_view.clicked.connect(self.switch_table_view) self.ui.searchBoxDeps.textChanged.connect(self.filterTblDeps) self.ui.searchBoxRefs.textChanged.connect(self.filterTblRefs) self.ui.btnSearchTree.clicked.connect(self.searchTree) self.ui.searchBoxTree.returnPressed.connect(self.searchTree) self._title = "" core.Analyse.report = self.update_progression self.ui.view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate) self.ui.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.ui.view.viewport().installEventFilter(self) self._scene = GraphicsScene() self._scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex) self._scene.itemDoubleClicked.connect(self.graphicsObjectDoubleClicked) self.ui.view.setScene(self._scene) helpLabel = QGraphicsTextItem() helpLabel.setPlainText("Sélectionnez le répertoire contenant les sources de la base à analyser,\npuis sélectionnez dans la liste de droite un objet à partir duquel afficher les dépendances...") helpLabel.setTextWidth(600) font = QFont() font.setItalic(True) font.setWeight(63) helpLabel.setFont(font) self._scene.addItem(helpLabel) def eventFilter(self, _, event): if event.type() == QEvent.Wheel: if event.angleDelta().y() > 0: self.zoom_plus() elif event.angleDelta().y() < 0: self.zoom_minus() return True return False def fit_in_view(self): rect = self._scene.itemsBoundingRect() rect.adjust(-50, -50, 50, 50) self._scene.setSceneRect(rect) self.ui.view.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio) def zoom_plus(self): self.ui.view.scale(1.2, 1.2) def zoom_minus(self): self.ui.view.scale(0.8, 0.8) def zoom_center(self): root = next((item for item in GraphicsObject.items if type(item) is GraphicsRootObject)) rect = root.boundingRect() rect.adjust(-200, -200, 200, 200) self.ui.view.fitInView(rect, Qt.KeepAspectRatio) def switch_graphic_view(self): self.ui.stackedView.setCurrentIndex(0) def switch_table_view(self): self.ui.stackedView.setCurrentIndex(1) def to_svg(self): fileName, _ = QFileDialog.getSaveFileName(self, "Save File", "", "Svg File (*.svg)") if not fileName: return gen = QSvgGenerator() gen.setFileName(fileName) gen.setSize(QSize(1000, 1000)) gen.setViewBox(QRect(0, 0, 1000, 1000)) gen.setTitle("Access Analyser") gen.setDescription("Access Analyser Report for {}".format(self._title)) painter = QPainter(gen) self._scene.render(painter) painter.end() def save(self): if not core.Analyse.objects: return filepath, _ = QFileDialog.getSaveFileName(self, 'Enregistrer sous', '', "Access Analyser Files (*.acan)") if filepath: core.Analyse.dump_to(filepath) def open(self): filepath, _ = QFileDialog.getOpenFileName(self, 'Charger les données de', '', "Access Analyser Files (*.acan)") if filepath: core.Analyse.load_from(filepath) self._title = Path(filepath).name self.load() def update_progression(self, i, total, msg=""): self.ui.progressBar.setMaximum(total) self.ui.progressBar.setValue(i) if msg: self.ui.txtPanel.append(msg) QApplication.processEvents() def selectSourceDir(self): source_dir = QFileDialog.getExistingDirectory(self, "Sélectionner le répertoire des sources", "", QFileDialog.ShowDirsOnly) if not source_dir: return self.run(source_dir) def run(self, source_dir): source_dir = Path(source_dir) self._title = source_dir self.ui.stackedWidget.setCurrentIndex(0) self.ui.progressBar.setVisible(True) self.ui.txtPanel.clear() core.Analyse.run(source_dir) self.ui.progressBar.setVisible(False) self.ui.txtPanel.clear() self.load() QMessageBox.information(self, "Analyse", "Analyse terminée: {} objets chargés en mémoire".format(len(core.Analyse.objects))) def load(self): self.clear_scene() self.ui.treeWidget.clear() self.ui.stackedWidget.setCurrentIndex(1) topitem = QTreeWidgetItem() topitem.setText(0, "Objets") self.ui.treeWidget.addTopLevelItem(topitem) groupes = {} for index, obj in enumerate(core.Analyse.objects): if not obj.type_ in groupes: item = QTreeWidgetItem() item.setText(0, obj.type_) groupes[obj.type_] = item topitem.addChild(item) item = QTreeWidgetItem() if any(len(m.warnings) > 0 for m in obj.mentions): item.setIcon(0, QIcon(QPixmap(core.here / "rsc\\warning_16.png"))) item.setText(1, obj.name_) item.setData(2, 0, index) groupes[obj.type_].addChild(item) self.ui.treeWidget.setColumnHidden(2, True) self.ui.treeWidget.expandToDepth(1) self.ui.btn_save.setEnabled(True) def enable_view_buttons(self, enable): self.ui.btn_zoom_plus.setEnabled(enable) self.ui.btn_zoom_center.setEnabled(enable) self.ui.btn_zoom_view.setEnabled(enable) self.ui.btn_zoom_minus.setEnabled(enable) self.ui.btn_graphic_view.setEnabled(enable) self.ui.btn_table_view.setEnabled(enable) def clear_scene(self): self._scene.clear() GraphicsObject.items = [] self.enable_view_buttons(False) def update_view(self, root_object=None): if root_object is None: root_object = next((go.obj for go in GraphicsObject.items if type(go) is GraphicsRootObject)) self.switch_graphic_view() self.clear_scene() root = GraphicsRootObject(root_object) self._scene.addItem(root) def _addDeps(go): for dep in go.obj.deps: if dep not in [go.obj for go in GraphicsObject.items]: gdep = GraphicsDepObject(dep, go) if dep.deps: _addDeps(gdep) else: gdep = GraphicsCloneDepObject(dep, go) self._scene.addItem(gdep) link = GraphicsLink(go, gdep) self._scene.addItem(link) _addDeps(root) def _addRefs(go): for ref in go.obj.refs: if ref not in [go.obj for go in GraphicsObject.items]: gref = GraphicsRefObject(ref, go) if ref.refs: _addRefs(gref) else: gref = GraphicsCloneRefObject(ref, go) self._scene.addItem(gref) link = GraphicsLink(gref, go) self._scene.addItem(link) _addRefs(root) for item in GraphicsObject.items: item._x, item._y = item.compute_coords() if isinstance(item, GraphicsDepObject): item._x -= (root.dep_emprise() - (cell_width + 2 * cell_spacing)) / 2 if isinstance(item, GraphicsRefObject): item._x -= (root.ref_emprise() - (cell_width + 2 * cell_spacing)) / 2 item.update() self.ui.tblDeps.clearContents() self.ui.tblRefs.clearContents() self.ui.tblDeps.setRowCount(0) self.ui.tblRefs.setRowCount(0) self.ui.lblRootTitle.setText("{}: {}".format(root_object.type_, root_object.name_)) for item in GraphicsObject.items: if type(item) is GraphicsDepObject: row = self.ui.tblDeps.rowCount() self.ui.tblDeps.insertRow(row) self.ui.tblDeps.setItem(row, 0, QTableWidgetItem(item.obj.type_)) self.ui.tblDeps.setItem(row, 1, QTableWidgetItem(item.obj.name_)) if type(item) is GraphicsRefObject: row = self.ui.tblRefs.rowCount() self.ui.tblRefs.insertRow(row) self.ui.tblRefs.setItem(row, 0, QTableWidgetItem(item.obj.type_)) self.ui.tblRefs.setItem(row, 1, QTableWidgetItem(item.obj.name_)) self.enable_view_buttons(True) self.fit_in_view() def treeItemSelected(self, item): index = item.data(2, 0) if index is None: self.ui.btn_edit_item.setEnabled(False) return obj = core.Analyse.objects[index] self.update_view(obj) self.ui.btn_edit_item.setEnabled(True) def filterTblDeps(self, filterStr): for row in range(self.ui.tblDeps.rowCount()): content = "{}{}".format(self.ui.tblDeps.item(row, 0).text(), self.ui.tblDeps.item(row, 1).text()) self.ui.tblDeps.setRowHidden(row, filterStr.lower() not in content.lower()) def filterTblRefs(self, filterStr): for row in range(self.ui.tblRefs.rowCount()): content = "{}{}".format(self.ui.tblRefs.item(row, 0).text(), self.ui.tblRefs.item(row, 1).text()) self.ui.tblRefs.setRowHidden(row, filterStr.lower() not in content.lower()) def searchTree(self): filterStr = self.ui.searchBoxTree.text() if not filterStr: return items = self.ui.treeWidget.findItems(filterStr, Qt.MatchContains | Qt.MatchRecursive, 1) if not items: QMessageBox.warning(self, "Recherche", "Aucun résultat pour '{}'".format(filterStr)) try: start = items.index(self.ui.treeWidget.currentItem()) + 1 except ValueError: start = 0 match = (items[start:] + items[:start])[0] self.ui.treeWidget.setCurrentItem(match) self.ui.treeWidget.scrollTo(self.ui.treeWidget.indexFromItem(match, 1)) def edit(self, obj): dlg = DetailsDialog(obj, self) dlg.show() r = dlg.exec_() if r: core.Analyse.build_trees() self.update_view() item = self.find_tree_item(obj) if any(len(m.warnings) > 0 for m in obj.mentions): item.setIcon(0, QIcon(QPixmap(core.here / "rsc\\warning_16.png"))) else: item.setIcon(0, QIcon()) def find_tree_item(self, obj): return next((item for item in self.ui.treeWidget.findItems("", Qt.MatchContains | Qt.MatchRecursive, 1) if item.data(2, 0) == core.Analyse.objects.index(obj))) def edit_selected_item(self): index = self.ui.treeWidget.currentItem().data(2, 0) obj = core.Analyse.objects[index] self.edit(obj) def graphicsObjectDoubleClicked(self, go): self.edit(go.obj) def keyPressEvent(self, e): if e.key() == Qt.Key_S and e.modifiers() & Qt.ControlModifier: self.save() e.accept() if e.key() == Qt.Key_Control: self.ui.view.setDragMode(QGraphicsView.ScrollHandDrag) e.accept() def keyReleaseEvent(self, e): if e.key() == Qt.Key_Control: self.ui.view.setDragMode(QGraphicsView.RubberBandDrag) class DetailsDialog(QDialog): def __init__(self, obj, parent=None): self.obj = obj super (DetailsDialog, self).__init__(parent) self.createWidgets() def createWidgets(self): self.ui = Ui_details() self.ui.setupUi(self) self.ui.tbl_mentions.verticalHeader().sectionDoubleClicked.connect(self.verticalHeaderDoubleClicked) self.ui.tbl_mentions.itemClicked.connect(self.itemClicked) self.ui.btn_add.clicked.connect(self.add_mention) self.ui.btn_edit.clicked.connect(self.edit_mention) self.ui.btn_del.clicked.connect(self.del_mention) self.ui.btn_ok.clicked.connect(self.ok) self.ui.btn_cancel.clicked.connect(self.ok) self.ui.lbl_title.setText("{}: {}".format(self.obj.type_, self.obj.name_)) self.ui.tbl_mentions.hideColumn(0) self.ui.tbl_mentions.setColumnWidth(1, 50) self.ui.tbl_mentions.setColumnWidth(2, 70) self.ui.tbl_mentions.setColumnWidth(3, 150) self.load_table() def load_table(self): self.ui.tbl_mentions.setSortingEnabled(False) self.ui.tbl_mentions.clearContents() self.ui.tbl_mentions.setRowCount(len(self.obj.mentions)) for index, mention in enumerate(self.obj.mentions): item = QTableWidgetItem("") if len(mention.warnings) > 0: item.setIcon(QIcon(QPixmap(core.here / "rsc\\warning_16.png"))) item.setToolTip("\n".join([w.text for w in mention.warnings]) + "\n\nDoucle-cliquer pour supprimer l'avertissement.") self.ui.tbl_mentions.setVerticalHeaderItem(index, item) item = QTableWidgetItem("") item.setData(0, index) self.ui.tbl_mentions.setItem(index, 0, item) item = QTableWidgetItem("{}".format(mention.line)) self.ui.tbl_mentions.setItem(index, 1, item) item = QTableWidgetItem("{}".format(mention.obj.type_)) self.ui.tbl_mentions.setItem(index, 2, item) item = QTableWidgetItem("{}".format(mention.objname) if mention.obj.type_ != "Module" else "{}.{}".format(mention.obj.name_, mention.objname)) self.ui.tbl_mentions.setItem(index, 3, item) item = QTableWidgetItem("{}".format(mention.quote)) item.setFont(QFont("Consolas", 8)) self.ui.tbl_mentions.setItem(index, 4, item) self.ui.tbl_mentions.setSortingEnabled(True) def itemClicked(self, _): self.ui.btn_edit.setEnabled(True) self.ui.btn_del.setEnabled(True) def verticalHeaderDoubleClicked(self, row): index = self.ui.tbl_mentions.item(row, 0).data(0) mention = self.obj.mentions[index] if mention.warnings: if QMessageBox.question(self, "Confirmation", "Voulez-vous supprimer cet avertissement?") == QMessageBox.Yes: del mention.warnings[:] self.ui.tbl_mentions.verticalHeaderItem(row).setIcon(QIcon()) def add_mention(self): dlg = ObjectSelectorDialog(parent=self) dlg.show() r = dlg.exec_() if not r: return mention = core.Mention(0, dlg.obj.name_, "(ajouté manuellement)", dlg.obj) linenum, _ = QInputDialog.getInt(self, "Ligne", "Numéro de ligne correspondant dans le code-source? (facultatif)", 0) if linenum > 0: mention.line = linenum try: with open(self.obj.sourcefile, "r", encoding='utf-8') as f: mention.quote = next((line.strip() for i, line in enumerate(f) if i == linenum - 1)).strip() except FileNotFoundError: pass self.obj.mentions.append(mention) self.load_table() def selected_index(self): row = self.ui.tbl_mentions.currentRow() if row < 0: return None return self.ui.tbl_mentions.item(row, 0).data(0) def edit_mention(self): row = self.ui.tbl_mentions.currentRow() if row < 0: return item = QTableWidgetItem("") item.setIcon(QIcon(QPixmap(core.here / "rsc\\edit_16.png"))) self.ui.tbl_mentions.setVerticalHeaderItem(row, item) index = self.ui.tbl_mentions.item(row, 0).data(0) mention = self.obj.mentions[index] dlg = ObjectSelectorDialog(filterStr=mention.objname, parent=self) dlg.show() r = dlg.exec_() if r: mention.obj = dlg.obj mention.warning = "" self.load_table() def del_mention(self): index = self.selected_index() if not QMessageBox.question(self, "Confirmation", "Etes-vous sûr de vouloir supprimer la référence sélectionnée?") == QMessageBox.Yes: return del self.obj.mentions[index] self.load_table() def ok(self): self.done(1) def cancel(self): self.done(0) def keyPressEvent(self, e): if e.key() == Qt.Key_Delete: if self.ui.tbl_mentions.currentRow() < 0: return self.del_mention() e.accept() class ObjectSelectorDialog(QDialog): def __init__(self, filterStr="", parent=None): self.obj = None super (ObjectSelectorDialog, self).__init__(parent) self.createWidgets() self.ui.searchBox.setText(filterStr) def createWidgets(self): self.ui = Ui_select() self.ui.setupUi(self) for obj in core.Analyse.objects: item = QListWidgetItem("{} - {}".format(obj.type_, obj.name_)) item.obj = obj self.ui.listWidget.addItem(item) self.ui.searchBox.textChanged.connect(self.filter) self.ui.listWidget.itemClicked.connect(self.itemClicked) self.ui.listWidget.itemDoubleClicked.connect(self.itemDoubleClicked) self.ui.btn_ok.clicked.connect(self.ok) def itemClicked(self, item): self.obj = item.obj self.ui.btn_ok.setEnabled(True) def itemDoubleClicked(self, item): self.itemClicked(item) self.ok() def filter(self, filterStr): for i in range(self.ui.listWidget.count()): item = self.ui.listWidget.item(i) item.setHidden(filterStr.lower() not in item.text().lower()) def ok(self): self.done(1) def done(self, status): if not status: self.obj = None super(ObjectSelectorDialog, self).done(status)