class Imageobj(QGraphicsView): leftMouseButtonPressed = pyqtSignal(float, float) rightMouseButtonPressed = pyqtSignal(float, float) leftMouseButtonReleased = pyqtSignal(float, float) rightMouseButtonReleased = pyqtSignal(float, float) leftMouseButtonDoubleClicked = pyqtSignal(float, float) rightMouseButtonDoubleClicked = pyqtSignal(float, float) def __init__(self): QGraphicsView.__init__(self) # Image is displayed as a QPixmap in a QGraphicsScene attached to this QGraphicsView. self.scene = QGraphicsScene() self.setScene(self.scene) self.rectgroup = QGraphicsItemGroup() self.linegroup = QGraphicsItemGroup() self._pixmapHandle = None self.canZoom = True self.canPan = True self.zoomStack = [] self.aspectRatioMode = Qt.KeepAspectRatio self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.begin = QPoint() self.end = QPoint() self.helper_bool = False self.helper_bool2 = False self.rect = QGraphicsRectItem() self.line = QGraphicsLineItem() def hasImage(self): """ Returns whether or not the scene contains an image pixmap. """ return self._pixmapHandle is not None def pixmap(self): """ Returns the scene's current image pixmap as a QPixmap, or else None if no image exists. :rtype: QPixmap | None """ if self.hasImage(): return self._pixmapHandle.pixmap() return None def image(self): """ Returns the scene's current image pixmap as a QImage, or else None if no image exists. :rtype: QImage | None """ if self.hasImage(): return self._pixmapHandle.pixmap().toImage() return None def setImage(self, image): """ Set the scene's current image pixmap to the input QImage or QPixmap. Raises a RuntimeError if the input image has type other than QImage or QPixmap. :type image: QImage | QPixmap """ if type(image) is QPixmap: pixmap = image elif type(image) is QImage: pixmap = QPixmap.fromImage(image) else: raise RuntimeError( "ImageViewer.setImage: Argument must be a QImage or QPixmap.") if self.hasImage(): self._pixmapHandle.setPixmap(pixmap) else: self._pixmapHandle = self.scene.addPixmap(pixmap) self.setSceneRect(QRectF( pixmap.rect())) # Set scene size to image size. self.updateViewer() def loadImageFromFile(self, fileName=""): """ Load an image from file. Without any arguments, loadImageFromFile() will popup a file dialog to choose the image file. With a fileName argument, loadImageFromFile(fileName) will attempt to load the specified image file directly. """ fileDir = os.path.dirname(os.path.realpath('__file__')) results_dir = os.path.join(fileDir, 'results\\200_frames_of_bloodflow_results') if len(fileName) == 0: if QT_VERSION_STR[0] == '4': fileName = QFileDialog.getOpenFileName(self, "Open image file.") elif QT_VERSION_STR[0] == '5': fileName, dummy = QFileDialog.getOpenFileName( self, "Open image file.") if len(fileName) and os.path.isfile(fileName): imageName, file_data = self.create_image(fileName, results_dir) image = QImage(imageName) self.setImage(image) print(fileName) def Extract_image_and_metadata(self, file_data): global file_metadata im2show_path = file_data["im2show_path"] metadata_path = file_data["metadata_path"] # fileName = file_data["fileName"] filepath = file_data["path"] if os.path.isfile(metadata_path): with open(metadata_path, 'r') as fp: file_metadata = json.load(fp) else: with tifffile.TiffFile(filepath) as tif: file_metadata = tif.scanimage_metadata with open(metadata_path, 'w') as fp: json.dump(file_metadata, fp) if not os.path.isfile(im2show_path): tif_original = tifffile.imread(filepath) # first_five = tif_full_original[1:10:2]#selecting 5 image from tif, only vessels tif_original_size = tif_original.shape if len(tif_original_size) == 2: tif = tif_original.copy() arr = tif else: first_five = tif_original[1:10:2].copy() if file_metadata["SI.hScan2D.bidirectional"]: num_im, w, h = first_five.shape tif = np.zeros((num_im, int(h / 2), w)) for i in range(num_im): tif[i] = first_five[i][::2] else: tif = tif_original.copy() arr = tif[0] tif_size = tif.shape print(tif_size) # image5 = (tif[0]/5)+(tif[1]/5)+(tif[2]/5)+(tif[3]/5)+(tif[4]/5) # arr_avg=np.array(np.round(image5),dtype=np.int16) #round the pixels values # arr = tif[0] im2show = Image.fromarray(arr) try: im2show.save(im2show_path) except OSError: im2show = im2show.convert(mode='I') im2show.save(im2show_path) return file_metadata def updateViewer(self): """ Show current zoom (if showing entire image, apply current aspect ratio mode). """ if not self.hasImage(): return if len(self.zoomStack) and self.sceneRect().contains( self.zoomStack[-1]): self.fitInView(self.zoomStack[-1], Qt.IgnoreAspectRatio ) # Show zoomed rect (ignore aspect ratio). else: self.zoomStack = [ ] # Clear the zoom stack (in case we got here because of an invalid zoom). self.fitInView( self.sceneRect(), self.aspectRatioMode ) # Show entire image (use current aspect ratio mode). def resizeEvent(self, event): """ Maintain current zoom on resize. """ self.updateViewer() def mousePressEvent(self, event): """ Start mouse pan or zoom mode. """ scenePos = self.mapToScene(event.pos()) if event.button() == Qt.LeftButton: if self.canPan and self.helper_bool: self.setDragMode(QGraphicsView.RubberBandDrag) elif self.canPan and self.helper_bool2: self.setDragMode(QGraphicsView.ScrollHandDrag) self.start = scenePos #QGraphicsView.mouseMoveEvent(self,event) #self.setMouseTracking(True) self.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y()) # self.cursorStartPosition = self.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y()) # self.start = QPoint(self.cursorStartPosition.x(),self.cursorStartPosition.y()) elif event.button() == Qt.RightButton: if self.canZoom: self.setDragMode(QGraphicsView.RubberBandDrag) self.rightMouseButtonPressed.emit(scenePos.x(), scenePos.y()) QGraphicsView.mousePressEvent(self, event) def mouseReleaseEvent(self, event): """ Stop mouse pan or zoom mode (apply zoom if valid). """ QGraphicsView.mouseReleaseEvent(self, event) scenePos = self.mapToScene(event.pos()) if event.button() == Qt.LeftButton: if self.helper_bool: viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() selectionBBox = self.scene.selectionArea().boundingRect( ).intersected(viewBBox) if selectionBBox.isValid(): self.rect.setRect(selectionBBox) self.rect.setPen(QPen(Qt.white)) self.scene.addItem(self.rect) elif self.helper_bool2: viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() self.cursorCurrentPosition = scenePos self.current = QPointF(self.cursorCurrentPosition.x(), self.cursorCurrentPosition.y()) pen = QPen(Qt.red, 1, Qt.SolidLine) self.line.setLine(QLineF(self.start, self.current)) self.line.setPen(pen) self.scene.addItem(self.line) self.setDragMode(QGraphicsView.NoDrag) self.leftMouseButtonReleased.emit(scenePos.x(), scenePos.y()) elif event.button() == Qt.RightButton: if self.canZoom: viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() selectionBBox = self.scene.selectionArea().boundingRect( ).intersected(viewBBox) self.scene.setSelectionArea( QPainterPath()) # Clear current selection area. if selectionBBox.isValid() and (selectionBBox != viewBBox): self.zoomStack.append(selectionBBox) self.updateViewer() self.setDragMode(QGraphicsView.NoDrag) self.rightMouseButtonReleased.emit(scenePos.x(), scenePos.y()) # self.scene.addItem(self.group) self.updateViewer() def mouseDoubleClickEvent(self, event): """ Show entire image. """ scenePos = self.mapToScene(event.pos()) if event.button() == Qt.LeftButton: self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y()) elif event.button() == Qt.RightButton: if self.canZoom: self.zoomStack = [] # Clear zoom stack. self.updateViewer() self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y()) QGraphicsView.mouseDoubleClickEvent(self, event) def approve_obj(self): self.scene.removeItem(self.rect) self.scene.removeItem(self.line) viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() selectionBBox = self.scene.selectionArea().boundingRect().intersected( viewBBox) rect = QGraphicsRectItem() rect.setRect(selectionBBox) rect.setPen(QPen(Qt.green)) self.rectgroup.addToGroup(rect) self.scene.addItem(self.rectgroup) line = QGraphicsLineItem() line.setLine(QLineF(self.start, self.current)) line.setPen(QPen(Qt.green)) self.linegroup.addToGroup(line) self.scene.addItem(self.linegroup) return (selectionBBox, self.start, self.current) def create_fromDB(self, pos_list=[], obj_type=""): if obj_type == "rois": num_rois = len(pos_list) for i in range(num_rois): points = pos_list[i] rect = QGraphicsRectItem() rect.setPen(QPen(Qt.green)) rect.setRect(points[0], points[1], points[2], points[3]) self.rectgroup.addToGroup(rect) self.scene.addItem(self.rectgroup) elif obj_type == "vector": num_vec = len(pos_list) for i in range(num_vec): points = pos_list[i] vec = QGraphicsLineItem() vec.setPen(QPen(Qt.green)) vec.setLine(points[0][0], points[0][1], points[1][0], points[1][1]) self.linegroup.addToGroup(vec) self.scene.addItem(self.linegroup) def mark_item(self, item_num): rect_items = self.rectgroup.childItems() line_items = self.linegroup.childItems() rect_item = rect_items[item_num] line_item = line_items[item_num] rect_item.setPen(QPen(Qt.red)) line_item.setPen(QPen(Qt.red)) rect_items.remove(rect_item) line_items.remove(line_item) for i in rect_items: i.setPen(QPen(Qt.green)) for i in line_items: i.setPen(QPen(Qt.green)) def delete_roi(self, num): self.scene.removeItem(self.rectgroup) self.scene.removeItem(self.linegroup) rect_item = self.rectgroup.childItems()[num] line_item = self.linegroup.childItems()[num] self.rectgroup.removeFromGroup(rect_item) self.linegroup.removeFromGroup(line_item) self.scene.addItem(self.rectgroup) self.scene.addItem(self.linegroup) def restart_obj(self): self.scene.removeItem(self.rectgroup) self.scene.removeItem(self.linegroup) rect_items = self.rectgroup.childItems() line_items = self.linegroup.childItems() for rect_item in rect_items: self.rectgroup.removeFromGroup(rect_item) for line_item in line_items: self.linegroup.removeFromGroup(line_item)
class Node(QGraphicsItemGroup): """ Parent class for all types of nodes. It contains all necessary functions to print a node in the GUI """ def __init__(self, node, parent, background, border, text, x=0, y=0, offset=20): """ Constructor for the node class. It generates all necessary variables and calls the draw function :param node: data node which it gets the data from :param parent: parent widget :param background: background color of the node :param border: border color for the node :param text: text color for the node :param x: x-position of the node :param y: y-position of the node :param offset: offset for the type to center it """ super().__init__() self.typeOffset = offset self.node = node self.parent = parent self.threatEdge = None self.counterEdge = None self.defaultEdge = None self.childEdges = [] self.parentEdges = [] self.headerGroup = QGraphicsItemGroup() self.attributes = QGraphicsItemGroup() self.footerGroup = QGraphicsItemGroup() self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.attributesHeight = 0 self.headerHeight = 0 self.printHeader(background, border, text) self.printAttributes(background, border, text) self.printFooter(background, border, text) self.setAcceptDrops(True) self.setPos(x, y) def getTypeRecursiveDown(self): """ Searches the children of a node to get a node with type != Conjunction If there is no other node with type != Conjunction, Conjunction will be returned :return: type of first child node with type != Conjunction or Conjunction """ if isinstance(self, Conjunction): for c in self.childEdges: if isinstance(c.dst, Conjunction): return c.dst.getTypeRecursiveDown() else: return type(c.dst) return type(self) else: return type(self) def getTypeRecursiveUp(self): """ Searches the parents of a node to get a node with type != Conjunction If there is no other node with type != Conjunction, Conjunction will be returned :return: type of first parent node with type != Conjunction or Conjunction """ if isinstance(self, Conjunction): for c in self.childEdges: if isinstance(c.dst, Conjunction): return c.dst.getTypeRecursiveUp() else: return type(c.dst) return type(self) else: return type(self) def addEdge(self, dst): """ Adds an child edge to this node an places the start of the arrow in the right place :param dst: destination node for the edge """ if isinstance(self, Threat) and dst.getTypeRecursiveDown() is Threat: edge = Edge(self, dst, -50) elif isinstance(self, Threat) and dst.getTypeRecursiveDown() is Countermeasure: edge = Edge(self, dst, 50) else: edge = Edge(self, dst, 0) self.parent.scene.addItem(edge) self.childEdges.append(edge) dst.parentEdges.append(self.childEdges[-1]) def actualizeEdges(self): """ Actualizes all child edges of this node so they start at the right position """ for e in self.childEdges: if isinstance(self, Threat) and e.dst.getTypeRecursiveDown() is Threat: e.offset = -50 elif isinstance(self, Threat) and e.dst.getTypeRecursiveDown() is Countermeasure: e.offset = 50 else: e.offset = 0 def fixParentEdgeRec(self): """ Fixes all starts of the parent edges so they start at the right position """ for p in self.parentEdges: if isinstance(p.start, Conjunction): p.start.fixParentEdgeRec() else: p.start.actualizeEdges() def getLeftRightChildren(self): """ Splits the children in to arrays with the same size :return: Tuple (left, right) with child elements split in to arrays """ left = [] right = [] neutralLeft = False for e in self.childEdges: if e.offset < 0: left.append(e.dst) elif e.offset > 0: right.append(e.dst) else: if neutralLeft is False: left.append(e.dst) neutralLeft = True else: right.append(e.dst) neutralLeft = False return left, right def printHeader(self, background, border, text): """ Prints the the header of the node. It contains the Node id, title and type :param background: background color of the node :param border: border color for the node :param text: text color for the node """ x = self.x() y = self.y() self.idText = QGraphicsTextItem() self.typeText = QGraphicsTextItem() self.titleText = QGraphicsTextItem() self.idRect = QGraphicsRectItem() self.typeRect = QGraphicsRectItem() self.titleRect = QGraphicsRectItem() self.typeText.setFont(Configuration.font) self.titleText.setFont(Configuration.font) self.idText.setFont(Configuration.font) self.typeText.setDefaultTextColor(QColor(text)) self.titleText.setDefaultTextColor(QColor(text)) self.idText.setDefaultTextColor(QColor(text)) self.titleText.setTextWidth(200) self.idText.setPlainText(self.node.id) self.typeText.setPlainText(type(self.node).__name__) self.titleText.setPlainText(self.node.title) titleHeight = int(self.titleText.boundingRect().height() / 20 + 0.5) * 20 self.idRect.setRect(x, y, 50, 20) self.typeRect.setRect(x + 50, y, 150, 20) self.titleRect.setRect(x, y + 20, 200, titleHeight) self.idRect.setBrush(QBrush(QColor(background))) self.typeRect.setBrush(QBrush(QColor(background))) self.titleRect.setBrush(QBrush(QColor(background))) self.idRect.setPen(QPen(QColor(border), 2)) self.typeRect.setPen(QPen(QColor(border), 2)) self.titleRect.setPen(QPen(QColor(border), 2)) self.idText.setPos(x, y - 2) self.typeText.setPos(x + self.typeOffset, y - 2) self.titleText.setPos(x, y + 18) self.headerHeight = titleHeight + 20 self.setToolTip(self.node.description) self.headerGroup.addToGroup(self.idRect) self.headerGroup.addToGroup(self.typeRect) self.headerGroup.addToGroup(self.titleRect) self.headerGroup.addToGroup(self.idText) self.headerGroup.addToGroup(self.typeText) self.headerGroup.addToGroup(self.titleText) self.addToGroup(self.headerGroup) def printAttributes(self, background, border, text): """ Prints the attributes of the node The attributes are a key, value pair :param background: background color of the node :param border: border color for the node :param text: text color for the node """ y = self.y() + self.headerHeight x = self.x() self.attributesHeight = 0 for k, v in self.node.attributes.items(): key = QGraphicsTextItem() key.setFont(Configuration.font) key.setDefaultTextColor(QColor(text)) key.setTextWidth(100) key.setPlainText(k) keyHeight = int(key.boundingRect().height() / 20 + 0.5) * 20 value = QGraphicsTextItem() value.setFont(Configuration.font) value.setDefaultTextColor(QColor(text)) value.setTextWidth(100) value.setPlainText(v) valueHeight = int(value.boundingRect().height() / 20 + 0.5) * 20 height = valueHeight if valueHeight > keyHeight else keyHeight keyRect = QGraphicsRectItem() keyRect.setRect(x, y, 100, height) valueRect = QGraphicsRectItem() valueRect.setRect(x + 100, y, 100, height) keyRect.setBrush(QBrush(QColor(background))) valueRect.setBrush(QBrush(QColor(background))) keyRect.setPen(QPen(QColor(border), 2)) valueRect.setPen(QPen(QColor(border), 2)) key.setPos(x, y - 2) value.setPos(x + 100, y - 2) self.attributes.addToGroup(keyRect) self.attributes.addToGroup(valueRect) self.attributes.addToGroup(key) self.attributes.addToGroup(value) y = y + height self.attributesHeight += height self.addToGroup(self.attributes) def redrawOptions(self, background, border, text): """ Redraws the node with option for the background, border and text color :param background: background color of the node :param border: border color for the node :param text: text color for the node """ y = self.y() x = self.x() self.prepareGeometryChange() for i in self.attributes.childItems(): self.attributes.removeFromGroup(i) self.parent.scene.removeItem(i) for i in self.headerGroup.childItems(): self.headerGroup.removeFromGroup(i) self.parent.scene.removeItem(i) for i in self.footerGroup.childItems(): self.footerGroup.removeFromGroup(i) self.parent.scene.removeItem(i) self.removeFromGroup(self.attributes) self.removeFromGroup(self.footerGroup) self.removeFromGroup(self.headerGroup) self.printHeader(background, border, text) self.printAttributes(background, border, text) self.printFooter(background, border, text) self.parent.scene.removeItem(self) self.parent.scene.addItem(self) self.setPos(x, y) def redraw(self): """ Redraws the node with standard colors """ self.redrawOptions(Qt.white, Qt.black, Qt.black) def printFooter(self, background, border, text): """ Prototype function for the footer. Implemented in the child classes :param background: background color of the node :param border: border color for the node :param text: text color for the node """ pass def setPos(self, x, y): """ Overloads setPos to set the position of the visible node in the data node :param x: X part of the position :param y: Y part of the position """ self.node.position = (x, y) super().setPos(x, y) def paint(self, painter, options, widget=None): """ Reimplementation for the paint function of the QGraphicsItemGroup. The Reimplementation is needed to print a proper border when the item is selected :param painter: The painter, which draws the node :param options: options for the paint job :param widget: widget of the Item """ myOption = QStyleOptionGraphicsItem(options) myOption.state &= ~QStyle.State_Selected super().paint(painter, myOption, widget=None) if options.state & QStyle.State_Selected: painter.setPen(QPen(Qt.black, 2, Qt.DotLine)) rect = QRect(self.boundingRect().x() - 1.5, self.boundingRect().y() - 1.5, self.boundingRect().x() + self.boundingRect().width() + 3, self.boundingRect().y() + self.boundingRect().height() + 0.5) painter.drawRect(rect) def selectChildren(self): """ Select all children """ self.setSelected(True) for i in self.childEdges: i.setSelected(True) i.dst.selectChildren() def delete(self): """ Deletes this node """ for e in self.parentEdges: self.parent.scene.removeItem(e) for e in self.childEdges: self.parent.scene.removeItem(e) self.parent.tree.removeNode(self.node.id) self.parent.scene.removeItem(self) def edit(self): """ Opens the edit dialog """ NodeEdit(self, self.parent).exec() def mouseDoubleClickEvent(self, event): """ Handles a double click on the node. The double click opens the edit window for this node :param event: click event """ self.edit() def dropEvent(self, event): """ Sets the correct position to the data node if the item is drag & dropped :param event: Drop event :return: Changed Value """ print("ok") self.node.position = (event.pos().x(), event.pos().y()) return super().dropEvent(self, event)
class NodeItem(QGraphicsItem): def __init__ (self, nodeobj, parent=None, view=None, state=1): super().__init__() self.edge = None self.linkIDs = None self.children = None self.childpos = None self.nodeobj = nodeobj self.style = FlGlob.mainwindow.style self.view = weakref.proxy(view) self.refID = parent.realid() if parent is not None else None self.state = state self.setrank(parent) self.setCursor(Qt.ArrowCursor) self.yoffset = 0 self.graphicsetup() self.setstate(state) def id (self): return (self.refID, self.nodeobj.ID) def realid (self): return self.nodeobj.ID def childlist (self, generate=False): ID = self.nodeobj.ID itemtable = self.view.itemtable if self.state == 1 and ID in itemtable and not self.iscollapsed(): if self.children and self.nodeobj.linkIDs == self.linkIDs and None not in [c() for c in self.children]: ret = self.children else: children = [] for child in self.nodeobj.linkIDs: if child in itemtable[ID]: item = itemtable[ID][child] else: continue children.append(weakref.ref(item)) self.linkIDs = self.nodeobj.linkIDs.copy() self.children = children ret = children else: ret = [] if generate: x = self.x() y = self.y() self.childpos = [] for target in ret: t = target() self.childpos.append((t.x()+t.boundingRect().left()-self.style.activemargin-x, t.y()-y)) if self.edge: if self.childpos != self.edge.childpos: self.edge.prepareGeometryChange() self.edge.sourceright = self.boundingRect().right() self.edge.update(self.edge.boundingRect()) return ret def setedge (self, edge): self.edge = edge edge.setX(self.x()) def setactive (self, active): if active: self.activebox.show() self.mainbox.setBrush(QBrush(self.altcolor)) else: self.activebox.hide() self.mainbox.setBrush(QBrush(self.maincolor)) def setselected (self, selected): if selected: self.selectbox.show() else: self.selectbox.hide() def setstate (self, state): self.state = state if state == 1: # normal self.show() self.graphgroup.setOpacity(1) self.shadowbox.show() elif state == 0: # ghost self.show() self.graphgroup.setOpacity(0.7) self.shadowbox.hide() elif state == -1: # hidden self.hide() def setplaymode (self, playmode): if playmode: self.setOpacity(0.5) else: self.setOpacity(1) def setY (self, y): parent = self.view.itembyID(self.refID) y += self.getyoffset() if self.edge is not None: self.edge.setY(y) super().setY(y) def setrank (self, parent): if parent is None: return if self.issubnode(): x = parent.x() self.setX(x) else: x = parent.x()+self.style.rankwidth self.setX(x) self.nudgechildren() if self.edge is not None: self.edge.setX(x) def nudgechildren (self): for child in self.childlist(): child().setrank(self) def getyoffset (self): if self.nodeobj.nodebank == -1: return self.yoffset else: return self.view.itembyID(self.refID).getyoffset() + self.yoffset def hide (self): super().hide() if self.edge: self.edge.hide() def show (self): super().show() if self.edge: self.edge.show() def issubnode (self): return self.nodeobj.nodebank is not -1 def isghost (self): return not self.state def realnode (self): return self.view.itembyID(self.nodeobj.ID) def isactive (self): return self.view.activenode is self def isselected (self): return self.view.selectednode is self def iscollapsed (self): return self.id() in self.view.collapsednodes def y_top (self): return self.y() - self.boundingRect().height()//2 def y_bottom (self): return self.y() + self.boundingRect().height()//2 def bulkshift (self, children, diff): self.setY(self.y() + diff) if children is None: children = [c() for c in self.childlist()] for child in children: child.bulkshift(None, diff) def treeposition (self, ranks=None): if ranks is None: ranks = dict() localranks = dict() children = [c() for c in self.childlist()] for child in children: localranks = child.treeposition(localranks) rank = self.x() // self.style.rankwidth if children: top = children[0].y_top() bottom = children[-1].y_bottom() self.setY((top+bottom)//2) localranks[rank] = [self.y_top, self.y_bottom] streeshift = None for r in localranks: if r in ranks: rankshift = ranks[r][1]() + self.style.rowgap - localranks[r][0]() if streeshift is None or rankshift > streeshift: streeshift = rankshift ranks[r][1] = localranks[r][1] else: ranks[r] = localranks[r] if streeshift: self.bulkshift(children, streeshift) return ranks def siblings (self): if self.refID is None: return None parent = self.view.nodecontainer.nodes[self.refID] if self.issubnode(): return parent.subnodes else: return parent.linkIDs def siblingabove (self): sibs = self.siblings() if sibs is None or self.nodeobj.ID not in sibs: return None myindex = sibs.index(self.nodeobj.ID) if myindex: sibID = (self.refID, sibs[myindex-1]) return self.view.itembyfullID(sibID) else: return None def siblingbelow (self): sibs = self.siblings() if sibs is None or self.nodeobj.ID not in sibs: return None myindex = sibs.index(self.nodeobj.ID) if len(sibs) > myindex+1: sibID = (self.refID, sibs[myindex+1]) return self.view.itembyfullID(sibID) else: return None def subtreesize (self, depth=-1): """Find vertical extents of a subtree. Returns min/max y coordinates up to given depth (negative depth means whole subtree).""" # calculate child positions for EgdeItem only once when calculating scenerect if depth<0: generate = True else: generate = False children = [c() for c in self.childlist(generate=generate)] maxdepth = abs(depth) if children and depth: nextdepth = depth-1 ymin = self.y_top() ymax = self.y_bottom() for child in children: top, bottom, depth = child.subtreesize(nextdepth) ymin = min(ymin, top) ymax = max(ymax, bottom) maxdepth = max(maxdepth, depth) else: ymin = self.y_top() ymax = self.y_bottom() return ymin, ymax, maxdepth def boundingRect (self): return self.rect def paint (self, painter, style, widget): pass def pixmap (self, path): return QPixmap(path).scaledToWidth(self.style.boldheight, Qt.SmoothTransformation) def graphicsetup (self): lightbrush = QBrush(FlPalette.light) mainbrush = QBrush(self.maincolor) altbrush = QBrush(self.altcolor) nopen = QPen(0) viewport = self.view.viewport() self.graphgroup = QGraphicsItemGroup(self) self.fggroup = QGraphicsItemGroup(self) self.shadowbox = QGraphicsRectItem(self) self.shadowbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.shadowbox.setBrush(FlPalette.dark) self.shadowbox.setPen(nopen) self.shadowbox.setPos(*(self.style.shadowoffset,)*2) self.graphgroup.addToGroup(self.shadowbox) self.activebox = QGraphicsRectItem(self) self.activebox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) activepen = QPen(self.maincolor, self.style.selectmargin, join=Qt.MiterJoin) self.activebox.setPen(activepen) self.activebox.hide() self.graphgroup.addToGroup(self.activebox) self.selectbox = QGraphicsRectItem(self) self.selectbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) selectpen = QPen(FlPalette.light, self.style.selectmargin, join=Qt.MiterJoin) self.selectbox.setPen(selectpen) self.selectbox.hide() self.graphgroup.addToGroup(self.selectbox) self.mainbox = QGraphicsRectItem(self) self.mainbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.mainbox.setBrush(mainbrush) self.mainbox.setPen(nopen) self.graphgroup.addToGroup(self.mainbox) self.nodelabel = QGraphicsSimpleTextItemCond(self, viewport) self.nodelabel.setBrush(lightbrush) self.nodelabel.setFont(self.style.boldfont) self.nodelabel.setText(self.label % self.realid()) self.nodelabel.setPos(self.style.itemmargin, self.style.itemmargin) self.fggroup.addToGroup(self.nodelabel) self.icon = self.pixmap("images/blank.png") self.iwidth = self.icon.width() self.iconx = self.style.nodetextwidth self.condicon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.condicon.setPos(self.iconx-self.iwidth, self.style.itemmargin) self.iconx = self.condicon.x() self.fggroup.addToGroup(self.condicon) self.randicon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.randicon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.randicon.x() self.fggroup.addToGroup(self.randicon) self.exiticon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.exiticon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.exiticon.x() self.fggroup.addToGroup(self.exiticon) self.entericon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.entericon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.entericon.x() self.fggroup.addToGroup(self.entericon) self.persisticon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.persisticon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.persisticon.x() self.fggroup.addToGroup(self.persisticon) self.comment = QGraphicsTextItemCond(self, viewport) self.comment.setTextWidth(self.style.nodetextwidth) self.comment.setDefaultTextColor(FlPalette.light) self.comment.setPos(0, self.nodelabel.y()+self.nodelabel.boundingRect().height()+self.style.itemmargin) self.fggroup.addToGroup(self.comment) self.graphgroup.addToGroup(self.fggroup) self.view.nodedocs[self.realid()]["comment"].contentsChanged.connect(self.updatecomment) self.updatecondition() self.updateenterscripts() self.updateexitscripts() self.updaterandweight() self.updatepersistence() # Never call updatelayout() from here (or any inheritable reimplementation)! def collapse (self, collapse): for item in self.fggroup.childItems(): if item is not self.nodelabel: if collapse: item.hide() else: item.show() self.updatelayout() def updatecondition (self): icons = {True: "key", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hascond()]) self.condicon.setPixmap(pixmap) if self.nodeobj.hascond(): self.condicon.setToolTip("Condition") else: self.condicon.setToolTip("") def updateenterscripts (self): icons = {True: "script-enter", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hasenterscripts()]) self.entericon.setPixmap(pixmap) if self.nodeobj.hasenterscripts(): self.entericon.setToolTip("Enter Scripts") else: self.entericon.setToolTip("") def updateexitscripts (self): icons = {True: "script-exit", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hasexitscripts()]) self.exiticon.setPixmap(pixmap) if self.nodeobj.hasexitscripts(): self.exiticon.setToolTip("Exit Scripts") else: self.exiticon.setToolTip("") def updaterandweight (self): icons = {True: "dice", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[bool(self.nodeobj.randweight)]) self.randicon.setPixmap(pixmap) if self.nodeobj.randweight: self.randicon.setToolTip("Random Weight: %s" % self.nodeobj.randweight) else: self.randicon.setToolTip("") def updatepersistence (self): icons = {"Mark": "mark", "OncePerConv": "once", "OnceEver": "onceever", "": "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.persistence]) self.persisticon.setPixmap(pixmap) if self.nodeobj.persistence: self.persisticon.setToolTip("Persistence: %s" % self.nodeobj.persistence) else: self.persisticon.setToolTip("") def updatecomment (self): self.fggroup.removeFromGroup(self.comment) contents = self.view.nodedocs[self.realid()]["comment"].toPlainText() if not contents: self.comment.hide() else: self.comment.show() self.comment.setPlainText(contents) self.fggroup.addToGroup(self.comment) self.updatelayout() def updatelayout (self): if self.iscollapsed(): rect = self.nodelabel.mapRectToParent(self.nodelabel.boundingRect()) else: rect = self.fggroup.childrenBoundingRect() mainrect = rect.marginsAdded(self.style.nodemargins) self.mainbox.setRect(mainrect) self.shadowbox.setRect(mainrect) self.selectbox.setRect(mainrect.marginsAdded(self.style.selectmargins)) activerect = mainrect.marginsAdded(self.style.activemargins) self.activebox.setRect(activerect) self.graphgroup.setPos(-activerect.width()//2-activerect.x(), -activerect.height()//2-activerect.y()) self.prepareGeometryChange() self.rect = self.graphgroup.mapRectToParent(mainrect) self.view.updatelayout() def mouseDoubleClickEvent (self, event): super().mouseDoubleClickEvent(event) event.accept() if event.button() == Qt.LeftButton: self.view.setactivenode(self) def mousePressEvent (self, event): super().mousePressEvent(event) if event.button() & (Qt.LeftButton | Qt.RightButton) : self.view.setselectednode(self) event.accept() def __repr__ (self): return "<%s %s>" % (type(self).__name__, self.id())
class MapWidget(QDialog): def __init__(self): QDialog.__init__(self) self.__mapWidgetUi = Ui_MapWidget() self.__mapWidgetUi.setupUi(self) self.__view = self.__mapWidgetUi.graphicsView self.__view.setObjectName("ACS_Map_View") self.__scene = QGraphicsScene() self.__view.setScene(self.__scene) self.__current_lat = 35.720428 self.__current_lon = -120.769924 self.__current_ground_width = 41000000. #meters(start w/ view of whole earth) #TODO: don't hard code location self.__tiler = acs_map_tiler.ACS_MapTiler(35.720428, -120.769924) self.__current_detail_layer = 0 #corresponds to "zoom" in map_tiler module self.__detail_layers = [] self.__rect_tiles = OrderedDict() #see rectKey method for how key works #detail layers are various levels of raster detail for the map. #0 is lowest level of detail, 20 highest. 0 loads fast and the #entire world can fit on a single tile. 20 loads slow, and it is unwise #to try to show the entire world due to the number of tiles required #(higher numbered detail layers are intended for "zooming in") self.setupDetailLayers() self.__plane_layer = QGraphicsItemGroup() self.__scene.addItem(self.__plane_layer) self.__plane_icons = {} self.__plane_labels = {} self.__plane_icon_pixmaps = {} img_bytes = pkg_resources.resource_stream("acs_dashboards", "data/images/flyingWingTiny.png").read() self.__plane_icon_pixmaps[0] = QPixmap() self.__plane_icon_pixmaps[0].loadFromData(img_bytes) img_bytes2 = pkg_resources.resource_stream("acs_dashboards", "data/images/flyingWingTiny2.png").read() self.__plane_icon_pixmaps[1] = QPixmap() self.__plane_icon_pixmaps[1].loadFromData(img_bytes2) #for drawing waypoints: self.__mission_layer = QGraphicsItemGroup() self.__scene.addItem(self.__mission_layer) self.__wp_diameter = 0.0001 self.__prev_drawn_nav_wp = None #needed when drawing lines between wps self.__wp_loiter_radius = None #NOTE: this is in meters; the map itself #is in degrees #for drawing fence: self.__fence_layer = QGraphicsItemGroup() self.__scene.addItem(self.__fence_layer) #slots self.__view.just_zoomed.connect(self.onZoom) self.__mapWidgetUi.zoom_sb.valueChanged.connect(self.onZoomSBValueChanged) self.__view.just_panned.connect(self.onPan) def getView(self): return self.__view def rectKey(self, x, y): '''rect_tiles key''' return (self.__current_detail_layer, x, y) #returns (min_lat, max_lat, min_lon, max_lon) as a tuple def extentsOfVisibleWorld(self): topLeft = self.__view.mapToScene(0,0) bottomRight = self.__view.mapToScene(self.__view.width(), self.__view.height()) return (-topLeft.y(), -bottomRight.y(), topLeft.x(), bottomRight.x()) #returns a list of TileInfos that are currently visible def tilesInVisibleWorld(self): (latTop, latBottom, lonLeft, lonRight) = self.extentsOfVisibleWorld() #print("Extents:", latTop, latBottom, lonLeft, lonRight, "\n") tile_info_list = self.__tiler.area_to_tile_list_lat_lon(latTop, latBottom, lonLeft, lonRight, self.__current_detail_layer) return tile_info_list def setupDetailLayers(self): #setup detail layers 0-20 for i in range(0,21): self.__detail_layers.append(QGraphicsItemGroup()) #add layer to scene: self.__scene.addItem(self.__detail_layers[i]) #hide all detail layers until it's time to show one: self.__detail_layers[i].hide() #show only the current detail layer self.setCurrentDetailLayer(0) def setCurrentDetailLayer(self, layerNum): self.__detail_layers[self.__current_detail_layer].hide() self.__detail_layers[layerNum].show() self.__current_detail_layer = layerNum self.__tiler.set_max_zoom(self.__current_detail_layer+1) #self.__tiler.prefetch() self.addTilesToCurrentDetailLayer() def createRectFromTileInfo(self, tile_info): #if Rectangle already exists, don't create it again key = self.rectKey(tile_info.x, tile_info.y) if key in self.__rect_tiles: return self.__rect_tiles[key] (y, x) = tile_info.coord() #TODO: do something about the hard coded 256s (end_y, end_x) = tile_info.coord((256, 256)) #y values need to reflect across the equator due to the origin in scene space #being on the top right (as opposed to bottom left in tile space) y = -y end_y = -end_y #keep things simple at the edges of the map: if y < -85.0: y = -85.0 if end_y > 85.0: end_y = 85.0 width = end_x - x height = end_y - y #create rectangle for the TileInfo and put it into the scene self.__rect_tiles[key] = QGraphicsRectItem(x, y, width, height, self.__detail_layers[self.__current_detail_layer]) #add raster data to the rect tile self.__tiler.load_tile(tile_info) #no border self.__rect_tiles[key].setPen(QPen(Qt.NoPen)) #remember the tiling data self.__rect_tiles[key].setData(0, tile_info) #attempt to add tile texture to rectangle: self.textureRect(self.__rect_tiles[key]) return self.__rect_tiles[key] def addTilesToCurrentDetailLayer(self): tile_info_list = self.tilesInVisibleWorld() for next_tile_info in tile_info_list: next_rect = self.createRectFromTileInfo(next_tile_info) #add rect tile to appropriate detail layer self.__detail_layers[self.__current_detail_layer].addToGroup(next_rect) #returns True if texture successfully applied, False otherwise #depth applies to how many zoom levels (or detail layers) we have traveled #while attempting to get a lower resolution texture def textureRect(self, rect_tile): tile_info = rect_tile.data(0) if tile_info is None: return False pm = QPixmap(self.__tiler.tile_to_path(tile_info)) if pm.width() != 256: #print("Probably didn't get tile:", next_tile_info.x, next_tile_info.y, "\n") #TODO: Attempt to texture with a lower res tile #Bear in mind that you will have to take Mercator projection into #account on the lower res tile. #First Attempt: didn't work #if tile_info.zoom <= self.__tiler.get_min_zoom(): # return False # #find colocated lower res tile #(lat,lon) = tile_info.coord() #tile_info2 = self.__tiler.coord_to_tile(lat,lon, tile_info.zoom-1) #rect_tile.setData(0, tile_info2) #print("prev tile: ", tile_info.tile, tile_info.coord()) #return self.textureRect(rect_tile, depth + 1) #until such time as we can pull lower res tiles and figure out #which area to render on a rectangle, skip: return False topLeft = rect_tile.boundingRect().topLeft() bottomRight = rect_tile.boundingRect().bottomRight() width = bottomRight.x() - topLeft.x() height = bottomRight.y() - topLeft.y() brush_trans = QTransform() brush_trans.translate(topLeft.x(), topLeft.y()) brush_trans.scale(width/256.0, height/256.0) qb = QBrush(pm) qb.setTransform(brush_trans) rect_tile.setBrush(qb) return True def checkForNewTextures(self): #ONLY care about rects in the current view: tile_info_list = self.tilesInVisibleWorld() for next_tile_info in tile_info_list: key = self.rectKey(next_tile_info.x, next_tile_info.y) # make sure key exists in self.__rect_tiles if key not in self.__rect_tiles: self.createRectFromTileInfo(next_tile_info) if self.__rect_tiles[key].brush().texture().width() != 256: self.textureRect(self.__rect_tiles[key]) #TODO: this code becomes applicable when lower res tiles become #available to higher zoom levels. #if self.__rect_tiles[key].data(0).zoom < self.__current_detail_layer: #lower res in there, try to get higher res #(remove tile info of the lower res tile): # del self.__rect_tiles[key] def clearFencePoints(self): children = self.__fence_layer.childItems() for child in children: self.__fence_layer.removeFromGroup(child) def clearWPs(self): children = self.__mission_layer.childItems() for child in children: self.__mission_layer.removeFromGroup(child) self.__prev_drawn_nav_wp = None def addWP(self, wp): if wp.command in [mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TURNS, mavutil.mavlink.MAV_CMD_NAV_LOITER_TIME, mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM, mavutil.mavlink.MAV_CMD_NAV_LAND]: #point rad = self.__wp_diameter * 0.5 ellipse = QGraphicsEllipseItem(wp.y - rad, -wp.x - rad, self.__wp_diameter, self.__wp_diameter, self.__mission_layer) ellipse.setBrush(QBrush(QColor(255, 255, 255))) e_pen = QPen(QColor(255, 255, 255)) e_pen.setWidth(0) ellipse.setPen(e_pen) self.__mission_layer.addToGroup(ellipse) #label label = QGraphicsTextItem(str(wp.seq), self.__mission_layer) label.setZValue(2) label.setDefaultTextColor(Qt.white) label.setPos(wp.y + rad, -wp.x - rad) label.setScale(0.00002) #bit hacky -- really should scale based on #current zoom, but I'm in a hurry. self.__mission_layer.addToGroup(label) label.show() #argument is a dictionary of waypoints (keys = ids) def drawNewMission(self, wps): self.clearWPs() self.drawMissionLines(wps) def drawWPLine(self, wp1, wp2): if wp1 is None or wp2 is None: print("Error: Can't draw line for Null endpoint") return rad = self.__wp_diameter * 0.5 mapped_wp1_x = wp1.y - rad mapped_wp1_y = -wp1.x #tangential approach? if (wp1.command == mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT and wp1.param1 == 1.0): l_rad = abs(self.__wp_loiter_radius) if l_rad is not None: dis = acs_math.gps_distance(wp1.x, wp1.y, wp2.x, wp2.y) theta = math.degrees(math.atan(l_rad / dis)) #sign of self.__wp_loiter_radius indicates the direction of the #loiter (negative is counter-clockwise, postive is clockwise) #Also, the waypoint itself can override the default setting #via param2 if ((wp1.param2 == 0 and self.__wp_loiter_radius > 0.0) or wp1.param2 > 0.0): theta = -theta tan_dis = math.sqrt( dis * dis - l_rad * l_rad) bearing = acs_math.gps_bearing(wp2.x, wp2.y, wp1.x, wp1.y) (tan_wp_x, tan_wp_y) = acs_math.gps_newpos(wp2.x, wp2.y, bearing - theta, tan_dis) mapped_wp1_x = tan_wp_y - rad mapped_wp1_y = -tan_wp_x next_line = QGraphicsLineItem(mapped_wp1_x, mapped_wp1_y, wp2.y - rad, -wp2.x, self.__mission_layer) l_pen = QPen(QColor(255, 255, 255)) l_pen.setWidth(0.00002) next_line.setPen(l_pen) self.__mission_layer.addToGroup(next_line) def setWPLoiterRad(self, rad): self.__wp_loiter_radius = rad def drawWPCircle(self, wp): if self.__wp_loiter_radius is None: print("Can't draw WP loiter circles; wp_loiter_radius not set.") return rad_deg = acs_math.gps_meters_to_degrees_lat(abs(self.__wp_loiter_radius), wp.x, wp.y) diameter = rad_deg * 2.0 ellipse = QGraphicsEllipseItem(wp.y - rad_deg, -wp.x - rad_deg, diameter, diameter, self.__mission_layer) #ellipse.setBrush(QBrush(QColor(255, 255, 255))) e_pen = QPen(QColor(255, 255, 255)) e_pen.setWidth(0.00002) ellipse.setPen(e_pen) self.__mission_layer.addToGroup(ellipse) def drawMissionLines(self, wps): for i in range(1, len(wps)): self.addWP(wps[i]) if wps[i].command in [mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TURNS, mavutil.mavlink.MAV_CMD_NAV_LOITER_TIME, mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM]: if self.__prev_drawn_nav_wp is not None: self.drawWPLine(self.__prev_drawn_nav_wp, wps[i]) if wps[i].command in [mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM, mavutil.mavlink.MAV_CMD_NAV_LOITER_TURNS, mavutil.mavlink.MAV_CMD_NAV_LOITER_TIME, mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT]: self.drawWPCircle(wps[i]) self.__prev_drawn_nav_wp = wps[i] elif wps[i].command == mavutil.mavlink.MAV_CMD_DO_JUMP: wp_id = wps[i].param1 if self.__prev_drawn_nav_wp is not None: self.drawWPLine(self.__prev_drawn_nav_wp, wps[wp_id]) self.__prev_drawn_nav_wp = None def drawNewFence(self, fps): self.clearFencePoints() prev_fp = None for i in range(1, len(fps)): if prev_fp is not None: prev_x = prev_fp.lon prev_y = -prev_fp.lat mapped_x = fps[i].lon mapped_y = -fps[i].lat next_line = QGraphicsLineItem(prev_x, prev_y, mapped_x, mapped_y, self.__fence_layer) l_pen = QPen(QColor(0, 255, 0)) l_pen.setWidth(0.1) next_line.setPen(l_pen) self.__fence_layer.addToGroup(next_line) prev_fp = fps[i] def updateIcon(self, id, uav_state, image_num=0): if uav_state.get_lon() == 0.0: #haven't received a Pose message yet return if id not in self.__plane_icons: #make the plane's label first label = QGraphicsTextItem(str(id), self.__plane_layer) label.setZValue(2) label.setDefaultTextColor(Qt.red) self.__plane_layer.addToGroup(label) label.show() self.__plane_labels[id] = label self.__plane_icons[id] = MapGraphicsIcon(id, label, self.__plane_layer) self.__plane_icons[id].centerIconAt(uav_state.get_lon(), -uav_state.get_lat()) self.__plane_icons[id].textureIcon(self.__plane_icon_pixmaps[image_num]) #plane icons need to be drawn on top of map tiles: self.__plane_icons[id].setZValue(1) self.__plane_layer.addToGroup(self.__plane_icons[id]) #key 0 = last update time self.__plane_icons[id].setData(0, 0) #refresh: #HACK: don't know why zooming works to refresh. Couldn't #get scene.invalidate() nor scene.update() to work self.onZoom(self.__current_detail_layer) #end if now = time.time() #if we don't have new pose data, then we don't update the plane icon if (self.__plane_icons[id].data(0) is None or self.__plane_icons[id].data(0) >= uav_state.get_last_pose_update()): return #place icon self.__plane_icons[id].centerIconAt(uav_state.get_lon(), -uav_state.get_lat()) #give correct heading: quat = uav_state.get_quat() heading = acs_math.yaw_from_quat(quat[0], quat[1], quat[2], quat[3]) self.__plane_icons[id].setHeading(heading) #set last update time self.__plane_icons[id].setData(0, now) def zoomTo(self, zoomLevel, lat = 0, lon = 0): self.__view.zoomTo(zoomLevel, lat, lon) def onZoom(self, zoom_level): self.setCurrentDetailLayer(zoom_level) self.__mapWidgetUi.zoom_sb.setValue(zoom_level) for id, nextPlaneIcon in self.__plane_icons.items(): nextPlaneIcon.scaleByViewAndTexture(self.__view) def onZoomSBValueChanged(self, new_zoom): if (int(self.__view.getCurrentZoom()) != new_zoom): self.__view.zoomTo(new_zoom) def onPan(self, new_lat, new_lon): lat_lon_str = str(new_lat) + ", " + str(new_lon) self.__mapWidgetUi.coords_lb.setText(lat_lon_str) def hideUAVIcon(self, id): if id in self.__plane_icons: self.__plane_icons[id].hide() self.__plane_labels[id].hide() def showUAVIcon(self, id): if id in self.__plane_icons: self.__plane_icons[id].show() self.__plane_labels[id].show()