class GameWidget(QGraphicsView): def __init__(self, parent=None): super(GameWidget, self).__init__(parent) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.m = 150 self.n = 150 self.scene = QGraphicsScene(self) self.setScene(self.scene) self.scene.setSceneRect(0, 0, self.width(), self.height()) self.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.universe = engine.Universe(self.n, self.m) print(Qt.red) self.timer = QTimer(self) self.timer.timeout.connect(self.step) def load(self, item): self.universe.file_input(item) def drawUniverse(self): width = self.width() height = self.height() cellWidth = width / self.universe.n cellHeight = height / self.universe.m x = 0 y = 0 for this_list in self.universe.map: y = 0 for elem in this_list: color = Qt.gray if elem == 1: color = Qt.green self.scene.addRect(x, y, cellWidth, cellHeight, brush=color) y += cellHeight x += cellWidth def step(self): self.scene.clear() self.universe.step() self.drawUniverse() self.scene.update() def run(self): self.timer.start() def stop(self): self.timer.stop()
def create_ui(self): button = QPushButton("Rotate - ", self) button.setGeometry(200, 450, 100, 50) button.clicked.connect(self.rotateMinus) button2 = QPushButton("Rotate + ", self) button2.setGeometry(320, 450, 100, 50) button2.clicked.connect(self.rotatePlus) scene = QGraphicsScene(self) greenBrush = QBrush(Qt.green) blueBrush = QBrush(Qt.blue) blackPen = QPen(Qt.black) blackPen.setWidth(5) ellipse = scene.addEllipse(10, 10, 200, 200, blackPen, greenBrush) rect = scene.addRect(-100, -100, 200, 200, blackPen, blueBrush) scene.addText("antoine-libert.com", QFont("Sanserif", 15)) ellipse.setFlag(QGraphicsItem.ItemIsMovable) rect.setFlag(QGraphicsItem.ItemIsMovable) self.view = QGraphicsView(scene, self) self.view.setGeometry(0, 0, 640, 440)
def _create_obj_group_renderer(self, object_class_name): display_icon = self.display_icons.get(object_class_name, -1) icon_code, color_code = interpret_icon_id(display_icon) font = QFont('Font Awesome 5 Free Solid') scene = QGraphicsScene() x = 0 for _ in range(2): y = 0 for _ in range(2): text_item = scene.addText(chr(icon_code), font) text_item.setDefaultTextColor(color_code) text_item.setPos(x, y) y += 0.875 * text_item.boundingRect().height() x += 0.875 * text_item.boundingRect().width() scene.addRect(scene.itemsBoundingRect()) self.obj_group_renderers[ object_class_name] = _SceneSvgRenderer.from_scene(scene)
def waferPainter(self): blackPen = QPen(Qt.black) blackPen.setWidth(1) blueBrush = QBrush(Qt.blue) scene = QGraphicsScene() chipNumX = 100 chipNumY = 100 canvasWidth = self.ui.mapView.width() canvasHeight = self.ui.mapView.height() chipWidthX = (canvasWidth - 5) / chipNumX chipHeightY = (canvasHeight - 5) / chipNumY waferEllipse = QGraphicsEllipseItem() scene.addEllipse(0, 0, canvasWidth - 5, canvasHeight - 5, blackPen) for numx in range(0, chipNumX): for numy in range(0, chipNumY): scene.addRect(numx * chipWidthX, numy * chipHeightY, chipWidthX, chipHeightY, blackPen, blueBrush) # --》隐藏graphicsView的滚动条 self.ui.mapView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.ui.mapView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.ui.mapView.setScene(scene)
class ReferenceCount(UsesQApplication): def setUp(self): super(ReferenceCount, self).setUp() self.scene = QGraphicsScene() def tearDown(self): super(ReferenceCount, self).tearDown() def beforeTest(self): points = [QPointF(0, 0), QPointF(100, 100), QPointF(0, 100)] pol = self.scene.addPolygon(QPolygonF(points)) self.assert_(isinstance(pol, QGraphicsPolygonItem)) self.wrp = weakref.ref(pol, pol_del) #refcount need be 3 because one ref for QGraphicsScene, and one to rect obj self.assertEqual(sys.getrefcount(pol), 3) def testReferenceCount(self): global destroyedRect global destroyedPol self.beforeTest() rect = self.scene.addRect(10.0, 10.0, 10.0, 10.0) self.assert_(isinstance(rect, QGraphicsRectItem)) self.wrr = weakref.ref(rect, rect_del) #refcount need be 3 because one ref for QGraphicsScene, and one to rect obj self.assertEqual(sys.getrefcount(rect), 3) del rect #not destroyed because one ref continue in QGraphicsScene self.assertEqual(destroyedRect, False) self.assertEqual(destroyedPol, False) del self.scene #QGraphicsScene was destroyed and this destroy internal ref to rect self.assertEqual(destroyedRect, True) self.assertEqual(destroyedPol, True)
def __init__(self): super(Window, self).__init__() self.setWindowTitle("Pyside2 Simple Application") self.setGeometry(300, 300, 300, 300) scene = QGraphicsScene(self) greenBrush = QBrush(Qt.green) blueBrush = QBrush(Qt.blue) blackPen = QPen(Qt.black) blackPen.setWidth(5) ellipse = scene.addEllipse(10, 10, 200, 200, blackPen, greenBrush) rect = scene.addRect(-100, -100, 200, 200, blackPen, blueBrush) ellipse.setFlag(QGraphicsItem.ItemIsMovable) rect.setFlag(QGraphicsItem.ItemIsMovable) self.view = QGraphicsView(scene, self) self.setCentralWidget(self.view) self.view.rotate(30)
class QPOIViewer(QWidget): """ POI Viewer QWidget """ TAG_SPACING = 50 LEGEND_X = -50 LEGEND_Y = 0 LEGEND_WIDTH = 10 TRACE_FUNC_X = 0 TRACE_FUNC_Y = 0 TRACE_FUNC_WIDTH = 50 TRACE_FUNC_MINHEIGHT = 1000 TAB_HEADER_SIZE = 40 MAX_WINDOW_SIZE = 500 MARK_X = LEGEND_X MARK_WIDTH = TRACE_FUNC_X - LEGEND_X + TRACE_FUNC_WIDTH MARK_HEIGHT = 1 POIID_COLUMN = 0 CRASH_COLUMN = 1 CATEGORY_COLUMN = 2 DIAGNOSE_COLUMN = 3 COLUMN_FIELD = ['id', 'bbl', 'category', 'diagnose'] def __init__(self, workspace, parent=None, diagnose_handler=None): super().__init__(parent=parent) self.workspace = workspace self._diagnose_handler = diagnose_handler self.mark = None self.legend = None self.legend_height = 0 self.legend_img = None self.trace_func_unit_height = 0 self.trace_func = None self.trace_id = None self.tabView = None self.traceView = None self.traceScene = None self.POITraceTab = None self.multiPOITab : QWidget = None self.multiPOIList : QTableWidget = None self.mark = None self.curr_position = 0 self._use_precise_position = False self._selected_traces = [] self._selected_poi = None self._init_widgets() self.selected_ins.am_subscribe(self._subscribe_select_ins) self.poi_trace.am_subscribe(self._subscribe_set_trace) self.multi_poi.am_subscribe(self._subscribe_add_poi) self.multiPOIList.cellDoubleClicked.connect(self._on_cell_double_click) self.multiPOIList.itemChanged.connect(self._on_diagnose_change) # # Forwarding properties # @property def disasm_view(self): """ Get the current disassembly view (if there is one), or create a new one as needed. """ view = self.workspace.view_manager.current_view_in_category("disassembly") if view is None: view = self.workspace._get_or_create_disassembly_view() return view @property def poi_trace(self): return self.workspace.instance.poi_trace @property def multi_poi(self): return self.workspace.instance.multi_poi @property def selected_ins(self): return self.disasm_view.infodock.selected_insns def _init_widgets(self): _l.debug("QPOI Viewer Initiating") self.tabView = QTabWidget() # QGraphicsView() self.tabView.setContentsMargins(0, 0, 0, 0) # # POI trace Tab # self.POITraceTab = QWidget() self.POITraceTab.setContentsMargins(0, 0, 0, 0) singleLayout = QVBoxLayout() singleLayout.setSpacing(0) singleLayout.setContentsMargins(0, 0, 0, 0) self.traceView = QGraphicsView() self.traceScene = QGraphicsScene() self.traceView.setScene(self.traceScene) singleLayout.addWidget(self.traceView) self.POITraceTab.setLayout(singleLayout) # # multiPOI Tab # self.multiPOITab = QMultiPOITab(self) # self.multiPOITab = QWidget() multiLayout = QVBoxLayout() multiLayout.setSpacing(0) multiLayout.setContentsMargins(0, 0, 0, 0) self.multiPOIList = QTableWidget(0, 4) # row, col self.multiPOIList.setHorizontalHeaderItem(0, QTableWidgetItem("ID")) self.multiPOIList.setHorizontalHeaderItem(1, QTableWidgetItem("Crash Point")) self.multiPOIList.setHorizontalHeaderItem(2, QTableWidgetItem("Tag")) self.multiPOIList.setHorizontalHeaderItem(3, QTableWidgetItem("Diagnose")) self.multiPOIList.horizontalHeader().setStretchLastSection(True) self.multiPOIList.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) self.multiPOIList.setSelectionBehavior(QAbstractItemView.SelectRows) multiLayout.addWidget(self.multiPOIList) self.multiPOITab.setLayout(multiLayout) self.tabView.addTab(self.multiPOITab, "POI List") self.tabView.addTab(self.POITraceTab, "POI Trace") self.POI_TRACE = 1 self.MULTI_POI = 0 layout = QVBoxLayout() layout.addWidget(self.tabView) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.show() def _reset(self): self.traceScene.clear() #clear items self.mark = None self.legend = None self.legend_height = 0 self.trace_func = QGraphicsItemGroup() self.trace_id = QGraphicsItemGroup() self.traceScene.addItem(self.trace_func) self.hide() # # Event # def _on_cell_double_click(self, row, _): _l.debug("row %d is double clicked", row) first_cell = self.multiPOIList.item(row, 0) if first_cell is None: return poi_id = first_cell.text() poi = self.multi_poi.am_obj.get_poi_by_id(poi_id) if poi is None: return # sanity checks if not isinstance(poi, dict): return if 'output' not in poi or not isinstance(poi['output'], dict): return if 'bbl_history' not in poi['output']: return trace = poi['output']['bbl_history'] if self._selected_poi != poi_id and trace is not None: # render the trace self.poi_trace.am_obj = TraceStatistics(self.workspace, trace, trace_id=poi_id) # show the trace statistic in POI trace self.poi_trace.am_event() # show covered basic blocks and functions self.multi_poi.am_obj.reload_heatmap(poi_id) # redraw function view view = self.workspace.view_manager.first_view_in_category('functions') if view is not None: view.refresh() # redraw disassembly view view = self.workspace.view_manager.first_view_in_category('disassembly') if view is not None: view.redraw_current_graph() if trace is not None: # switch to POI trace tab self.tabView.setCurrentIndex(self.POI_TRACE) self._selected_poi = poi_id second_cell = self.multiPOIList.item(row, 1) crash_addr = None if second_cell is not None: try: crash_addr = int(second_cell.text(), 16) except ValueError: pass if crash_addr is not None: # show the crashing address view = self.workspace.view_manager.first_view_in_category('disassembly') if view is not None: crash_func = self._get_func_from_addr(crash_addr) if crash_func is not None: self.workspace.on_function_selected(crash_func) self.selected_ins.clear() self.selected_ins.update([crash_addr]) self.selected_ins.am_event() view.current_graph.show_instruction(crash_addr) def _on_diagnose_change(self, item: QTableWidgetItem): column = item.column() row = item.row() poi_id = self.multiPOIList.item(row, self.POIID_COLUMN).text() content = item.text() original_content = self.multi_poi.am_obj.get_content_by_id_column(poi_id, column) _l.debug('updaing %s, content: %s, original: %s', poi_id, content, original_content) if not self._is_identical(content, original_content): updated_poi = self.multi_poi.update_poi(poi_id, column, content) self._diagnose_handler.submit_updated_poi(poi_id, updated_poi) def _subscribe_add_poi(self): _l.debug('add a poi to multi poi list') if self.multi_poi.am_none: self.multi_poi.am_obj = MultiPOI(self.workspace) poi_ids = self.multi_poi.am_obj.get_all_poi_ids() self.multiPOIList.clearContents() self._populate_poi_table(self.multiPOIList, poi_ids) self.show() def _subscribe_set_trace(self): _l.debug('on set trace in poi trace viewer') self._reset() if self.poi_trace.am_none: return _l.debug('minheight: %d, count: %d', self.TRACE_FUNC_MINHEIGHT, self.poi_trace.count) if self.poi_trace.count <= 0: _l.warning("No valid addresses found in trace to show. Check base address offsets?") self.poi_trace.am_obj = None self.poi_trace.am_event() return if self.TRACE_FUNC_MINHEIGHT < self.poi_trace.count * 15: self.trace_func_unit_height = 15 show_func_tag = True else: self.trace_func_unit_height = self.TRACE_FUNC_MINHEIGHT / self.poi_trace.count show_func_tag = True self.legend_height = int(self.poi_trace.count * self.trace_func_unit_height) self._show_trace_func(show_func_tag) self._show_legend() self._set_mark_color() self._refresh_multi_list() # boundingSize = self.traceScene.itemsBoundingRect().width() # windowSize = boundingSize # if boundingSize > self.MAX_WINDOW_SIZE: # windowSize = self.MAX_WINDOW_SIZE # self.traceScene.setSceneRect(self.traceScene.itemsBoundingRect()) #resize # if windowSize > self.width(): # self.setMinimumWidth(windowSize) self.show() def _subscribe_select_ins(self, **kwargs): # pylint: disable=unused-argument if self.poi_trace.am_none: return if self.mark is not None: for i in self.mark.childItems(): self.mark.removeFromGroup(i) self.traceScene.removeItem(i) self.traceScene.removeItem(self.mark) self.mark = QGraphicsItemGroup() self.traceScene.addItem(self.mark) if self.selected_ins: addr = next(iter(self.selected_ins)) positions = self.poi_trace.get_positions(addr) if positions: #if addr is in list of positions # handle case where insn was selected from disas view if not self._use_precise_position: self.curr_position = positions[0] - self.poi_trace.count for p in positions: color = self._get_mark_color(p, self.poi_trace.count) y = self._get_mark_y(p) if p == self.poi_trace.count + self.curr_position: #add thicker line for 'current' mark self.mark.addToGroup(self.traceScene.addRect(self.MARK_X, y, self.MARK_WIDTH, self.MARK_HEIGHT*4, QPen(QColor('black')), QBrush(color))) else: self.mark.addToGroup(self.traceScene.addRect(self.MARK_X, y, self.MARK_WIDTH, self.MARK_HEIGHT, QPen(color), QBrush(color))) self.traceScene.update() #force redraw of the traceScene self.scroll_to_position(self.curr_position) def _get_func_from_addr(self, addr): if self.workspace.instance.cfg.am_none: return None bbl = self.workspace.instance.cfg.get_any_node(addr, anyaddr=True) function_addr = bbl.function_address return self.workspace.instance.project.kb.functions.get(function_addr) def _populate_poi_table(self, view, poi_ids): view.clearContents() view.setRowCount(len(poi_ids)) row = 0 #start after label row for poi_id in poi_ids: poi = self.multi_poi.am_obj.get_poi_by_id(poi_id) _l.debug('populating poi: %s', poi) category = poi['category'] output = poi['output'] crash_addr = output['bbl'] if crash_addr is not None: crash = hex(crash_addr) else: crash = None diagnose = output.get('diagnose') _l.debug('poi_ids: %s', poi_ids) _l.debug('current poi id: %s', poi_id) self._set_item(view, row, self.POIID_COLUMN, poi_id, editable=False) self._set_item(view, row, self.CRASH_COLUMN, crash, editable=True) self._set_item(view, row, self.CATEGORY_COLUMN, category, editable=True) self._set_item(view, row, self.DIAGNOSE_COLUMN, diagnose, editable=True) row += 1 _l.debug('poi_ids: %s', poi_ids) @staticmethod def _set_item(view, row, column, text, editable=True): if not text: text = "" item = QTableWidgetItem(text) if not editable: item.setFlags(item.flags() ^ Qt.ItemIsEditable) view.setItem(row, column, item) def _refresh_multi_list(self): multiPOI = self.multi_poi.am_obj trace_ids = multiPOI.get_all_poi_ids() self.multiPOIList.clearContents() self._populate_poi_table(self.multiPOIList, trace_ids) if self._selected_traces and self.multiPOIList.rowCount() > 0: self.multiPOIList.item(0, 0).setSelected(True) self.multiPOIList.item(0, 1).setSelected(True) else: for row in range(self.multiPOIList.rowCount()): item = self.multiPOIList.item(row, 0) inputItem = self.multiPOIList.item(row, 1) if item.text() in self._selected_traces: item.setSelected(True) inputItem.setSelected(True) self.multi_poi.am_event() def _on_tab_change(self): multiPOI = self.multi_poi.am_obj if self.tabView.currentIndex() == self.MULTI_POI: multiPOI.is_active_tab = True self._refresh_multi_list() elif self.tabView.currentIndex() == self.POI_TRACE: multiPOI = self.multi_poi.am_obj multiPOI.is_active_tab = False # self._show_trace_ids() def scroll_to_position(self, position): relative_pos = self.poi_trace.count + position y_offset = self._get_mark_y(relative_pos) scrollValue = 0 if y_offset > 0.5 * self.traceView.size().height(): scrollValue = y_offset - 0.5 * self.traceView.size().height() scrollValue = min(scrollValue, self.traceView.verticalScrollBar().maximum()) self.traceView.verticalScrollBar().setValue(scrollValue) self._use_precise_position = False def jump_next_insn(self): # for some reason indexing is done backwards if self.curr_position + self.poi_trace.count < self.poi_trace.count - 1: self.curr_position += 1 self._use_precise_position = True bbl_addr = self.poi_trace.get_bbl_from_position(self.curr_position) func = self.poi_trace.get_func_from_position(self.curr_position) self._jump_bbl(func, bbl_addr) def jump_prev_insn(self): if self.curr_position + self.poi_trace.count > 0: self.curr_position -= 1 self._use_precise_position = True bbl_addr = self.poi_trace.get_bbl_from_position(self.curr_position) func = self.poi_trace.get_func_from_position(self.curr_position) self._jump_bbl(func, bbl_addr) def mousePressEvent(self, event): button = event.button() pos = self._to_logical_pos(event.pos()) if button == Qt.LeftButton and self.tabView.currentIndex() == self.POI_TRACE and self._at_legend(pos): func = self._get_func_from_y(pos.y()) bbl_addr = self._get_bbl_from_y(pos.y()) self._use_precise_position = True self.curr_position = self._get_position(pos.y()) self._jump_bbl(func, bbl_addr) def _jump_bbl(self, func, bbl_addr): disasm_view = self.disasm_view if disasm_view is not None: all_insn_addrs = self.workspace.instance.project.factory.block(bbl_addr).instruction_addrs # TODO: replace this with am_events perhaps? self.workspace.on_function_selected(func) self.selected_ins.clear() self.selected_ins.update(all_insn_addrs) self.selected_ins.am_event() # TODO: this ought to happen automatically as a result of the am_event disasm_view.current_graph.show_instruction(bbl_addr) def _get_mark_color(self, i, total): relative_gradient_pos = i * 1000 // total return self.legend_img.pixelColor(self.LEGEND_WIDTH // 2, relative_gradient_pos) def _get_mark_y(self, i): return self.TRACE_FUNC_Y + self.trace_func_unit_height * i def _show_trace_func(self, show_func_tag=True): x = self.TRACE_FUNC_X y = self.TRACE_FUNC_Y prev_name = None for position in self.poi_trace.trace_func: func_name = position.func_name color = self.poi_trace.get_func_color(func_name) self.trace_func.addToGroup(self.traceScene.addRect(x, y, self.TRACE_FUNC_WIDTH, self.trace_func_unit_height, QPen(color), QBrush(color))) if show_func_tag is True and func_name != prev_name: tag = self.traceScene.addText(func_name, QFont("Source Code Pro", 7)) tag.setPos(x + self.TRACE_FUNC_WIDTH + self.TAG_SPACING, y - tag.boundingRect().height() // 2) self.trace_func.addToGroup(tag) anchor = self.traceScene.addLine( self.TRACE_FUNC_X + self.TRACE_FUNC_WIDTH, y, x + self.TRACE_FUNC_WIDTH + self.TAG_SPACING, y) self.trace_func.addToGroup(anchor) prev_name = func_name y += self.trace_func_unit_height @staticmethod def _make_legend_gradient(x1, y1, x2, y2): gradient = QLinearGradient(x1, y1, x2, y2) gradient.setColorAt(0.0, Qt.red) gradient.setColorAt(0.4, Qt.yellow) gradient.setColorAt(0.6, Qt.green) gradient.setColorAt(0.8, Qt.blue) gradient.setColorAt(1.0, Qt.darkBlue) return gradient def _show_legend(self): pen = QPen(Qt.transparent) gradient = self._make_legend_gradient(self.LEGEND_X, self.LEGEND_Y, self.LEGEND_X, self.LEGEND_Y + self.legend_height) brush = QBrush(gradient) self.legend = self.traceScene.addRect(self.LEGEND_X, self.LEGEND_Y, self.LEGEND_WIDTH, self.legend_height, pen, brush) reference_gradient = self._make_legend_gradient(0, 0, self.LEGEND_WIDTH, 1000) base_img = QImage(self.LEGEND_WIDTH, 1000, QImage.Format.Format_ARGB32) p = QPainter(base_img) p.fillRect(base_img.rect(),reference_gradient) self.legend_img = base_img #reference shade def _set_mark_color(self): _l.debug('trace count: %d', self.poi_trace.count) for p in range(self.poi_trace.count): color = self._get_mark_color(p, self.poi_trace.count) self.poi_trace.set_mark_color(p, color) def _at_legend(self, pos): x = pos.x() y = pos.y() return self.TRACE_FUNC_X + self.LEGEND_X < x < self.traceView.width() and \ self.TRACE_FUNC_Y < y < self.TRACE_FUNC_Y + self.legend_height def _to_logical_pos(self, pos): x_offset = self.traceView.horizontalScrollBar().value() y_offset = self.traceView.verticalScrollBar().value() return QPoint(pos.x() + x_offset, pos.y() + y_offset) def _get_position(self, y): y_relative = y - self.legend_height - self.TAB_HEADER_SIZE return int(y_relative // self.trace_func_unit_height) def _get_bbl_from_y(self, y): position = self._get_position(y) return self.poi_trace.get_bbl_from_position(position) def _get_func_from_y(self, y): position = self._get_position(y) func = self.poi_trace.get_func_from_position(position) return func # # Context Menu # def menu_add_empty_poi(self): _l.debug('adding a new empty poi item') if self._diagnose_handler.get_image_id() is None: QMessageBox.warning(self.workspace.main_window, "No CHESS target available", "No angr project is loaded, or you did not associate the current project with a CHESS " "target. Please load a binary and associate it with a CHESS target before creating " "POIs.") return poi_id = str(uuid4()) if self.multi_poi.am_none: self.multi_poi.am_obj = MultiPOI(self.workspace) empty_poi = deepcopy(EMPTY_POI) self.multi_poi.add_poi(poi_id, empty_poi) self.multi_poi.am_event() self._diagnose_handler.submit_updated_poi(poi_id, empty_poi) def menu_remove_poi(self): items = self.multiPOIList.selectedItems() row = items.pop().row() poi_id = self.multiPOIList.item(row, 0).text() _l.debug('removing ID %s', poi_id) self.multi_poi.remove_poi(poi_id) self.multi_poi.am_event() @staticmethod def _is_identical(content, original_content): if content == original_content: return True if content == '' and original_content is None: return True try: if int(content, 16) == int(original_content): return True except (TypeError, ValueError): return False return False
class AddItem(UsesQApplication): '''Tests for QGraphicsScene.add*''' qapplication = True def setUp(self): #Acquire resources super(AddItem, self).setUp() self.scene = QGraphicsScene() # While the scene does not inherits from QWidget, requires # an application to make the internals work. def tearDown(self): #Release resources del self.scene super(AddItem, self).tearDown() def testEllipse(self): #QGraphicsScene.addEllipse item = self.scene.addEllipse(100, 100, 100, 100) self.assertTrue(isinstance(item, QGraphicsEllipseItem)) def testLine(self): #QGraphicsScene.addLine item = self.scene.addLine(100, 100, 200, 200) self.assertTrue(isinstance(item, QGraphicsLineItem)) def testPath(self): #QGraphicsScene.addPath item = self.scene.addPath(QPainterPath()) self.assertTrue(isinstance(item, QGraphicsPathItem)) def testPixmap(self): #QGraphicsScene.addPixmap item = self.scene.addPixmap(QPixmap()) self.assertTrue(isinstance(item, QGraphicsPixmapItem)) def testPolygon(self): #QGraphicsScene.addPolygon points = [QPointF(0, 0), QPointF(100, 100), QPointF(0, 100)] item = self.scene.addPolygon(QPolygonF(points)) self.assertTrue(isinstance(item, QGraphicsPolygonItem)) def testRect(self): #QGraphicsScene.addRect item = self.scene.addRect(100, 100, 100, 100) self.assertTrue(isinstance(item, QGraphicsRectItem)) def testSimpleText(self): #QGraphicsScene.addSimpleText item = self.scene.addSimpleText('Monty Python 42') self.assertTrue(isinstance(item, QGraphicsSimpleTextItem)) def testText(self): #QGraphicsScene.addText item = self.scene.addText('Monty Python 42') self.assertTrue(isinstance(item, QGraphicsTextItem)) def testWidget(self): #QGraphicsScene.addWidget # XXX: printing some X11 error when using under PyQt4 item = self.scene.addWidget(QPushButton()) self.assertTrue(isinstance(item, QGraphicsProxyWidget))
class MainWindow(QMainWindow): def __init__(self): # load setting json file with open('setting.json') as f: self.app_setting = json.load(f) # Status of window super().__init__() self.title = 'Image Editor' self.left = 70 self.top = 70 self.width = 800 self.height = 700 # Status of view image self.org_qimg = None self.org_img_width = 0 self.org_img_height = 0 self.layer_pixmap = None self.layer_width = 0 self.layer_height = 0 self.layer_alpha = 50.0 # Prepare color bar data self.colormap_gain = self.app_setting["SoftwareSetting"]["process"][ "colormap"]["gain"] self.colormap_offset_x = self.app_setting["SoftwareSetting"][ "process"]["colormap"]["offset_x"] self.colormap_offset_green = self.app_setting["SoftwareSetting"][ "process"]["colormap"]["offset_green"] self.colormap_data = [ colormap.colorBarRGB(x * 0.001, self.colormap_offset_x, self.colormap_offset_green, self.colormap_gain) for x in range(1000) ] self.img_edit_mode = 'cursor' self.draw_color = QColor(255, 0, 0) self.draw_tool_size = 5 self.eraser_color = QColor(0, 0, 0, 0) # setup user interface components self.setup_ui() # Setup user interface components def setup_ui(self): # Set main window title self.setWindowTitle(self.title) # Set main wiodow initial position self.setGeometry(self.left, self.top, self.width, self.height) # Set up mainWindow's layout self.mainWidget = QWidget(self) # Note not to forget this code. self.main_layout = QVBoxLayout() # Set menu for main window self.main_menu = self.menuBar() self.file_menu = self.main_menu.addMenu('File') self.edit_menu = self.main_menu.addMenu('Edit') self.help_menu = self.main_menu.addMenu('Help') self.main_layout.addWidget(self.main_menu) # Set "Original Image Open" menu self.org_img_open_button = QAction( self.style().standardIcon(getattr(QStyle, 'SP_FileDialogStart')), 'Open Orginal Image', self) self.org_img_open_button.setShortcut('Ctrl+O') self.org_img_open_button.triggered.connect(self.open_org_img_dialog) self.file_menu.addAction(self.org_img_open_button) # Set "Save layer image" menu self.layer_img_save_button = QAction( self.style().standardIcon(getattr(QStyle, 'SP_FileDialogEnd')), 'Save Layer Image', self) self.layer_img_save_button.setShortcut('Ctrl+S') self.layer_img_save_button.triggered.connect(self.save_layer_image) self.file_menu.addAction(self.layer_img_save_button) # Set "Save compose image(original + layer image)" menu self.compose_img_save_button = QAction( self.style().standardIcon(getattr(QStyle, 'SP_FileDialogEnd')), 'Save Compose Image', self) self.compose_img_save_button.setShortcut('Ctrl+D') self.compose_img_save_button.triggered.connect(self.save_compose_image) self.file_menu.addAction(self.compose_img_save_button) # Set "exit software" menu self.exit_button = QAction( self.style().standardIcon(getattr(QStyle, 'SP_DialogCloseButton')), 'Exit', self) self.exit_button.setShortcut('Ctrl-Q') self.exit_button.setStatusTip('Exit software') self.exit_button.triggered.connect(self.close) self.file_menu.addAction(self.exit_button) self.upper_layout = QHBoxLayout() self.main_layout.addLayout(self.upper_layout) # Set image display area self.gview_default_size = 500 self.graphics_view = QGraphicsView() self.graphics_view.setFixedSize(self.gview_default_size, self.gview_default_size) self.graphics_view.setObjectName("imageDisplayArea") self.upper_layout.addWidget(self.graphics_view) # image display area's contents self.scene = GraphicsSceneForMainView(self.graphics_view, self) self.imgs_pixmap = [] self.imgs = [] self.img_status_layout = QVBoxLayout() self.upper_layout.addLayout(self.img_status_layout) # Set tranparency value of layer image self.transparency_title_label = QLabel('layer transparency value') self.img_status_layout.addWidget(self.transparency_title_label) transparency = round((1.0 - self.layer_alpha / 255.0) * 100) self.img_transparency_edit = QLineEdit(str(transparency)) self.img_transparency_sld = QSlider(Qt.Horizontal) self.img_transparency_sld.setFocusPolicy(Qt.NoFocus) self.img_transparency_sld.setRange(0, 100) self.img_transparency_sld.setValue(transparency) self.transparency_layout = QFormLayout() self.transparency_layout.addRow(self.img_transparency_sld, self.img_transparency_edit) self.img_status_layout.addLayout(self.transparency_layout) # Signal of transparency value changed self.img_transparency_sld.valueChanged.connect( self.transparency_change_sld) self.img_transparency_edit.textChanged.connect( self.transparency_change_edit) self.img_editor_layout = QVBoxLayout() self.img_status_layout.addLayout(self.img_editor_layout) # Set layer image editor tool self.img_editor_tool1_layout = QHBoxLayout() self.img_editor_layout.addLayout(self.img_editor_tool1_layout) self.tool_button_size = 64 # Set Mouse cursor self.mouse_cursor_button = QPushButton() self.mouse_cursor_button.setIcon(QIcon('icon/cursor.png')) self.mouse_cursor_button.setCheckable(True) self.mouse_cursor_button.setIconSize( QSize(self.tool_button_size, self.tool_button_size)) self.img_editor_tool1_layout.addWidget(self.mouse_cursor_button) # Set Pen self.pen_button = QPushButton() self.pen_button.setIcon(QIcon('icon/pen.png')) self.pen_button.setCheckable(True) self.pen_button.setIconSize( QSize(self.tool_button_size, self.tool_button_size)) self.img_editor_tool1_layout.addWidget(self.pen_button) # Set Eraser self.eraser_button = QPushButton() self.eraser_button.setIcon(QIcon('icon/eraser.png')) self.eraser_button.setCheckable(True) self.eraser_button.setIconSize( QSize(self.tool_button_size, self.tool_button_size)) self.img_editor_tool1_layout.addWidget(self.eraser_button) # Group button of mouse cursor, pen, eraser self.img_editor_tool1_group1 = QButtonGroup() self.img_editor_tool1_group1.addButton(self.mouse_cursor_button, 1) self.img_editor_tool1_group1.addButton(self.pen_button, 2) self.img_editor_tool1_group1.addButton(self.eraser_button, 3) # Set signal-slot of image editor button self.mouse_cursor_button.toggled.connect( self.mouse_cursor_button_toggled) self.pen_button.toggled.connect(self.pen_button_toggled) self.eraser_button.toggled.connect(self.eraser_button_toggled) # Set color bar self.color_bar_width = 64 self.color_bar_height = 256 self.color_bar_view = QGraphicsView() self.color_bar_view.setFixedSize(self.color_bar_width + 3, self.color_bar_height + 3) self.color_bar_scene = GraphicsSceneForTools() self.color_bar_img = QImage(self.color_bar_width, self.color_bar_height, QImage.Format_RGB888) for i in range(self.color_bar_height): # Set drawing pen for colormap ii = round(i * (1000 / 256)) color = QColor(self.colormap_data[ii][0], self.colormap_data[ii][1], self.colormap_data[ii][2]) pen = QPen(color, 1, Qt.SolidLine, \ Qt.SquareCap, Qt.RoundJoin) self.color_bar_scene.addLine(0, self.color_bar_height - i - 1, self.color_bar_width, self.color_bar_height - i - 1, pen=pen) for j in range(self.color_bar_width): self.color_bar_img.setPixelColor(j, self.color_bar_height - i - 1, color) self.color_bar_scene.set_img_content(self.color_bar_img) self.color_bar_view.setScene(self.color_bar_scene) # Connect signal to slot of color_bar_scene self.color_bar_scene.img_info.connect(self.set_selected_color) self.img_editor_tool1_layout.addWidget(self.color_bar_view) # Set thickness of Pen or Eraser self.draw_status_layout = QVBoxLayout() self.draw_thick_title_label = QLabel('thickness of pen or eraser') self.draw_status_layout.addWidget(self.draw_thick_title_label) self.draw_thick_edit = QLineEdit(str(self.draw_tool_size)) self.draw_thick_sld = QSlider(Qt.Horizontal) self.draw_thick_sld.setFocusPolicy(Qt.NoFocus) self.draw_thick_sld.setRange(1, 30) self.draw_thick_sld.setValue(self.draw_tool_size) self.draw_thick_layout = QFormLayout() self.draw_thick_layout.addRow(self.draw_thick_sld, self.draw_thick_edit) self.draw_status_layout.addLayout(self.draw_thick_layout) self.img_editor_layout.addLayout(self.draw_status_layout) # Signal of draw thickness value changed self.draw_thick_sld.valueChanged.connect(self.draw_thick_change_sld) self.draw_thick_edit.textChanged.connect(self.draw_thick_change_edit) # Set view area of selected color self.select_color_view_size = 64 self.select_color_view = QGraphicsView() self.select_color_view.setFixedSize(self.select_color_view_size + 3, self.select_color_view_size + 3) self.select_color_scene = QGraphicsScene() brush = QBrush(self.draw_color) self.select_color_rect = self.select_color_scene.addRect(QRect(0, 0, self.select_color_view_size, self.select_color_view_size), \ brush=brush) self.select_color_view.setScene(self.select_color_scene) self.select_color_title_label = QLabel('Selected color') self.selected_color_layout = QFormLayout() self.selected_color_layout.addRow(self.select_color_title_label, self.select_color_view) self.img_editor_layout.addLayout(self.selected_color_layout) # Set save button self.save_button_layout = QHBoxLayout() self.img_status_layout.addLayout(self.save_button_layout) self.layer_save_button = QPushButton('Save layer image') self.layer_save_button.setIcon(QIcon('icon/layer_save.png')) self.layer_save_button.setIconSize( QSize(self.tool_button_size, self.tool_button_size)) self.compose_save_button = QPushButton( 'Save composed original and layer image') self.compose_save_button.setIcon(QIcon('icon/compose_save.png')) self.compose_save_button.setIconSize( QSize(self.tool_button_size, self.tool_button_size)) self.save_button_layout.addWidget(self.layer_save_button) self.save_button_layout.addWidget(self.compose_save_button) self.layer_save_button.clicked.connect(self.save_layer_image) self.compose_save_button.clicked.connect(self.save_compose_image) # Set display area of selected file path self.org_img_path_title_label = QLabel('original image file: ') self.org_img_path_label = QLabel('') self.file_path_layout = QFormLayout() self.file_path_layout.addRow(self.org_img_path_title_label, self.org_img_path_label) self.bottom_layout = QVBoxLayout() self.bottom_layout.addLayout(self.file_path_layout) self.main_layout.addLayout(self.bottom_layout) self.mainWidget.setLayout(self.main_layout) self.setCentralWidget(self.mainWidget) # Original image select Function def open_org_img_dialog(self): options = QFileDialog.Options() org_img_default_path = self.app_setting["SoftwareSetting"][ "file_path"]["org_img_dir"] self.org_img_file_path, selected_filter = QFileDialog.getOpenFileName(self, 'Select original image', org_img_default_path, \ 'Image files(*.jpg *jpeg *.png)', options=options) org_img_dir_path, org_img_file = os.path.split(self.org_img_file_path) org_img_bare_name, org_img_ext = os.path.splitext(org_img_file) self.org_img_path_label.setText(self.org_img_file_path) self.set_image_on_viewer() def set_image_on_viewer(self): # Delete existing image item if len(self.imgs_pixmap) != 0: for item in self.imgs_pixmap: self.scene.removeItem(item) self.scene.clear_contents() self.imgs_pixmap.clear() self.imgs.clear() # load original image self.org_qimg = QImage(self.org_img_file_path) self.org_pixmap = QPixmap.fromImage(self.org_qimg) org_img_size = self.org_qimg.size() self.org_img_width = org_img_size.width() self.org_img_height = org_img_size.height() # Set layer image self.layer_qimg = QImage(self.org_img_width, self.org_img_height, QImage.Format_RGBA8888) self.layer_qimg.fill(QColor(0, 0, 0, self.layer_alpha)) self.layer_pixmap = QPixmap.fromImage(self.layer_qimg) self.imgs.append(self.org_qimg) self.imgs.append(self.layer_qimg) # Set image to scene self.imgs_pixmap.append(QGraphicsPixmapItem(self.org_pixmap)) self.scene.addItem(self.imgs_pixmap[-1]) self.imgs_pixmap.append(QGraphicsPixmapItem(self.layer_pixmap)) self.scene.addItem(self.imgs_pixmap[-1]) self.scene.set_img_contents(self.imgs) # Set scene to graphics view self.graphics_view.setScene(self.scene) self.graphics_view.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.show() # Slot function of transparency slider changed def transparency_change_sld(self, value): self.img_transparency_edit.setText(str(value)) self.layer_alpha = int(255 * (1.0 - (value / 100))) # Change layer image's transparency(alpha value) for y in range(self.org_img_height): for x in range(self.org_img_width): self.layer_qimg.setPixelColor( QPoint(x, y), QColor(0, 0, 0, self.layer_alpha)) self.layer_pixmap = QPixmap.fromImage(self.layer_qimg) # remove previous layer image self.scene.removeItem(self.imgs_pixmap[-1]) self.imgs_pixmap.pop(-1) # add new layer image to scene self.imgs_pixmap.append(QGraphicsPixmapItem(self.layer_pixmap)) self.scene.addItem(self.imgs_pixmap[-1]) self.show() # Slot function of transparency text edit changed def transparency_change_edit(self, value): if int(value) < 0 or int(value) > 100: return self.img_transparency_sld.setValue(int(value)) self.layer_alpha = int(255 * (1.0 - (int(value) / 100.0))) # Change layer image's transparency(alpha value) for y in range(self.org_img_height): for x in range(self.org_img_width): self.layer_qimg.setPixelColor( QPoint(x, y), QColor(0, 0, 0, self.layer_alpha)) self.layer_pixmap = QPixmap.fromImage(self.layer_qimg) # remove previous layer image self.scene.removeItem(self.imgs_pixmap[-1]) self.imgs_pixmap.pop(-1) # add new layer image to scene self.imgs_pixmap.append(QGraphicsPixmapItem(self.layer_pixmap)) self.scene.addItem(self.imgs_pixmap[-1]) self.show() # slot(receiver of signal) of mouse_cursor_button toggled def mouse_cursor_button_toggled(self, checked): if checked: self.img_edit_mode = 'cursor' self.scene.set_mode(self.img_edit_mode) self.color_bar_scene.set_mode(self.img_edit_mode) # slot(receiver of signal) of pen_button toggled def pen_button_toggled(self, checked): if checked: self.img_edit_mode = 'pen' self.scene.set_mode(self.img_edit_mode) self.color_bar_scene.set_mode(self.img_edit_mode) # slot(receiver of signal) of eraser_button toggled def eraser_button_toggled(self, checked): if checked: self.img_edit_mode = 'eraser' self.scene.set_mode(self.img_edit_mode) self.color_bar_scene.set_mode(self.img_edit_mode) self.draw_color = self.eraser_color self.select_color_scene.removeItem(self.select_color_rect) brush = QBrush(self.draw_color) self.select_color_rect = self.select_color_scene.addRect(QRect(0, 0, self.select_color_view_size, self.select_color_view_size), \ brush=brush) self.select_color_view.setScene(self.select_color_scene) # Slot of color bar clicked for selection color def set_selected_color(self, color): # Delete existng image item self.select_color_scene.removeItem(self.select_color_rect) self.draw_color = color brush = QBrush(self.draw_color) self.select_color_rect = self.select_color_scene.addRect(QRect(0, 0, self.select_color_view_size, self.select_color_view_size), \ brush=brush) self.select_color_view.setScene(self.select_color_scene) # Slot function of draw thicikeness slider changed def draw_thick_change_sld(self, value): self.draw_thickness_edit.setText(str(value)) self.draw_tool_size = value # Slot function of draw thicikeness text editor changed def draw_thick_change_edit(self, value): if int(value) < 1 or int(value) > 30: return self.draw_thickness_sld.setValue(int(value)) def make_layer_image(self): for i, line in enumerate(self.scene.lines): pen = self.scene.pens[i] pen_size = int(pen.width()) pen_color = pen.color() # start pixel of line x1 = int(line.x1()) y1 = int(line.y1()) # end pixel of line x2 = int(line.x2()) y2 = int(line.y2()) dx = int(line.dx()) dy = int(line.dy()) # When only 1pixl line if dx <= 1 and dy <= 1: draw_pix_x1_s = max(x1 - int(pen_size / 2), 0) draw_pix_x1_e = min(x1 + int(pen_size / 2), self.org_img_width - 1) draw_pix_y1_s = max(y1 - int(pen_size / 2), 0) draw_pix_y1_e = min(y1 + int(pen_size / 2), self.org_img_height - 1) # for Pen's size for y in range(draw_pix_y1_s, draw_pix_y1_e): for x in range(draw_pix_x1_s, draw_pix_x1_e): self.layer_qimg.setPixelColor(x, y, pen_color) draw_pix_x2_s = max(x2 - int(pen_size / 2), 0) draw_pix_x2_e = min(x2 + int(pen_size / 2), self.org_img_width - 1) draw_pix_y2_s = max(y2 - int(pen_size / 2), 0) draw_pix_y2_e = min(y2 + int(pen_size / 2), self.org_img_height - 1) # for Pen's size for y in range(draw_pix_y2_s, draw_pix_y2_e): for x in range(draw_pix_x2_s, draw_pix_x2_e): self.layer_qimg.setPixelColor(x, y, pen_color) else: # For avoid devide by 0 if dx == 0: for y in range(y1, y2 + 1): draw_pix_y_s = y - int(pen_size / 2) draw_pix_y_e = y + int(pen_size / 2) # for Pen's size for yy in range(draw_pix_y_s, draw_pix_y_e): self.layer_qimg.setPixelColor(x1, yy, pen_color) else: grad = dy / dx # Choose coordinates with small slope not to skip pixels if grad >= 1.0: for x in range(dx): y = y1 + int(grad * x + 0.5) draw_pix_x_s = max(x1 + x - int(pen_size / 2), 0) draw_pix_x_e = min(x1 + x + int(pen_size / 2), self.org_img_width - 1) draw_pix_y_s = max(y - int(pen_size / 2), 0) draw_pix_y_e = min(y + int(pen_size / 2), self.org_img_height - 1) # for Pen's size for yy in range(draw_pix_y_s, draw_pix_y_e + 1): for xx in range(draw_pix_x_s, draw_pix_x_e + 1): self.layer_qimg.setPixelColor( xx, yy, pen_color) else: for y in range(dy): x = x1 + int(1 / grad * y + 0.5) draw_pix_y_s = max(y1 + y - int(pen_size / 2), 0) draw_pix_y_e = min(y1 + y + int(pen_size / 2), self.org_img_height - 1) draw_pix_x_s = max(x - int(pen_size / 2), 0) draw_pix_x_e = min(x + int(pen_size / 2), self.org_img_width - 1) # for Pen's size for yy in range(draw_pix_y_s, draw_pix_y_e + 1): for xx in range(draw_pix_x_s, draw_pix_x_e + 1): self.layer_qimg.setPixelColor( xx, yy, pen_color) # Slot function of save layer image button clicked def save_layer_image(self): self.make_layer_image() layer_img_default_path = self.app_setting["SoftwareSetting"][ "file_path"]["layer_img_dir"] options = QFileDialog.Options() file_name, selected_filete = QFileDialog.getSaveFileName(self, 'Save layer image', layer_img_default_path, \ 'image files(*.png, *jpg)', options=options) #print('layer image save name:{file}'.format(file=file_name)) self.layer_qimg.save(file_name) ret = QMessageBox(self, 'Success', 'layer image is saved successfully', QMessageBox.Ok) # Make composed orignal and layered image def make_compose_image(self): self.make_layer_image() self.compose_qimg = QImage(self.org_img_width, self.org_img_height, QImage.Format_RGBA8888) painter = QPainter(self.compose_qimg) painter.setCompositionMode(QPainter.CompositionMode_SourceOver) painter.drawImage(0, 0, self.org_qimg) painter.setCompositionMode(QPainter.CompositionMode_SourceOver) painter.drawImage(0, 0, self.layer_qimg) painter.end() # Slot function of save composer original and layer image button clicked def save_compose_image(self): self.make_compose_image() compose_img_default_path = self.app_setting["SoftwareSetting"][ "file_path"]["compose_img_dir"] options = QFileDialog.Options() file_name, selected_fileter = QFileDialog.getSaveFileName(self, 'Save composed image', compose_img_default_path, \ 'image files(*.png, *jpg)', options=options) #print('compose image save name:{file}'.format(file=file_name)) self.compose_qimg.save(file_name) ret = QMessageBox(self, 'Success', 'compose image is saved successfully', QMessageBox.Ok)
class Apartment2D(object): def __init__(self, ui): #self.ui = ui #self.ui.guiDlg.resize(QDesktopWidget().availableGeometry(self).size() * 0.6) self.scene = QGraphicsScene() self.dim = { "HMIN": -3000, "VMIN": -4000, "WIDTH": 6000, "HEIGHT": 8000 } self.scene.setSceneRect(self.dim['HMIN'], self.dim['VMIN'], self.dim['WIDTH'], self.dim['HEIGHT']) ui.graphicsView.setScene(self.scene) self.boxes = [] #self.ui.graphicsView.setViewport(QGLWidget()) ui.graphicsView.scale(1, -1) ui.graphicsView.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) ui.graphicsView.setTransformationAnchor(QGraphicsView.NoAnchor) ui.graphicsView.setResizeAnchor(QGraphicsView.NoAnchor) self.persons = {} self.pixmapSize = (0, 0) self.initializeWorld() def addPerson(self, pos, angle=0, color=-1, size=100): colors = QColor.colorNames() color = colors[random.randint(0, len(colors) - 1)] if color == -1 else color pos = [pos[0], pos[2]] if len(pos) > 2 else pos p = self.scene.addEllipse(pos[0] - size // 2, pos[1] - size // 2, size, size, pen=QPen(QColor(color), 20), brush=QBrush(color=QColor(color))) # pixmap pixmap = QPixmap("person.png").scaled(600, 300) self.pixmapSize = (pixmap.width() / 2, pixmap.height() / 2) pixItem = QGraphicsPixmapItem(pixmap) pixItem.setTransformOriginPoint(pixItem.boundingRect().center()) pixItem.setZValue(20) self.scene.addItem(pixItem) self.persons[p] = pixItem return p def movePerson(self, elipse, pos, size=100): #elipse.setPos(pos[0], pos[1]) pos = [pos[0], pos[2]] if len(pos) > 2 else pos color = elipse.pen().color() self.scene.addEllipse(pos[0] - size // 2, pos[1] - size // 2, size, size, pen=QPen(QColor(color), 20), brush=QBrush(color=QColor(color))) # pixmap self.persons[elipse].setPos(pos[0] - self.pixmapSize[0], pos[1] - self.pixmapSize[1]) # change rotation value when provided self.persons[elipse].setRotation(180) def wheelEvent(self, event): zoomInFactor = 1.15 zoomOutFactor = 1 / zoomInFactor # Zoom if event.delta() > 0: zoomFactor = zoomInFactor else: zoomFactor = zoomOutFactor self.graphicsView.scale(zoomFactor, zoomFactor) def initializeWorld(self): with open('autonomy.json', 'r') as f: world = json.load(f) #load dimensions dim = world["dimensions"] x_offset = -3200 y_offset = 1850 # load roundtables # for k,v in world["roundTables"].items(): # print(v) # box = self.scene.addEllipse(QRectF(-v[2]// 2, -v[3]// 2, v[2], v[3]), QPen(QColor("Khaki")), QBrush(QColor("Khaki"))); # box.setPos(v[4]+x_offset, v[5]+x_offset); # self.boxes.append(box) # load tables for k, v in world["tables"].items(): box = self.scene.addRect( QRectF(-v[2] // 2, -v[3] // 2, v[2], v[3]), QPen(QColor("SandyBrown")), QBrush(QColor("SandyBrown"))) box.setPos(v[4] + x_offset, v[5] + y_offset) box.setTransformOriginPoint(box.mapFromScene(QPointF(0, 0))) box.setRotation(v[6]) self.boxes.append(box) # load walls for k, v in world['walls'].items(): box = self.scene.addRect( QRectF(-v[2] // 2, -v[3] // 2, v[2], v[3]), QPen(QColor("Brown")), QBrush(QColor("Brown"))) box.setPos(v[4] + x_offset, v[5] + y_offset) box.setTransformOriginPoint(box.mapFromScene(QPointF(0, 0))) box.setRotation(v[6]) self.boxes.append(box) # } # //load points # QVariantMap points = mainMap[QString("points")].toMap(); # for (auto &t : points) # { # QVariantList object = t.toList(); # auto box = scene.addRect(QRectF(-object[2].toFloat() / 2, -object[3].toFloat() / 2, object[2].toFloat(), object[3].toFloat()), QPen(QColor("Brown")), QBrush(QColor("Brown"))); # box->setPos(object[4].toFloat()+x_offset, object[5].toFloat()+y_offset); # boxes.push_back(box); # } # //load boxes # QVariantMap cajas = mainMap[QString("boxes")].toMap(); # for (auto &t : cajas) # { # QVariantList object = t.toList(); # auto box = scene.addRect(QRectF(-object[2].toFloat() / 2, -object[3].toFloat() / 2, object[2].toFloat(), object[3].toFloat()), QPen(QColor("Brown")), QBrush(QColor("Orange"))); # box->setPos(object[4].toFloat()+x_offset, object[5].toFloat()+y_offset); # //box->setPos(object[4].toFloat(), object[5].toFloat()); # //box->setRotation(object[6].toFloat()*180/M_PI2); # box->setFlag(QGraphicsItem::ItemIsMovable); # boxes.push_back(box); # } # QTransform t; # //t.translate(3200, -1850); # t.rotate(-90); # //t.translate(-3200, 1850); # for(auto &item : boxes) # { # item->setPos(t.map(item->pos())); # item->setRotation(item->rotation() + 90); # item->setPos(item->pos() + QPointF(1850,3200)); # } # ///////////// # //AXIS self.scene.addLine(0, 0, 400, 0, QPen(QBrush(QColor("red")), 20)) self.scene.addLine(0, 0, 0, 400, QPen(QBrush(QColor("blue")), 20))
class HumanVisualizationWidget(QGraphicsView): def __init__(self, parent=None): super(HumanVisualizationWidget, self).__init__(parent) self._scene = QGraphicsScene(self) self.setScene(self._scene) # circle = QGraphicsEllipseItem( 10, 10, 10 ,10) # self._scene.addItem(circle) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self._boxes = [] self._humans = {} def load_inner_model(self, file): import xml.etree.cElementTree as ET tree = ET.ElementTree(file=file) root = tree.getroot() transforms = tree.findall(".//transform[plane]") walls = {} for trans in transforms: if 'id' in trans.attrib and 'pared' in trans.attrib['id']: print("Pared:", trans.attrib['id']) current_wall = [0] * 7 # "wall5": [x, y, width, height, posx, posy, 0] if 'tx' in trans.attrib: # print trans.attrib['tx'] current_wall[4] = int(float(trans.attrib['tx'])) if 'ty' in trans.attrib: # print trans.attrib['ty'] current_wall[5] = int(float(trans.attrib['tz'])) # current_wall = planes = trans.findall('plane') for plane in planes: if 'id' in plane.attrib and 'muro' in plane.attrib['id']: # if 'nx' in plane.attrib: # print plane.attrib['nx'] # if 'nz' in plane.attrib: # print plane.attrib['nz'] if 'size' in plane.attrib: # print int(float(plane.attrib['size'].split(',')[0]) current_wall[2] = int( float(plane.attrib['size'].split(',')[0])) / 2. # print int(float(plane.attrib['size'].split(',')[1]) current_wall[3] = int( float(plane.attrib['size'].split(',')[1])) / 2. if current_wall[2] < current_wall[3]: current_wall[2] = 200 else: current_wall[3] = 200 walls[trans.attrib['id']] = current_wall for id in sorted(walls.keys()): object = walls[id] # rect = QRectF(-float(object[2]) / 2, -float(object[3]) / 2, float(object[2]), float(object[3])) rect = QRectF(0, 0, float(object[2]), float(object[3])) border = QPen(QColor("black")) fill = QBrush(QColor("black")) box = self._scene.addRect(rect, border, fill) self._scene.addEllipse( QRectF(float(object[4]), float(object[5]), 10, 10), QPen(QColor("green")), QBrush(QColor("green"))) box.setPos(float(object[4]), float(object[5])) box.setRotation(float(object[6])) self._boxes.append(box) self._scene.update() QApplication.processEvents() sleep(1) def load_custom_json_world(self, file): if not os.path.isfile(file): print("Error reading world file, check config params:", file) return False with open(file, "r") as read_file: json_data = json.load(read_file) types_colors = { "tables": "SandyBrown", "roundTables": "Khaki", "walls": "Brown", "points": "Blue" } self.clear() for type, color in types_colors.items(): if type in json_data: tables = json_data[type] for object in tables.values(): rect = QRectF(-float(object[2]) / 2, -float(object[3]) / 2, float(object[2]), float(object[3])) border = QPen(QColor(color)) fill = QBrush(QColor(color)) if type == "roundTables": box = self._scene.addEllipse(rect, border, fill) else: box = self._scene.addRect(rect, border, fill) box.setPos(float(object[4]), float(object[5])) box.setRotation(float(object[6])) self._boxes.append(box) def load_json_world(self, file): if not os.path.isfile(file): print("Error reading world file, check config params:", file) return False with open(file, "r") as read_file: json_data = json.load(read_file) polygon_points = [] paths_count = 0 for item in json_data: if 'json_geometry' in item: geometry = item['json_geometry'] if geometry['type'] == 'Polygon': for coord in geometry['coordinates'][0]: if isinstance(coord, list) and ( (isinstance(coord, list) and len(coord) == 2) or (len(coord) == 3 and coord[3] == 0)): current_point = QPointF(coord[0], coord[1]) polygon_points.append(current_point) else: print("Unknown coord", geometry["coordinates"][0]) polygon = QPolygonF(polygon_points) path = QPainterPath() path.addPolygon(polygon) contour = QGraphicsPathItem(path) # r = lambda: random.randint(0, 255) # next_color = '#%02X%02X%02X' % (r(), r(), r()) contour.setPen(QPen(QColor("red"), 0.1)) contour.setBrush(QBrush(Qt.transparent)) # if paths_count == 4: print(item['json_featuretype']) self._scene.addItem(contour) paths_count += 1 self.update() def clear(self): for human in self._humans.values(): self._scene.removeItem(human) self._humans = {} # self._scene.setSceneRect(QRectF(0,0,400,400)) def resizeEvent(self, event): # skip initial entry self.own_resize() super(HumanVisualizationWidget, self).resizeEvent(event) def own_resize(self): self.fitInView(self._scene.itemsBoundingRect(), Qt.KeepAspectRatio) self._scene.setSceneRect(self._scene.itemsBoundingRect()) def add_human_by_pos(self, id, pos): x, y = pos human = QGraphicsEllipseItem(0, 0, 200, 200) self._scene.addItem(human) human.setBrush(QBrush(Qt.black, style=Qt.SolidPattern)) human_text = QGraphicsTextItem(str(pos)) font = QFont("Helvetica [Cronyx]", 40, QFont.Bold) human_text.setFont(font) human_text.setParentItem(human) human.setPos(pos[0], pos[1]) self._humans[id] = human human.setZValue(30) def move_human(self, id, pos): x, y = pos human = self._humans[id] human.setPos(x, y) def set_human_color(self, id, color): if id in self._humans: self._humans[id].setBrush(color)
class Screenshot(QGraphicsView): """ Main Class """ screen_shot_grabed = Signal(QImage) screen_shot_pos_grabed = Signal(QRect) widget_closed = Signal() def __init__(self, flags=constant.DEFAULT, parent=None): """ flags: binary flags. see the flags in the constant.py """ super().__init__(parent) # Init self.penColorNow = QColor(PENCOLOR) self.penSizeNow = PENSIZE self.fontNow = QFont('Sans') self.clipboard = QApplication.clipboard() self.drawListResult = [ ] # draw list that sure to be drew, [action, coord] self.drawListProcess = None # the process to the result self.selected_area = QRect( ) # a QRect instance which stands for the selected area self.selectedAreaRaw = QRect() self.mousePosition = MousePosition.OUTSIDE_AREA # mouse position self.screenPixel = None self.textRect = None self.mousePressed = False self.action = ACTION_SELECT self.mousePoint = self.cursor().pos() self.startX, self.startY = 0, 0 # the point where you start self.endX, self.endY = 0, 0 # the point where you end self.pointPath = QPainterPath( ) # the point mouse passes, used by draw free line self.items_to_remove = [ ] # the items that should not draw on screenshot picture self.textPosition = None # result self.target_img = None self.target_img_pos = None # Init window self.getscreenshot() self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) self.setMouseTracking(True) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setContentsMargins(0, 0, 0, 0) self.setStyleSheet("QGraphicsView { border-style: none; }") self.tooBar = MyToolBar(flags, self) self.tooBar.trigger.connect(self.changeAction) self.penSetBar = None if flags & constant.RECT or flags & constant.ELLIPSE or flags & constant.LINE or flags & constant.FREEPEN \ or flags & constant.ARROW or flags & constant.TEXT: self.penSetBar = PenSetWidget(self) self.penSetBar.penSizeTrigger.connect(self.changePenSize) self.penSetBar.penColorTrigger.connect(self.changePenColor) self.penSetBar.fontChangeTrigger.connect(self.changeFont) self.textInput = TextInput(self) self.textInput.inputChanged.connect(self.textChange) self.textInput.cancelPressed.connect(self.cancelInput) self.textInput.okPressed.connect(self.okInput) self.graphics_scene = QGraphicsScene(0, 0, self.screenPixel.width(), self.screenPixel.height()) self.show() self.setScene(self.graphics_scene) self.windowHandle().setScreen(QGuiApplication.screenAt(QCursor.pos())) self.scale = self.get_scale() # self.setFixedSize(self.screenPixel.width(), self.screenPixel.height()) self.setGeometry(QGuiApplication.screenAt(QCursor.pos()).geometry()) self.showFullScreen() self.redraw() QShortcut(QKeySequence('ctrl+s'), self).activated.connect(self.saveScreenshot) QShortcut(QKeySequence('esc'), self).activated.connect(self.close) @staticmethod def take_screenshot(flags): loop = QEventLoop() screen_shot = Screenshot(flags) screen_shot.show() screen_shot.widget_closed.connect(loop.quit) loop.exec_() img = screen_shot.target_img return img @staticmethod def take_screenshot_pos(flags): loop = QEventLoop() screen_shot = Screenshot(flags) screen_shot.show() screen_shot.widget_closed.connect(loop.quit) loop.exec_() pos = screen_shot.target_img_pos return pos def getscreenshot(self): screen = QGuiApplication.screenAt(QCursor.pos()) self.screenPixel = screen.grabWindow(0) def mousePressEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if event.button() != Qt.LeftButton: return if self.action is None: self.action = ACTION_SELECT self.startX, self.startY = event.x(), event.y() if self.action == ACTION_SELECT: if self.mousePosition == MousePosition.OUTSIDE_AREA: self.mousePressed = True self.selected_area = QRect() self.selected_area.setTopLeft(QPoint(event.x(), event.y())) self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.redraw() elif self.mousePosition == MousePosition.INSIDE_AREA: self.mousePressed = True else: pass elif self.action == ACTION_MOVE_SELECTED: if self.mousePosition == MousePosition.OUTSIDE_AREA: self.action = ACTION_SELECT self.selected_area = QRect() self.selected_area.setTopLeft(QPoint(event.x(), event.y())) self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.redraw() self.mousePressed = True elif self.action in DRAW_ACTION: self.mousePressed = True if self.action == ACTION_FREEPEN: self.pointPath = QPainterPath() self.pointPath.moveTo(QPoint(event.x(), event.y())) elif self.action == ACTION_TEXT: if self.textPosition is None: self.textPosition = QPoint(event.x(), event.y()) self.textRect = None self.redraw() def mouseMoveEvent(self, event: QMouseEvent): """ :type event: QMouseEvent :param event: :return: """ self.mousePoint = QPoint(event.globalPos().x(), event.globalPos().y()) if self.action is None: self.action = ACTION_SELECT if not self.mousePressed: point = QPoint(event.x(), event.y()) self.detect_mouse_position(point) self.setCursorStyle() self.redraw() else: self.endX, self.endY = event.x(), event.y() # if self.mousePosition != OUTSIDE_AREA: # self.action = ACTION_MOVE_SELECTED if self.action == ACTION_SELECT: self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.redraw() elif self.action == ACTION_MOVE_SELECTED: self.selected_area = QRect(self.selectedAreaRaw) if self.mousePosition == MousePosition.INSIDE_AREA: move_to_x = event.x( ) - self.startX + self.selected_area.left() move_to_y = event.y( ) - self.startY + self.selected_area.top() if 0 <= move_to_x <= self.screenPixel.width( ) - 1 - self.selected_area.width(): self.selected_area.moveLeft(move_to_x) if 0 <= move_to_y <= self.screenPixel.height( ) - 1 - self.selected_area.height(): self.selected_area.moveTop(move_to_y) self.selected_area = self.selected_area.normalized() self.selectedAreaRaw = QRect(self.selected_area) self.startX, self.startY = event.x(), event.y() self.redraw() elif self.mousePosition == MousePosition.ON_THE_LEFT_SIDE: move_to_x = event.x( ) - self.startX + self.selected_area.left() if move_to_x <= self.selected_area.right(): self.selected_area.setLeft(move_to_x) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_RIGHT_SIDE: move_to_x = event.x( ) - self.startX + self.selected_area.right() self.selected_area.setRight(move_to_x) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_UP_SIDE: move_to_y = event.y( ) - self.startY + self.selected_area.top() self.selected_area.setTop(move_to_y) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_DOWN_SIDE: move_to_y = event.y( ) - self.startY + self.selected_area.bottom() self.selected_area.setBottom(move_to_y) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_TOP_LEFT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.left() move_to_y = event.y( ) - self.startY + self.selected_area.top() self.selected_area.setTopLeft(QPoint(move_to_x, move_to_y)) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_BOTTOM_RIGHT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.right() move_to_y = event.y( ) - self.startY + self.selected_area.bottom() self.selected_area.setBottomRight( QPoint(move_to_x, move_to_y)) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_TOP_RIGHT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.right() move_to_y = event.y( ) - self.startY + self.selected_area.top() self.selected_area.setTopRight(QPoint( move_to_x, move_to_y)) self.selected_area = self.selected_area.normalized() self.redraw() elif self.mousePosition == MousePosition.ON_THE_BOTTOM_LEFT_CORNER: move_to_x = event.x( ) - self.startX + self.selected_area.left() move_to_y = event.y( ) - self.startY + self.selected_area.bottom() self.selected_area.setBottomLeft( QPoint(move_to_x, move_to_y)) self.redraw() else: pass elif self.action == ACTION_RECT: self.drawRect(self.startX, self.startY, event.x(), event.y(), False) self.redraw() pass elif self.action == ACTION_ELLIPSE: self.drawEllipse(self.startX, self.startY, event.x(), event.y(), False) self.redraw() elif self.action == ACTION_ARROW: self.drawArrow(self.startX, self.startY, event.x(), event.y(), False) self.redraw() elif self.action == ACTION_LINE: self.drawLine(self.startX, self.startY, event.x(), event.y(), False) self.redraw() elif self.action == ACTION_FREEPEN: y1, y2 = event.x(), event.y() rect = self.selected_area.normalized() if y1 <= rect.left(): y1 = rect.left() elif y1 >= rect.right(): y1 = rect.right() if y2 <= rect.top(): y2 = rect.top() elif y2 >= rect.bottom(): y2 = rect.bottom() self.pointPath.lineTo(y1, y2) self.drawFreeLine(self.pointPath, False) self.redraw() def mouseReleaseEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if event.button() != Qt.LeftButton: return if self.mousePressed: self.mousePressed = False self.endX, self.endY = event.x(), event.y() if self.action == ACTION_SELECT: self.selected_area.setBottomRight(QPoint(event.x(), event.y())) self.selectedAreaRaw = QRect(self.selected_area) self.action = ACTION_MOVE_SELECTED self.redraw() elif self.action == ACTION_MOVE_SELECTED: self.selectedAreaRaw = QRect(self.selected_area) self.redraw() # self.action = None elif self.action == ACTION_RECT: self.drawRect(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_ELLIPSE: self.drawEllipse(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_ARROW: self.drawArrow(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_LINE: self.drawLine(self.startX, self.startY, event.x(), event.y(), True) self.redraw() elif self.action == ACTION_FREEPEN: self.drawFreeLine(self.pointPath, True) self.redraw() def detect_mouse_position(self, point): """ :type point: QPoint :param point: the mouse position you want to check :return: """ if self.selected_area == QRect(): self.mousePosition = MousePosition.OUTSIDE_AREA return if self.selected_area.left() - ERRORRANGE <= point.x( ) <= self.selected_area.left() and ( self.selected_area.top() - ERRORRANGE <= point.y() <= self.selected_area.top()): self.mousePosition = MousePosition.ON_THE_TOP_LEFT_CORNER elif self.selected_area.right() <= point.x( ) <= self.selected_area.right() + ERRORRANGE and ( self.selected_area.top() - ERRORRANGE <= point.y() <= self.selected_area.top()): self.mousePosition = MousePosition.ON_THE_TOP_RIGHT_CORNER elif self.selected_area.left() - ERRORRANGE <= point.x( ) <= self.selected_area.left() and ( self.selected_area.bottom() <= point.y() <= self.selected_area.bottom() + ERRORRANGE): self.mousePosition = MousePosition.ON_THE_BOTTOM_LEFT_CORNER elif self.selected_area.right() <= point.x( ) <= self.selected_area.right() + ERRORRANGE and ( self.selected_area.bottom() <= point.y() <= self.selected_area.bottom() + ERRORRANGE): self.mousePosition = MousePosition.ON_THE_BOTTOM_RIGHT_CORNER elif -ERRORRANGE <= point.x() - self.selected_area.left() <= 0 and ( self.selected_area.topLeft().y() < point.y() < self.selected_area.bottomLeft().y()): self.mousePosition = MousePosition.ON_THE_LEFT_SIDE elif 0 <= point.x() - self.selected_area.right() <= ERRORRANGE and ( self.selected_area.topRight().y() < point.y() < self.selected_area.bottomRight().y()): self.mousePosition = MousePosition.ON_THE_RIGHT_SIDE elif -ERRORRANGE <= point.y() - self.selected_area.top() <= 0 and ( self.selected_area.topLeft().x() < point.x() < self.selected_area.topRight().x()): self.mousePosition = MousePosition.ON_THE_UP_SIDE elif 0 <= point.y() - self.selected_area.bottom() <= ERRORRANGE and ( self.selected_area.bottomLeft().x() < point.x() < self.selected_area.bottomRight().x()): self.mousePosition = MousePosition.ON_THE_DOWN_SIDE elif not self.selected_area.contains(point): self.mousePosition = MousePosition.OUTSIDE_AREA else: self.mousePosition = MousePosition.INSIDE_AREA def setCursorStyle(self): if self.action in DRAW_ACTION: self.setCursor(Qt.CrossCursor) return if self.mousePosition == MousePosition.ON_THE_LEFT_SIDE or \ self.mousePosition == MousePosition.ON_THE_RIGHT_SIDE: self.setCursor(Qt.SizeHorCursor) elif self.mousePosition == MousePosition.ON_THE_UP_SIDE or \ self.mousePosition == MousePosition.ON_THE_DOWN_SIDE: self.setCursor(Qt.SizeVerCursor) elif self.mousePosition == MousePosition.ON_THE_TOP_LEFT_CORNER or \ self.mousePosition == MousePosition.ON_THE_BOTTOM_RIGHT_CORNER: self.setCursor(Qt.SizeFDiagCursor) elif self.mousePosition == MousePosition.ON_THE_TOP_RIGHT_CORNER or \ self.mousePosition == MousePosition.ON_THE_BOTTOM_LEFT_CORNER: self.setCursor(Qt.SizeBDiagCursor) elif self.mousePosition == MousePosition.OUTSIDE_AREA: self.setCursor(Qt.ArrowCursor) elif self.mousePosition == MousePosition.INSIDE_AREA: self.setCursor(Qt.OpenHandCursor) else: self.setCursor(Qt.ArrowCursor) pass def drawMagnifier(self): # First, calculate the magnifier position due to the mouse position watch_area_width = 16 watch_area_height = 16 cursor_pos = self.mousePoint watch_area = QRect( QPoint(cursor_pos.x() - watch_area_width / 2, cursor_pos.y() - watch_area_height / 2), QPoint(cursor_pos.x() + watch_area_width / 2, cursor_pos.y() + watch_area_height / 2)) if watch_area.left() < 0: watch_area.moveLeft(0) watch_area.moveRight(watch_area_width) if self.mousePoint.x( ) + watch_area_width / 2 >= self.screenPixel.width(): watch_area.moveRight(self.screenPixel.width() - 1) watch_area.moveLeft(watch_area.right() - watch_area_width) if self.mousePoint.y() - watch_area_height / 2 < 0: watch_area.moveTop(0) watch_area.moveBottom(watch_area_height) if self.mousePoint.y( ) + watch_area_height / 2 >= self.screenPixel.height(): watch_area.moveBottom(self.screenPixel.height() - 1) watch_area.moveTop(watch_area.bottom() - watch_area_height) # tricks to solve the hidpi impact on QCursor.pos() watch_area.setTopLeft( QPoint(watch_area.topLeft().x() * self.scale, watch_area.topLeft().y() * self.scale)) watch_area.setBottomRight( QPoint(watch_area.bottomRight().x() * self.scale, watch_area.bottomRight().y() * self.scale)) watch_area_pixmap = self.screenPixel.copy(watch_area) # second, calculate the magnifier area magnifier_area_width = watch_area_width * 10 magnifier_area_height = watch_area_height * 10 font_area_height = 40 cursor_size = 24 magnifier_area = QRectF( QPoint(QCursor.pos().x() + cursor_size, QCursor.pos().y() + cursor_size), QPoint(QCursor.pos().x() + cursor_size + magnifier_area_width, QCursor.pos().y() + cursor_size + magnifier_area_height)) if magnifier_area.right() >= self.screenPixel.width(): magnifier_area.moveLeft(QCursor.pos().x() - magnifier_area_width - cursor_size / 2) if magnifier_area.bottom( ) + font_area_height >= self.screenPixel.height(): magnifier_area.moveTop(QCursor.pos().y() - magnifier_area_height - cursor_size / 2 - font_area_height) # third, draw the watch area to magnifier area watch_area_scaled = watch_area_pixmap.scaled( QSize(magnifier_area_width * self.scale, magnifier_area_height * self.scale)) magnifier_pixmap = self.graphics_scene.addPixmap(watch_area_scaled) magnifier_pixmap.setOffset(magnifier_area.topLeft()) # then draw lines and text self.graphics_scene.addRect(QRectF(magnifier_area), QPen(QColor(255, 255, 255), 2)) self.graphics_scene.addLine( QLineF( QPointF(magnifier_area.center().x(), magnifier_area.top()), QPointF(magnifier_area.center().x(), magnifier_area.bottom())), QPen(QColor(0, 255, 255), 2)) self.graphics_scene.addLine( QLineF( QPointF(magnifier_area.left(), magnifier_area.center().y()), QPointF(magnifier_area.right(), magnifier_area.center().y())), QPen(QColor(0, 255, 255), 2)) # get the rgb of mouse point point_rgb = QColor(self.screenPixel.toImage().pixel(self.mousePoint)) # draw information self.graphics_scene.addRect( QRectF( magnifier_area.bottomLeft(), magnifier_area.bottomRight() + QPoint(0, font_area_height + 30)), QPen(Qt.black), QBrush(Qt.black)) rgb_info = self.graphics_scene.addSimpleText( ' Rgb: ({0}, {1}, {2})'.format(point_rgb.red(), point_rgb.green(), point_rgb.blue())) rgb_info.setPos(magnifier_area.bottomLeft() + QPoint(0, 5)) rgb_info.setPen(QPen(QColor(255, 255, 255), 2)) rect = self.selected_area.normalized() size_info = self.graphics_scene.addSimpleText( ' Size: {0} x {1}'.format(rect.width() * self.scale, rect.height() * self.scale)) size_info.setPos(magnifier_area.bottomLeft() + QPoint(0, 15) + QPoint(0, font_area_height / 2)) size_info.setPen(QPen(QColor(255, 255, 255), 2)) def get_scale(self): return self.devicePixelRatio() def saveScreenshot(self, clipboard=False, fileName='screenshot.png', picType='png'): fullWindow = QRect(0, 0, self.width() - 1, self.height() - 1) selected = QRect(self.selected_area) if selected.left() < 0: selected.setLeft(0) if selected.right() >= self.width(): selected.setRight(self.width() - 1) if selected.top() < 0: selected.setTop(0) if selected.bottom() >= self.height(): selected.setBottom(self.height() - 1) source = (fullWindow & selected) source.setTopLeft( QPoint(source.topLeft().x() * self.scale, source.topLeft().y() * self.scale)) source.setBottomRight( QPoint(source.bottomRight().x() * self.scale, source.bottomRight().y() * self.scale)) image = self.screenPixel.copy(source) if clipboard: QGuiApplication.clipboard().setImage(image.toImage(), QClipboard.Clipboard) else: image.save(fileName, picType, 10) self.target_img = image self.target_img_pos = source self.screen_shot_grabed.emit(image.toImage()) self.screen_shot_pos_grabed.emit(source) def redraw(self): self.graphics_scene.clear() # draw screenshot self.graphics_scene.addPixmap(self.screenPixel) # prepare for drawing selected area rect = QRectF(self.selected_area) rect = rect.normalized() top_left_point = rect.topLeft() top_right_point = rect.topRight() bottom_left_point = rect.bottomLeft() bottom_right_point = rect.bottomRight() top_middle_point = (top_left_point + top_right_point) / 2 left_middle_point = (top_left_point + bottom_left_point) / 2 bottom_middle_point = (bottom_left_point + bottom_right_point) / 2 right_middle_point = (top_right_point + bottom_right_point) / 2 # draw the picture mask mask = QColor(0, 0, 0, 155) if self.selected_area == QRect(): self.graphics_scene.addRect(0, 0, self.screenPixel.width(), self.screenPixel.height(), QPen(Qt.NoPen), mask) else: self.graphics_scene.addRect(0, 0, self.screenPixel.width(), top_right_point.y(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect(0, top_left_point.y(), top_left_point.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect( top_right_point.x(), top_right_point.y(), self.screenPixel.width() - top_right_point.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect( 0, bottom_left_point.y(), self.screenPixel.width(), self.screenPixel.height() - bottom_left_point.y(), QPen(Qt.NoPen), mask) # draw the toolBar if self.action != ACTION_SELECT: spacing = 5 # show the toolbar first, then move it to the correct position # because the width of it may be wrong if this is the first time it shows self.tooBar.show() dest = QPointF(rect.bottomRight() - QPointF(self.tooBar.width(), 0) - QPointF(spacing, -spacing)) if dest.x() < spacing: dest.setX(spacing) pen_set_bar_height = self.penSetBar.height( ) if self.penSetBar is not None else 0 if dest.y() + self.tooBar.height( ) + pen_set_bar_height >= self.height(): if rect.top() - self.tooBar.height( ) - pen_set_bar_height < spacing: dest.setY(rect.top() + spacing) else: dest.setY(rect.top() - self.tooBar.height() - pen_set_bar_height - spacing) self.tooBar.move(dest.toPoint()) if self.penSetBar is not None: self.penSetBar.show() self.penSetBar.move(dest.toPoint() + QPoint(0, self.tooBar.height() + spacing)) if self.action == ACTION_TEXT: self.penSetBar.showFontWidget() else: self.penSetBar.showPenWidget() else: self.tooBar.hide() if self.penSetBar is not None: self.penSetBar.hide() # draw the list for step in self.drawListResult: self.drawOneStep(step) if self.drawListProcess is not None: self.drawOneStep(self.drawListProcess) if self.action != ACTION_TEXT: self.drawListProcess = None if self.selected_area != QRect(): self.items_to_remove = [] # draw the selected rectangle pen = QPen(QColor(0, 255, 255), 2) self.items_to_remove.append(self.graphics_scene.addRect(rect, pen)) # draw the drag point radius = QPoint(3, 3) brush = QBrush(QColor(0, 255, 255)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_left_point - radius, top_left_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_middle_point - radius, top_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_right_point - radius, top_right_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(left_middle_point - radius, left_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(right_middle_point - radius, right_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_left_point - radius, bottom_left_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_middle_point - radius, bottom_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_right_point - radius, bottom_right_point + radius), pen, brush)) # draw the textedit if self.textPosition is not None: textSpacing = 50 position = QPoint() if self.textPosition.x() + self.textInput.width( ) >= self.screenPixel.width(): position.setX(self.textPosition.x() - self.textInput.width()) else: position.setX(self.textPosition.x()) if self.textRect is not None: if self.textPosition.y() + self.textInput.height( ) + self.textRect.height() >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height() - self.textRect.height()) else: position.setY(self.textPosition.y() + self.textRect.height()) else: if self.textPosition.y() + self.textInput.height( ) >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height()) else: position.setY(self.textPosition.y()) self.textInput.move(position) self.textInput.show() # self.textInput.getFocus() # draw the magnifier if self.action == ACTION_SELECT: self.drawMagnifier() if self.mousePressed: self.drawSizeInfo() if self.action == ACTION_MOVE_SELECTED: self.drawSizeInfo() # deal with every step in drawList def drawOneStep(self, step): """ :type step: tuple """ if step[0] == ACTION_RECT: self.graphics_scene.addRect( QRectF(QPointF(step[1], step[2]), QPointF(step[3], step[4])), step[5]) elif step[0] == ACTION_ELLIPSE: self.graphics_scene.addEllipse( QRectF(QPointF(step[1], step[2]), QPointF(step[3], step[4])), step[5]) elif step[0] == ACTION_ARROW: arrow = QPolygonF() linex = float(step[1] - step[3]) liney = float(step[2] - step[4]) line = sqrt(pow(linex, 2) + pow(liney, 2)) # in case to divided by 0 if line == 0: return sinAngel = liney / line cosAngel = linex / line # sideLength is the length of bottom side of the body of an arrow # arrowSize is the size of the head of an arrow, left and right # sides' size is arrowSize, and the bottom side's size is arrowSize / 2 sideLength = step[5].width() arrowSize = 8 bottomSize = arrowSize / 2 tmpPoint = QPointF(step[3] + arrowSize * sideLength * cosAngel, step[4] + arrowSize * sideLength * sinAngel) point1 = QPointF(step[1] + sideLength * sinAngel, step[2] - sideLength * cosAngel) point2 = QPointF(step[1] - sideLength * sinAngel, step[2] + sideLength * cosAngel) point3 = QPointF(tmpPoint.x() - sideLength * sinAngel, tmpPoint.y() + sideLength * cosAngel) point4 = QPointF(tmpPoint.x() - bottomSize * sideLength * sinAngel, tmpPoint.y() + bottomSize * sideLength * cosAngel) point5 = QPointF(step[3], step[4]) point6 = QPointF(tmpPoint.x() + bottomSize * sideLength * sinAngel, tmpPoint.y() - bottomSize * sideLength * cosAngel) point7 = QPointF(tmpPoint.x() + sideLength * sinAngel, tmpPoint.y() - sideLength * cosAngel) arrow.append(point1) arrow.append(point2) arrow.append(point3) arrow.append(point4) arrow.append(point5) arrow.append(point6) arrow.append(point7) arrow.append(point1) self.graphics_scene.addPolygon(arrow, step[5], step[6]) elif step[0] == ACTION_LINE: self.graphics_scene.addLine( QLineF(QPointF(step[1], step[2]), QPointF(step[3], step[4])), step[5]) elif step[0] == ACTION_FREEPEN: self.graphics_scene.addPath(step[1], step[2]) elif step[0] == ACTION_TEXT: textAdd = self.graphics_scene.addSimpleText(step[1], step[2]) textAdd.setPos(step[3]) textAdd.setBrush(QBrush(step[4])) self.textRect = textAdd.boundingRect() # draw the size information on the top left corner def drawSizeInfo(self): sizeInfoAreaWidth = 200 sizeInfoAreaHeight = 30 spacing = 5 rect = self.selected_area.normalized() sizeInfoArea = QRect(rect.left(), rect.top() - spacing - sizeInfoAreaHeight, sizeInfoAreaWidth, sizeInfoAreaHeight) if sizeInfoArea.top() < 0: sizeInfoArea.moveTopLeft(rect.topLeft() + QPoint(spacing, spacing)) if sizeInfoArea.right() >= self.screenPixel.width(): sizeInfoArea.moveTopLeft(rect.topLeft() - QPoint(spacing, spacing) - QPoint(sizeInfoAreaWidth, 0)) if sizeInfoArea.left() < spacing: sizeInfoArea.moveLeft(spacing) if sizeInfoArea.top() < spacing: sizeInfoArea.moveTop(spacing) self.items_to_remove.append( self.graphics_scene.addRect(QRectF(sizeInfoArea), QPen(Qt.white), QBrush(Qt.black))) sizeInfo = self.graphics_scene.addSimpleText(' {0} x {1}'.format( rect.width() * self.scale, rect.height() * self.scale)) sizeInfo.setPos(sizeInfoArea.topLeft() + QPoint(0, 2)) sizeInfo.setPen(QPen(QColor(255, 255, 255), 2)) self.items_to_remove.append(sizeInfo) def drawRect(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() tmpRect = QRect(QPoint(x1, x2), QPoint(y1, y2)).normalized() resultRect = rect & tmpRect tmp = [ ACTION_RECT, resultRect.topLeft().x(), resultRect.topLeft().y(), resultRect.bottomRight().x(), resultRect.bottomRight().y(), QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawEllipse(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() tmpRect = QRect(QPoint(x1, x2), QPoint(y1, y2)).normalized() resultRect = rect & tmpRect tmp = [ ACTION_ELLIPSE, resultRect.topLeft().x(), resultRect.topLeft().y(), resultRect.bottomRight().x(), resultRect.bottomRight().y(), QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawArrow(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() if y1 <= rect.left(): y1 = rect.left() elif y1 >= rect.right(): y1 = rect.right() if y2 <= rect.top(): y2 = rect.top() elif y2 >= rect.bottom(): y2 = rect.bottom() tmp = [ ACTION_ARROW, x1, x2, y1, y2, QPen(QColor(self.penColorNow), int(self.penSizeNow)), QBrush(QColor(self.penColorNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawLine(self, x1, x2, y1, y2, result): rect = self.selected_area.normalized() if y1 <= rect.left(): y1 = rect.left() elif y1 >= rect.right(): y1 = rect.right() if y2 <= rect.top(): y2 = rect.top() elif y2 >= rect.bottom(): y2 = rect.bottom() tmp = [ ACTION_LINE, x1, x2, y1, y2, QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def drawFreeLine(self, pointPath, result): tmp = [ ACTION_FREEPEN, QPainterPath(pointPath), QPen(QColor(self.penColorNow), int(self.penSizeNow)) ] if result: self.drawListResult.append(tmp) else: self.drawListProcess = tmp def textChange(self): if self.textPosition is None: return self.text = self.textInput.getText() self.drawListProcess = [ ACTION_TEXT, str(self.text), QFont(self.fontNow), QPoint(self.textPosition), QColor(self.penColorNow) ] self.redraw() def undoOperation(self): if len(self.drawListResult) == 0: self.action = ACTION_SELECT self.selected_area = QRect() self.selectedAreaRaw = QRect() self.tooBar.hide() if self.penSetBar is not None: self.penSetBar.hide() else: self.drawListResult.pop() self.redraw() def saveOperation(self): filename = QFileDialog.getSaveFileName(self, 'Save file', './screenshot.png', '*.png;;*.jpg') if len(filename[0]) == 0: return else: self.saveScreenshot(False, filename[0], filename[1][2:]) self.close() def close(self): self.widget_closed.emit() super().close() self.tooBar.close() if self.penSetBar is not None: self.penSetBar.close() def saveToClipboard(self): QApplication.clipboard().setText('Test in save function') self.saveScreenshot(True) self.close() # slots def changeAction(self, nextAction): QApplication.clipboard().setText('Test in changeAction function') if nextAction == ACTION_UNDO: self.undoOperation() elif nextAction == ACTION_SAVE: self.saveOperation() elif nextAction == ACTION_CANCEL: self.close() elif nextAction == ACTION_SURE: self.saveToClipboard() else: self.action = nextAction self.setFocus() def changePenSize(self, nextPenSize): self.penSizeNow = nextPenSize def changePenColor(self, nextPenColor): self.penColorNow = nextPenColor def cancelInput(self): self.drawListProcess = None self.textPosition = None self.textRect = None self.textInput.hide() self.textInput.clearText() self.redraw() def okInput(self): self.text = self.textInput.getText() self.drawListResult.append([ ACTION_TEXT, str(self.text), QFont(self.fontNow), QPoint(self.textPosition), QColor(self.penColorNow) ]) self.textPosition = None self.textRect = None self.textInput.hide() self.textInput.clearText() self.redraw() def changeFont(self, font): self.fontNow = font
class MyImageViewer(QMainWindow): def __init__(self, filename): super(MyImageViewer, self).__init__() self.filename = filename self.goTo = 0 self.frameIdx = 0 self.doZoom = False self.showOverlay = True self.imageFile = ImageFile(self.filename) self.initUI() self.updateUI() def prevFrame(self): if self.frameIdx > 0: self.frameIdx -= 1 self.updateUI() def nextFrame(self): if self.frameIdx < self.imageFile.frameMax: self.frameIdx += 1 self.updateUI() def firstFrame(self): self.frameIdx = 0 self.updateUI() def lastFrame(self): self.frameIdx = self.imageFile.frameMax self.updateUI() def toggleZoom(self): self.doZoom = not self.doZoom self.updateUI() def toggleOverlay(self): self.showOverlay = not self.showOverlay # don't recenter since we're not changing the frame self.updateUI(updateCenter=False) def keyPressEvent(self, e): # tab key toggles overlay if e.key() == 16777217: self.toggleOverlay() # enter key goes to number entered elif e.key() == 16777220: if self.goTo >= 0 and self.goTo <= self.imageFile.frameMax: self.frameIdx = self.goTo self.updateUI() self.goTo = 0 # ascii characters elif e.key() < 128: # number keys: store digits to build number of goto frame if chr(e.key()).isnumeric(): self.goTo = 10 * self.goTo + int(chr(e.key())) # z toggles zoom elif chr(e.key()) == 'Z': self.toggleZoom() else: #print("key:",e.key()) pass def updateUI(self, updateCenter=True): self.pixmap.setPixmap(self.getFramePixmap(self.frameIdx)) outlineBox = self.imageFile.box( self.imageFile.findCenterOfMass(self.frameIdx)) if self.boxRect: self.boxRect.setRect(*outlineBox) if self.doZoom: self.view.resetTransform() self.view.scale(2, 2) if updateCenter: self.view.centerOn( *self.imageFile.findCenterOfMass(self.frameIdx)) else: self.view.resetTransform() self.setWindowTitle( "%s... [%d/%d]" % (self.filename[:20], self.frameIdx, self.imageFile.frameMax)) def getFramePixmap(self, i): image = QtGui.QImage(self.imageFile.getFrame(i, self.showOverlay), self.imageFile.imgH, self.imageFile.imgW, QtGui.QImage.Format_ARGB32) return QtGui.QPixmap.fromImage(image) def initUI(self): self.scene = QGraphicsScene() self.pixmap = QGraphicsPixmapItem() self.scene.addItem(self.pixmap) self.boxRect = None # option to draw box around overlay spots if False: outlineBox = self.imageFile.box( self.imageFile.findCenterOfMass(self.frameIdx)) self.scene.addRect(QRect(*outlineBox), pen=QtGui.QPen(QtCore.Qt.blue, 1)) self.boxRect = self.scene.items()[ 0] # not the best way to get rect self.view = MyGraphicsView(self) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setScene(self.scene) self.setCentralWidget(self.view) controlDock = QDockWidget() controlDock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.TopDockWidgetArea, controlDock) main_layout = QHBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) # first frame button firstButton = QPushButton('<<', self) firstButton.clicked.connect(self.firstFrame) main_layout.addWidget(firstButton) # previous frame button prevButton = QPushButton('<', self) prevButton.clicked.connect(self.prevFrame) main_layout.addWidget(prevButton) # next frame button nextButton = QPushButton('>', self) nextButton.clicked.connect(self.nextFrame) main_layout.addWidget(nextButton) # last frame button lastButton = QPushButton('>>', self) lastButton.clicked.connect(self.lastFrame) main_layout.addWidget(lastButton) # zoom button zoomButton = QPushButton('zoom', self) zoomButton.clicked.connect(self.toggleZoom) main_layout.addWidget(zoomButton) # zoom button overlayButton = QPushButton('color', self) overlayButton.clicked.connect(self.toggleOverlay) main_layout.addWidget(overlayButton) # add dock widget dockContainerWidget = QWidget(controlDock) dockContainerWidget.setLayout(main_layout) dockContainerWidget.setGeometry(0, 0, self.imageFile.imgW, 20) self.setFixedSize(self.imageFile.imgW, self.imageFile.imgH + dockContainerWidget.height()) self.show()
class QTraceViewer(QWidget): """ Load a basic block trace through json and visualize it in the disassembly Ref: https://github.com/angr/angr-management/pull/122 """ TAG_SPACING = 50 LEGEND_X = -50 LEGEND_Y = 0 LEGEND_WIDTH = 10 TRACE_FUNC_X = 0 TRACE_FUNC_Y = 0 TRACE_FUNC_WIDTH = 50 TRACE_FUNC_MINHEIGHT = 1000 TAB_HEADER_SIZE = 40 MAX_WINDOW_SIZE = 500 MARK_X = LEGEND_X MARK_WIDTH = TRACE_FUNC_X - LEGEND_X + TRACE_FUNC_WIDTH MARK_HEIGHT = 1 def __init__(self, workspace, disasm_view, parent=None): super().__init__(parent=parent) self.workspace = workspace self.disasm_view = disasm_view self.mark = None self.legend = None self.legend_height = 0 self.legend_img = None self.trace_func_unit_height = 0 self.trace_func = None self.trace_id = None self.view = None self.traceView = None self.traceTab = None self.traceScene = None self.multiView = None self.listView = None self.mark = None self.curr_position = 0 self._use_precise_position = False self._selected_traces = [] self._init_widgets() self.trace.am_subscribe(self._on_set_trace) self.selected_ins.am_subscribe(self._on_select_ins) self.traceTab.installEventFilter(self) # # Forwarding properties # @property def trace(self): return self.workspace.instance.trace @property def multi_trace(self): return self.workspace.instance.multi_trace @property def selected_ins(self): return self.disasm_view.infodock.selected_insns def _init_widgets(self): self.view = QTabWidget() # QGraphicsView() self.traceTab = QWidget() tracelayout = QVBoxLayout() self.traceView = QGraphicsView() self.traceScene = QGraphicsScene() self.traceView.setScene(self.traceScene) self.listView = QTableWidget(0, 2) # row, col self.listView.setHorizontalHeaderItem(0, QTableWidgetItem("Trace ID")) self.listView.setHorizontalHeaderItem(1, QTableWidgetItem("Input ID")) self.listView.setSelectionMode(QAbstractItemView.SingleSelection) self.listView.setSelectionBehavior(QAbstractItemView.SelectRows) # self.listView.horizontalHeader().setStretchLastSection(True) # self.listView.horizontalHeader().setSectionResizeModel(0, QHeaderView.Stretch) self.listView.cellClicked.connect(self._switch_current_trace) self.traceSeedButton = QPushButton("View Input Seed") self.traceSeedButton.clicked.connect(self._view_input_seed) tracelayout.addWidget(self.traceView) tracelayout.addWidget(self.listView) tracelayout.addWidget(self.traceSeedButton) self.traceTab.setLayout(tracelayout) self.multiView = QWidget() multiLayout = QVBoxLayout() self.multiTraceList = QTableWidget(0, 2) # row, col self.multiTraceList.setSelectionMode(QAbstractItemView.MultiSelection) self.multiTraceList.setSelectionBehavior(QAbstractItemView.SelectRows) self.multiTraceList.setHorizontalScrollMode( self.multiTraceList.ScrollPerPixel) self.multiTraceList.setHorizontalHeaderItem( 0, QTableWidgetItem("Trace ID")) self.multiTraceList.setHorizontalHeaderItem( 1, QTableWidgetItem("Input ID")) self.selectMultiTrace = QPushButton("Refresh Heatmap") self.selectMultiTrace.clicked.connect(self._refresh_heatmap) multiLayout.addWidget(self.multiTraceList) multiLayout.addWidget(self.selectMultiTrace) self.multiView.setLayout(multiLayout) self.view.addTab(self.traceTab, "SingleTrace") self.view.addTab(self.multiView, "MultiTrace HeatMap") self.SINGLE_TRACE = 0 self.MULTI_TRACE = 1 self.view.currentChanged.connect(self._on_tab_change) self._reset() layout = QVBoxLayout() layout.addWidget(self.view) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(self.view, Qt.AlignLeft) self.setLayout(layout) def _reset(self): self.traceScene.clear() #clear items self.listView.clearContents() self.multiTraceList.clearContents() self.mark = None self.legend = None self.legend_height = 0 self.trace_func = QGraphicsItemGroup() self.trace_id = QGraphicsItemGroup() self.traceScene.addItem(self.trace_func) self.hide() def _view_input_seed(self): current_trace_stats = self.trace.am_obj input_id = current_trace_stats.input_id inputSeed = self.multi_trace.am_obj.get_input_seed_for_id(input_id) msgText = "%s" % inputSeed msgDetails = "Input for [%s]" % current_trace_stats.id msgbox = QMessageBox() msgbox.setWindowTitle("Seed Input") msgbox.setDetailedText(msgDetails) msgbox.setText(msgText) msgbox.setStandardButtons(QMessageBox.Ok) msgbox.exec() def _switch_current_trace(self, row): if self.listView.rowCount() <= 0: return current_trace = self.trace.am_obj.id new_trace = self.multiTraceList.item(row, 0).text() if current_trace == new_trace: return trace_stats = self.multi_trace.am_obj.get_trace_with_id(new_trace) if trace_stats: self.trace.am_obj = trace_stats self._on_set_trace() def _on_set_trace(self): self._reset() if self.trace.am_none or self.trace.count is None: return l.debug('minheight: %d, count: %d', self.TRACE_FUNC_MINHEIGHT, self.trace.count) if self.trace.count <= 0: l.warning( "No valid addresses found in trace to show. Check base address offsets?" ) self.trace.am_obj = None self.trace.am_event() return if self.TRACE_FUNC_MINHEIGHT < self.trace.count * 15: self.trace_func_unit_height = 15 show_func_tag = True else: self.trace_func_unit_height = self.TRACE_FUNC_MINHEIGHT / self.trace.count show_func_tag = True self.legend_height = int(self.trace.count * self.trace_func_unit_height) self._show_trace_func(show_func_tag) self._show_legend() self._show_trace_ids() self._set_mark_color() self._refresh_multi_list() boundingSize = self.traceScene.itemsBoundingRect().width() windowSize = boundingSize if boundingSize > self.MAX_WINDOW_SIZE: windowSize = self.MAX_WINDOW_SIZE self.traceScene.setSceneRect( self.traceScene.itemsBoundingRect()) #resize self.setFixedWidth(windowSize) # self.listScene.setSceneRect(self.listScene.itemsBoundingRect()) #resize self.multiView.setFixedWidth(windowSize) cellWidth = windowSize // 2 self.listView.setColumnWidth(0, cellWidth) self.listView.setColumnWidth(1, cellWidth) self.listView.setFixedHeight(self.multiView.height() // 4) self.multiTraceList.setColumnWidth(0, cellWidth) self.multiTraceList.setColumnWidth(1, cellWidth) self.view.setFixedWidth(windowSize) self.show() def _populate_trace_table(self, view, trace_ids): numIDs = len(trace_ids) view.clearContents() view.setRowCount(numIDs) row = 0 #start after label row for traceID in trace_ids: inputID = self.multi_trace.am_obj.get_input_id_for_trace_id( traceID) if inputID is None: self.workspace.log("No inputID found for trace %s" % traceID) view.setItem(row, 0, QTableWidgetItem(traceID)) view.setItem(row, 1, QTableWidgetItem(inputID)) row += 1 def _refresh_heatmap(self): multiTrace = self.multi_trace.am_obj multiTrace.clear_heatmap() multiTrace.is_active_tab = True selected_items = self.multiTraceList.selectedItems() self._selected_traces.clear() for row in range(self.multiTraceList.rowCount()): item = self.multiTraceList.item(row, 0) if item in selected_items: self._selected_traces.append(item.text()) multiTrace.reload_heatmap(self._selected_traces) self.multi_trace.am_event() def _refresh_multi_list(self): multiTrace = self.multi_trace.am_obj trace_ids = multiTrace.get_all_trace_ids() self.multiTraceList.clearContents() self._populate_trace_table(self.multiTraceList, trace_ids) if self._selected_traces and self.multiTraceList.rowCount() > 0: self.multiTraceList.item(0, 0).setSelected(True) self.multiTraceList.item(0, 1).setSelected(True) else: for row in range(self.multiTraceList.rowCount()): item = self.multiTraceList.item(row, 0) inputItem = self.multiTraceList.item(row, 1) if item.text() in self._selected_traces: item.setSelected(True) inputItem.setSelected(True) self.multi_trace.am_event() def _on_tab_change(self): # self._reset() multiTrace = self.multi_trace.am_obj if self.view.currentIndex() == self.MULTI_TRACE: multiTrace.is_active_tab = True self._refresh_multi_list() elif self.view.currentIndex() == self.SINGLE_TRACE: multiTrace = self.multi_trace.am_obj multiTrace.is_active_tab = False self._show_trace_ids() def _on_select_ins(self, **kwargs): # pylint: disable=unused-argument if self.trace.am_none: return if self.mark is not None: for i in self.mark.childItems(): self.mark.removeFromGroup(i) self.traceScene.removeItem(i) self.traceScene.removeItem(self.mark) self.mark = QGraphicsItemGroup() self.traceScene.addItem(self.mark) if self.selected_ins: addr = next(iter(self.selected_ins)) positions = self.trace.get_positions(addr) if positions: #if addr is in list of positions if not self._use_precise_position: #handle case where insn was selected from disas view self.curr_position = positions[0] - self.trace.count for p in positions: color = self._get_mark_color(p, self.trace.count) y = self._get_mark_y(p) if p == self.trace.count + self.curr_position: #add thicker line for 'current' mark self.mark.addToGroup( self.traceScene.addRect(self.MARK_X, y, self.MARK_WIDTH, self.MARK_HEIGHT * 4, QPen(QColor('black')), QBrush(color))) else: self.mark.addToGroup( self.traceScene.addRect(self.MARK_X, y, self.MARK_WIDTH, self.MARK_HEIGHT, QPen(color), QBrush(color))) self.traceScene.update() #force redraw of the traceScene self.scroll_to_position(self.curr_position) def scroll_to_position(self, position): relative_pos = self.trace.count + position y_offset = self._get_mark_y(relative_pos) scrollValue = 0 if y_offset > 0.5 * self.traceView.size().height(): scrollValue = y_offset - 0.5 * self.traceView.size().height() scrollValue = min(scrollValue, self.traceView.verticalScrollBar().maximum()) self.traceView.verticalScrollBar().setValue(scrollValue) self._use_precise_position = False def jump_next_insn(self): if self.curr_position + self.trace.count < self.trace.count - 1: #for some reason indexing is done backwards self.curr_position += 1 self._use_precise_position = True bbl_addr = self.trace.get_bbl_from_position(self.curr_position) func = self.trace.get_func_from_position(self.curr_position) self._jump_bbl(func, bbl_addr) def jump_prev_insn(self): if self.curr_position + self.trace.count > 0: self.curr_position -= 1 self._use_precise_position = True bbl_addr = self.trace.get_bbl_from_position(self.curr_position) func = self.trace.get_func_from_position(self.curr_position) self._jump_bbl(func, bbl_addr) def eventFilter(self, obj, event): #specifically to catch arrow keys #pylint: disable=unused-argument # more elegant solution to link w/ self.view's scroll bar keypressevent? if event.type() == QEvent.Type.KeyPress: if not event.modifiers() & Qt.ShiftModifier: #shift + arrowkeys return False key = event.key() if key in [Qt.Key_Up, Qt.Key_Left]: self.jump_prev_insn() elif key in [Qt.Key_Down, Qt.Key_Right]: self.jump_next_insn() return True return False # pass through all other events def mousePressEvent(self, event): button = event.button() pos = self._to_logical_pos(event.pos()) if button == Qt.LeftButton and self.view.currentIndex( ) == self.SINGLE_TRACE and self._at_legend(pos): func = self._get_func_from_y(pos.y()) bbl_addr = self._get_bbl_from_y(pos.y()) self._use_precise_position = True self.curr_position = self._get_position(pos.y()) self._jump_bbl(func, bbl_addr) def _jump_bbl(self, func, bbl_addr): all_insn_addrs = self.workspace.instance.project.factory.block( bbl_addr).instruction_addrs # TODO: replace this with am_events perhaps? if func is None: return self.workspace.on_function_selected(func) self.selected_ins.clear() self.selected_ins.update(all_insn_addrs) self.selected_ins.am_event() # TODO: this ought to happen automatically as a result of the am_event self.disasm_view.current_graph.show_instruction(bbl_addr) def _get_mark_color(self, i, total): relative_gradient_pos = i * 1000 // total return self.legend_img.pixelColor(self.LEGEND_WIDTH // 2, relative_gradient_pos) def _get_mark_y(self, i): return self.TRACE_FUNC_Y + self.trace_func_unit_height * i def _show_trace_ids(self): trace_ids = self.multi_trace.get_all_trace_ids() # traceID = self.listScene.addText(id_txt, QFont("Source Code Pro", 7)) # traceID.setPos(5,5) self.listView.clearContents() self._populate_trace_table(self.listView, trace_ids) if len(self.listView.selectedItems()) <= 0 and not self.trace.am_none: for row in range(self.listView.rowCount()): item = self.listView.item(row, 0) inputItem = self.listView.item(row, 1) if self.trace.id in item.text(): item.setSelected(True) inputItem.setSelected(True) break def _show_trace_func(self, show_func_tag): x = self.TRACE_FUNC_X y = self.TRACE_FUNC_Y prev_name = None for position in self.trace.trace_func: bbl_addr = position.bbl_addr func_name = position.func_name l.debug('Draw function %x, %s', bbl_addr, func_name) color = self.trace.get_func_color(func_name) self.trace_func.addToGroup( self.traceScene.addRect(x, y, self.TRACE_FUNC_WIDTH, self.trace_func_unit_height, QPen(color), QBrush(color))) if show_func_tag is True and func_name != prev_name: tag = self.traceScene.addText(func_name, QFont("Source Code Pro", 7)) tag.setPos(x + self.TRACE_FUNC_WIDTH + self.TAG_SPACING, y - tag.boundingRect().height() // 2) self.trace_func.addToGroup(tag) anchor = self.traceScene.addLine( self.TRACE_FUNC_X + self.TRACE_FUNC_WIDTH, y, x + self.TRACE_FUNC_WIDTH + self.TAG_SPACING, y) self.trace_func.addToGroup(anchor) prev_name = func_name y += self.trace_func_unit_height @staticmethod def _make_legend_gradient(x1, y1, x2, y2): gradient = QLinearGradient(x1, y1, x2, y2) gradient.setColorAt(0.0, Qt.red) gradient.setColorAt(0.4, Qt.yellow) gradient.setColorAt(0.6, Qt.green) gradient.setColorAt(0.8, Qt.blue) gradient.setColorAt(1.0, Qt.darkBlue) return gradient def _show_legend(self): pen = QPen(Qt.transparent) gradient = self._make_legend_gradient( self.LEGEND_X, self.LEGEND_Y, self.LEGEND_X, self.LEGEND_Y + self.legend_height) brush = QBrush(gradient) self.legend = self.traceScene.addRect(self.LEGEND_X, self.LEGEND_Y, self.LEGEND_WIDTH, self.legend_height, pen, brush) reference_gradient = self._make_legend_gradient( 0, 0, self.LEGEND_WIDTH, 1000) base_img = QImage(self.LEGEND_WIDTH, 1000, QImage.Format.Format_ARGB32) p = QPainter(base_img) p.fillRect(base_img.rect(), reference_gradient) self.legend_img = base_img #reference shade def _set_mark_color(self): for p in range(self.trace.count): color = self._get_mark_color(p, self.trace.count) self.trace.set_mark_color(p, color) def _at_legend(self, pos): x = pos.x() y = pos.y() return self.TRACE_FUNC_X + self.LEGEND_X < x < self.traceView.width() and \ self.TRACE_FUNC_Y < y < self.TRACE_FUNC_Y + self.legend_height def _to_logical_pos(self, pos): x_offset = self.traceView.horizontalScrollBar().value() y_offset = self.traceView.verticalScrollBar().value() return QPoint(pos.x() + x_offset, pos.y() + y_offset) def _get_position(self, y): y_relative = y - self.legend_height - self.TAB_HEADER_SIZE return int(y_relative // self.trace_func_unit_height) def _get_bbl_from_y(self, y): position = self._get_position(y) return self.trace.get_bbl_from_position(position) def _get_func_from_y(self, y): position = self._get_position(y) func = self.trace.get_func_from_position(position) return func
class LabelingWidget(QGraphicsView): def __init__(self): super().__init__() self._tag_brushes = [] self._tag_brushes.append(QBrush(QColor(0, 0, 0, 0))) self._tag_brushes.append(QBrush(QColor(255, 0, 0, 90))) self._image_item = QGraphicsPixmapItem() self._create_default_scene() self.setScene(self._scene) def _create_default_scene(self): self._scene = QGraphicsScene() self._scene.addItem(self._image_item) line_pen = QPen(Qt.black, 2) for i in range(LABELING_TAG_WIDTH + 1): self._scene.addLine(i * LABELING_STEP, 0, i * LABELING_STEP, LABELING_HEIGHT, line_pen) for i in range(LABELING_TAG_HEIGHT + 1): self._scene.addLine(0, i * LABELING_STEP, LABELING_WIDTH, i * LABELING_STEP, line_pen) self._tags = np.zeros((LABELING_TAG_HEIGHT, LABELING_TAG_WIDTH), dtype=int) self._tags_items = [] tag_pen = QPen(Qt.black, 0) for i in range(LABELING_TAG_HEIGHT): tags = [] for j in range(LABELING_TAG_WIDTH): item = self._scene.addRect(j * LABELING_STEP, i * LABELING_STEP, LABELING_STEP, LABELING_STEP, tag_pen, self._tag_brushes[0]) tags.append(item) self._tags_items.append(tags) def _update_tags(self): for i in range(LABELING_TAG_HEIGHT): for j in range(LABELING_TAG_WIDTH): self._tags_items[i][j].setBrush( self._tag_brushes[self._tags[i, j]]) def set_image(self, path): self._image_item.setPixmap(QPixmap(path)) def clear(self): self._tags[:, :] = 0 self._update_tags() def get_tags(self): return self._tags.copy() def set_tags(self, tags): if tags.shape[0] != LABELING_TAG_HEIGHT or tags.shape[ 1] != LABELING_TAG_WIDTH: raise ValueError('Wrong tags shape') self._tags = tags.copy() self._update_tags() def resizeEvent(self, event): super().resizeEvent(event) self.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio) def mousePressEvent(self, event): point = self.mapToScene(event.pos()) index_x = int(point.x() / LABELING_STEP) index_y = int(point.y() / LABELING_STEP) if event.buttons() & Qt.LeftButton != 0: self._tags[index_y, index_x] = 1 elif event.buttons() & Qt.RightButton != 0: self._tags[index_y, index_x] = 0 self._update_tags() def mouseMoveEvent(self, event): point = self.mapToScene(event.pos()) index_x = int(point.x() / LABELING_STEP) index_y = int(point.y() / LABELING_STEP) if event.buttons() & Qt.LeftButton != 0: self._tags[index_y, index_x] = 1 elif event.buttons() & Qt.RightButton != 0: self._tags[index_y, index_x] = 0 self._update_tags()
self.mapToParent(-(self.boundingRect().width()), -(self.boundingRect().width() + 2))) if not self.scene().sceneRect().contains((newPoint)): print('move to 0, 0') newPoint = self.mapToParent(0, 0) else: self.setPos(newPoint) if __name__ == '__main__': app = QApplication(sys.argv) scene = QGraphicsScene(QRectF(-200, -200, 300, 300)) view = QGraphicsView() view.setRenderHint(QPainter.Antialiasing) boundaryPen = QPen(Qt.red) scene.addRect(scene.sceneRect()) itemCount = 20 for i in range(0, itemCount): item = MyItem() scene.addItem(item) timer = QTimer() timer.setInterval(500) timer.timeout.connect(scene.advance) view.setScene(scene) view.show() sys.exit(app.exec_())
class BarrenLandsWindow(QMainWindow): """User interface to interact with the barren lands library.""" def __init__(self, width=400, height=600, zones=None): """Initialization of BarrenLands GUI Args: width (int): The width of the canvas/field. height (int): The height of the canvas/field. zones (str): Text data to add to raw input if provided. """ QMainWindow.__init__(self) self.app = QApplication.instance() self.resources = pathlib.Path(__file__).parent self.results = set() self.canvas_width = width self.canvas_height = height self.user_input = zones # Colors for drawing onto the QGraphicsScene self.brush_barren = QBrush(QColor(120, 120, 75), Qt.Dense2Pattern) self.brush_overlay = QBrush(QColor(20, 20, 20, 35), Qt.SolidPattern) self.brush_innerlay = QBrush(QColor(32, 37, 44), Qt.SolidPattern) self.brush_fertile = QBrush(QColor(90, 220, 90, 150), Qt.Dense5Pattern) self.brush_fertile_no_go = QBrush(QColor(90, 220, 90, 150), Qt.BDiagPattern) # Setup the filed class that will handle our calculations. self.field = land.Field(width=self.canvas_width, height=self.canvas_height) self.barren_zones = list() self.fertile_zones = list() self.setup_window() self.build_core_ui() self.build_controls() def setup_window(self): """Setup all the window related settings/flags.""" self.setWindowTitle("Barren Lands") self.setMinimumHeight(600) self.setMinimumWidth(700) self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) self.setWindowIcon(QIcon(utils.get_icon())) self.setStyleSheet(utils.get_css()) def build_core_ui(self): """Sets up the core elements of this window. Notes: Setting up QGraphicsScene: https://stackoverflow.com/questions/23174481/pyside-qt-layout-not-working """ self.base_widget = QWidget(self) self.layout_base = QHBoxLayout(self.base_widget) self.layout_canvas = QVBoxLayout() self.setContentsMargins(0, 0, 0, 0) # Setup the QGraphics items to display out zones as active elements. scene_zone = QRect(0, 0, self.canvas_width, self.canvas_height) self.scene = QGraphicsScene(scene_zone, self) self.canvas = QGraphicsView(self.scene, self) self.canvas.setFixedSize(self.canvas_width + 1, self.canvas_height + 1) # Draw faint bullseye for target. self.scene.addEllipse(self.get_center_rect(300), Qt.NoPen, self.brush_overlay) self.scene.addEllipse(self.get_center_rect(200), Qt.NoPen, self.brush_innerlay) self.scene.addEllipse(self.get_center_rect(100), Qt.NoPen, self.brush_overlay) self.layout_canvas.addWidget(self.canvas) self.lbl_island = QLabel("Island Areas:") self.layout_canvas.addWidget(self.lbl_island) self.layout_base.addLayout(self.layout_canvas) # Set the core widget to the window. self.setCentralWidget(self.base_widget) def get_center_rect(self, size): """Generates a QRect that is at the center of the canvas and it's width/height set to size. Args: size (int): The size to set the bounds to. Returns: (QRectF): A rectangle coordinate that is center on the canvas with a given size. """ return QRectF(self.canvas_width * 0.5 - (size * 0.5), self.canvas_height * 0.5 - (size * 0.5), size, size) def build_controls(self): """Build the control panel that contains all the inputs.""" self.controls = QWidget(self) self.layout_controls = QVBoxLayout(self.controls) self.layout_controls.setContentsMargins(0, 0, 0, 0) # Initialize the text box for user input self.input = self.build_coord_input() # Create the buttons self.btn_add_bzone = BarrenButton(label="Add", parent=self, cmd=self.ctl_add_input) self.btn_run = BarrenButton(label="Analyze", parent=self, cmd=self.ctl_run) # Build the utility buttons self.layout_btm_btn = QHBoxLayout() self.layout_btm_btn.setContentsMargins(0, 0, 0, 0) self.btn_reset = BarrenButton(label="Reset All", parent=self, cmd=self.ctl_clear_zones) self.btn_debug = BarrenButton(label="Debug", parent=self, cmd=self.ctr_debug) self.layout_btm_btn.addWidget(self.btn_debug) self.layout_btm_btn.addWidget(self.btn_reset) self.results_grp = self.build_results_group() self.lbl_area = QLabel("Total Area: 0") self.lbl_area.setAlignment(Qt.AlignCenter) # Add everything to the controls layout self.layout_controls.addLayout(self.input) self.layout_controls.addWidget(self.btn_add_bzone) self.layout_controls.addWidget(self.btn_run) self.layout_controls.addWidget(self.results_grp) self.layout_controls.addWidget(self.lbl_area) self.layout_controls.addLayout(self.layout_btm_btn) # Add to the windows base layout. self.layout_base.addWidget(self.controls) def build_results_group(self): """Build the results group to contain the results of analysis. Returns: results_grp (QGroupBox): The group box that contains the results layout. """ grp_results = QGroupBox("Results: Largest island zone.") grp_results.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # Add a layout to hold all the results from the Analysis. self.layout_results = QVBoxLayout() self.layout_results.setContentsMargins(0, 0, 0, 0) grp_results.setLayout(self.layout_results) return grp_results def build_coord_input(self): """Build the necessary elements for the user input text box. Returns: (QVBoxLayout): The base layout of the coord input. """ layout_base = QVBoxLayout() layout_base.setContentsMargins(0, 0, 0, 0) self.raw_input = QPlainTextEdit() self.raw_input.setMaximumHeight(75) self.raw_input.setPlaceholderText('example: "0 292 399 307"') if self.user_input: self.raw_input.setPlainText(self.user_input) self.layout_input = QVBoxLayout() self.layout_input.setContentsMargins(0, 0, 0, 0) layout_base.addWidget(self.raw_input) layout_base.addLayout(self.layout_input) return layout_base def ctr_debug(self): """CONTROL: Called by the 'debug' button to randomly assign colors to the zones.""" for result in self.results: rand_color = [random.randint(0, 255) for i in range(3)] rand_color = QColor(*rand_color) brush = QBrush(rand_color, Qt.Dense5Pattern) result.rectangle.setBrush(brush) result.base = brush def ctl_add_input(self): """CONTROL: Called by the 'Add' button to handle parsing the raw input.""" raw_data = self.raw_input.toPlainText() if raw_data: parsed_input = utils.format_raw_input(raw_data) for user_input in parsed_input: start_coord = land.Coord(user_input[0], user_input[1]) end_coord = land.Coord(user_input[2], user_input[3]) zone = land.Zone(start_coord, end_coord) self.field.add_zone(zone, barren=True) self.draw_zone(zone, barren=True) def ctl_run(self): """CONTROL: Called by the 'Run' button to initialize the final analysis.""" # Make sure some values are cleared ahead of the analysis. self.clear_results() for rectangle in self.fertile_zones: self.scene.removeItem(rectangle) self.field.fertile_zones = set() # Run the analysis self.field.check_zones() total_area = 0 # Format and draw the results in the QGraphicsScene. for i, island in enumerate(self.field.islands): for zone in island: rectangle = self.draw_zone(zone) size = zone.get_size() if i == 0: # Only add (largest) island to results. Always at 0. total_area += size self.results.add( ResultLabel(label=str(size), rectangle=rectangle, zone=zone)) else: # This zone is fertile, but Inaccessible. rectangle.setBrush(self.brush_fertile_no_go) self.canvas.update() # Pain the update for the user to see the new zone. self.app.processEvents() # Dont show all at the same time. (For more pleasing visual) time.sleep(.015) # Print islands, smallest to largest. print("Islands", self.field.islands_as_area()) island_areas = " ".join(str(i) for i in self.field.islands_as_area()) self.lbl_island.setText(f"Island Areas: {island_areas}") # Set the label as the total area as the largest island. self.lbl_area.setText(f"Total Area: {total_area}") # Sort the results by their zones area. for result in sorted(self.results, key=lambda x: x.zone.get_size()): # Add the result to the results layout in the UI self.layout_results.addWidget(result) def ctl_clear_zones(self): """CONTROL: Called by the 'Reset' button to handle resetting the data and graphics.""" for rectangle in self.barren_zones + self.fertile_zones: self.scene.removeItem(rectangle) self.field.fertile_zones = set() self.fertile_zones = list() self.field.barren_zones = set() self.barren_zones = list() self.clear_results() def clear_results(self): """Clears the results set in preparation for incoming new results.""" for result in self.results: result.deleteLater() self.results = set() self.lbl_area.setText("Total Area:") self.lbl_island.setText("Island Areas:") def draw_zone(self, zone, barren=False): """Creates a QGraphicsRecItem from a Zone and adds it to the scene. Args: zone (Zone): The zone to generate the rectangle from. barren (bool): barren colors if True else fertile color. Returns: (QGraphicsRecItem): The drown rectangle that is added to the canvas. """ # Build the QGraphicsRecItem from the zone data. rectangle = self.scene.addRect(zone.start.x, zone.start.y, zone.end.x - zone.start.x + 1, zone.end.y - zone.start.y + 1) if barren: self.barren_zones.append(rectangle) brush = self.brush_barren else: self.fertile_zones.append(rectangle) brush = self.brush_fertile # Apply the color to the rectangle item. rectangle.setBrush(brush) # Remove the default border rectangle.setPen(Qt.NoPen) return rectangle
class TestGUI(QMainWindow): def __init__(self): self.app = QApplication(sys.argv) ui_file = QFile("testgui.ui") ui_file.open(QFile.ReadOnly) loader = QUiLoader() self.window = loader.load(ui_file) ui_file.close() self.scene_gt = QGraphicsScene() self.scene_gt.setSceneRect(-2000, -3000, 4000, 6000) self.view_gt = QGraphicsView(self.scene_gt, self.window.scene_gt) self.view_gt.scale(-0.1, 0.1) self.view_gt.resize(self.window.scene_gt.geometry().width(), self.window.scene_gt.geometry().height()) self.personPos_gt = self.scene_gt.addEllipse( QRectF(-200, -200, 400, 400), QPen(QColor("LightGreen")), QBrush(QColor("LightGreen"))) self.personPos_gt.setFlag(QGraphicsItem.ItemIsMovable) self.personPos_gt.setPos(0, 0) self.personAng_gt = self.scene_gt.addRect(QRectF(-10, 0, 20, 300), QPen(QColor("Green")), QBrush(QColor("Green"))) self.personAng_gt.setFlag(QGraphicsItem.ItemIsMovable) self.personAng_gt.setPos(0, 0) self.scene_cc = QGraphicsScene() self.scene_cc.setSceneRect(-2000, -3000, 4000, 6000) self.view_cc = QGraphicsView(self.scene_cc, self.window.scene_cc) self.view_cc.scale(-0.1, 0.1) self.view_cc.resize(self.window.scene_cc.geometry().width(), self.window.scene_cc.geometry().height()) self.personPos_cc = self.scene_cc.addEllipse( QRectF(-200, -200, 400, 400), QPen(QColor("Red")), QBrush(QColor("Red"))) self.personPos_cc.setFlag(QGraphicsItem.ItemIsMovable) self.personPos_cc.setPos(0, 0) self.personAng_cc = self.scene_cc.addRect(QRectF(-10, 0, 20, 300), QPen(QColor("Black")), QBrush(QColor("Black"))) self.personAng_cc.setFlag(QGraphicsItem.ItemIsMovable) self.personAng_cc.setPos(0, 0) self.scene_nn = QGraphicsScene() self.scene_nn.setSceneRect(-2000, -3000, 4000, 6000) self.view_nn = QGraphicsView(self.scene_nn, self.window.scene_nn) self.view_nn.scale(-0.1, 0.1) self.view_nn.resize(self.window.scene_nn.geometry().width(), self.window.scene_nn.geometry().height()) self.personPos_nn = self.scene_nn.addEllipse( QRectF(-200, -200, 400, 400), QPen(QColor("LightBlue")), QBrush(QColor("LightBlue"))) self.personPos_nn.setFlag(QGraphicsItem.ItemIsMovable) self.personPos_nn.setPos(0, 0) self.personAng_nn = self.scene_nn.addRect(QRectF(-10, 0, 20, 300), QPen(QColor("Blue")), QBrush(QColor("Blue"))) self.personAng_nn.setFlag(QGraphicsItem.ItemIsMovable) self.personAng_nn.setPos(0, 0) self.loadData(sys.argv[1]) self.window.instantScrollBar.valueChanged.connect(self.changeInstant) self.it = 0 self.timer = QTimer() self.timer.timeout.connect(self.compute) self.timer.start(150) self.window.show() r = self.app.exec_() sys.exit(r) def loadData(self, filename): test_dataset = graph_generator.CalibrationDataset(filename, 'run', '1') with open(sys.argv[1], 'r') as f: raw = f.read() raw = list(raw) raws = ''.join(raw) data = json.loads(raws)['data_set'] model = trackerapi.TrackerAPI('.', test_dataset) self.x_gt = [] self.x_cc = [] self.x_nn = [] self.z_gt = [] self.z_cc = [] self.z_nn = [] self.a_gt = [] self.a_cc = [] self.a_nn = [] try: with open('kk', 'rb') as f: results = pickle.load(f) except: results = [x for x in model.predict()] with open('kk', 'wb') as f: pickle.dump(results, f, pickle.HIGHEST_PROTOCOL) eX_cc = [] eZ_cc = [] eA_cc = [] eX_nn = [] eZ_nn = [] eA_nn = [] self.trajectory = [] s = 0 ang_prev = 0 for i in range(int(len(results))): n_joints = 0 for cam in range(len(data[i]['superbody'])): n_joints += len(data[i]['superbody'][cam]['joints']) if n_joints < 3: continue s += 1 if s < 2 or s % 1 != 0: continue self.x_gt.append(data[i]['superbody'][0]['ground_truth'][0]) self.z_gt.append(data[i]['superbody'][0]['ground_truth'][2]) self.a_gt.append(data[i]['superbody'][0]['ground_truth'][3] * 180. / math.pi) self.trajectory.append(QPointF(self.x_gt[-1], self.z_gt[-1])) x_cc = 0 z_cc = 0 ncams = 0 for cam in range(0, len(data[i]['superbody'])): x_cc += data[i]['superbody'][cam]['world'][0] z_cc += data[i]['superbody'][cam]['world'][2] ncams += 1 self.x_cc.append(x_cc / ncams) self.z_cc.append(z_cc / ncams) s_cc = 0 c_cc = 0 ncams = 0 for cam in range(0, len(data[i]['superbody'])): joints = data[i]['superbody'][cam]["joints"] if ("right_shoulder" in joints and "left_shoulder" in joints) or ("right_hip" in joints and "left_hip" in joints): s_cc = math.sin(data[i]['superbody'][cam]['world'][3]) c_cc = math.cos(data[i]['superbody'][cam]['world'][3]) ncams += 1 if ncams > 0: a_cc = math.atan2(s_cc / ncams, c_cc / ncams) ang_prev = a_cc else: a_cc = ang_prev self.a_cc.append(a_cc * 180. / math.pi) self.x_nn.append(results[i][0] * 4000) self.z_nn.append(results[i][2] * 4000) self.a_nn.append( math.atan2(results[i][3] / 0.7, results[i][4] / 0.7) * 180. / math.pi) eX_cc.append(abs(self.x_gt[-1] - self.x_cc[-1])) eZ_cc.append(abs(self.z_gt[-1] - self.z_cc[-1])) eAng = 180 - abs(abs(self.a_gt[-1] - self.a_cc[-1]) - 180) if eAng < 0: eAng = 360 + eAng eA_cc.append(eAng) eX_nn.append(abs(self.x_gt[-1] - self.x_nn[-1].item())) eZ_nn.append(abs(self.z_gt[-1] - self.z_nn[-1].item())) eAng = 180 - abs(abs(self.a_gt[-1] - self.a_nn[-1]) - 180) if eAng < 0: eAng = 360 + eAng eA_nn.append(eAng) array_err_z = np.array(eZ_nn) print("len array error", len(array_err_z)) # self.window.eX_cc.display(np.array(eX_cc).mean()) # self.window.eZ_cc.display(np.array(eZ_cc).mean()) # self.window.eA_cc.display(np.array(eA_cc).mean()) # self.window.eX_nn.display(np.array(eX_nn).mean()) # self.window.eZ_nn.display(np.array(eZ_nn).mean()) # self.window.eA_nn.display(np.array(eA_nn).mean()) self.scene_gt.addPolygon(self.trajectory, QPen(QColor("Black"))) self.scene_cc.addPolygon(self.trajectory, QPen(QColor("Black"))) self.scene_nn.addPolygon(self.trajectory, QPen(QColor("Black"))) self.window.instantScrollBar.setMaximum(len(self.a_gt) - 1) def compute(self): if self.window.playButton.isChecked(): if self.it >= len(self.x_gt): self.it = 0 self.movePerson() self.it += 1 self.window.instantScrollBar.setValue(self.it) def changeInstant(self, instant): self.it = instant self.movePerson() def movePerson(self): self.personPos_gt.setPos(self.x_gt[self.it], self.z_gt[self.it]) self.personAng_gt.setPos(self.x_gt[self.it], self.z_gt[self.it]) self.personAng_gt.setRotation(self.a_gt[self.it]) self.personPos_cc.setPos(self.x_cc[self.it], self.z_cc[self.it]) self.personAng_cc.setPos(self.x_cc[self.it], self.z_cc[self.it]) self.personAng_cc.setRotation(self.a_cc[self.it]) self.personPos_nn.setPos(self.x_nn[self.it], self.z_nn[self.it]) self.personAng_nn.setPos(self.x_nn[self.it], self.z_nn[self.it]) self.personAng_nn.setRotation(self.a_nn[self.it])
class QTraceViewer(QWidget): TAG_SPACING = 50 LEGEND_X = -50 LEGEND_Y = 0 LEGEND_WIDTH = 10 TRACE_FUNC_X = 0 TRACE_FUNC_Y = 0 TRACE_FUNC_WIDTH = 50 TRACE_FUNC_MINHEIGHT = 1000 MARK_X = LEGEND_X MARK_WIDTH = TRACE_FUNC_X - LEGEND_X + TRACE_FUNC_WIDTH MARK_HEIGHT = 1 def __init__(self, workspace, disasm_view, parent=None): super().__init__(parent=parent) self.workspace = workspace self.disasm_view = disasm_view self.view = None self.scene = None self.mark = None self.curr_position = 0 self._use_precise_position = False self._init_widgets() self.trace.am_subscribe(self._on_set_trace) self.selected_ins.am_subscribe(self._on_select_ins) self.view.installEventFilter(self) # # Forwarding properties # @property def trace(self): return self.workspace.instance.trace @property def selected_ins(self): return self.disasm_view.infodock.selected_insns def _init_widgets(self): self.view = QGraphicsView() self.scene = QGraphicsScene() self.view.setScene(self.scene) self._reset() layout = QHBoxLayout() layout.addWidget(self.view) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(self.view, Qt.AlignLeft) self.setLayout(layout) def _reset(self): self.scene.clear() #clear items self.mark = None self.legend = None self.legend_height = 0 self.trace_func = QGraphicsItemGroup() self.scene.addItem(self.trace_func) self.hide() def _on_set_trace(self, **kwargs): self._reset() if self.trace.am_obj is not None: l.debug('minheight: %d, count: %d', self.TRACE_FUNC_MINHEIGHT, self.trace.count) if self.trace.count <= 0: l.warning( "No valid addresses found in trace to show. Check base address offsets?" ) self.trace.am_obj = None self.trace.am_event() return if self.TRACE_FUNC_MINHEIGHT < self.trace.count * 15: self.trace_func_unit_height = 15 show_func_tag = True else: self.trace_func_unit_height = self.TRACE_FUNC_MINHEIGHT / self.trace.count show_func_tag = True self.legend_height = int(self.trace.count * self.trace_func_unit_height) self._show_trace_func(show_func_tag) self._show_legend() self._set_mark_color() self.scene.setSceneRect(self.scene.itemsBoundingRect()) #resize self.setFixedWidth(self.scene.itemsBoundingRect().width()) self.view.setFixedWidth(self.scene.itemsBoundingRect().width()) self.show() def _on_select_ins(self, **kwargs): if self.trace == None: return if self.mark is not None: for i in self.mark.childItems(): self.mark.removeFromGroup(i) self.scene.removeItem(i) self.scene.removeItem(self.mark) self.mark = QGraphicsItemGroup() self.scene.addItem(self.mark) if self.selected_ins: addr = next(iter(self.selected_ins)) positions = self.trace.get_positions(addr) if positions: #if addr is in list of positions if not self._use_precise_position: #handle case where insn was selected from disas view self.curr_position = positions[0] - self.trace.count for p in positions: color = self._get_mark_color(p, self.trace.count) y = self._get_mark_y(p, self.trace.count) if p == self.trace.count + self.curr_position: #add thicker line for 'current' mark self.mark.addToGroup( self.scene.addRect(self.MARK_X, y, self.MARK_WIDTH, self.MARK_HEIGHT * 4, QPen(QColor('black')), QBrush(color))) else: self.mark.addToGroup( self.scene.addRect(self.MARK_X, y, self.MARK_WIDTH, self.MARK_HEIGHT, QPen(color), QBrush(color))) #y = self._get_mark_y(positions[0], self.trace.count) #self.view.verticalScrollBar().setValue(y - 0.5 * self.view.size().height()) self.scene.update() #force redraw of the scene self.scroll_to_position(self.curr_position) def scroll_to_position(self, position): relative_pos = self.trace.count + position y_offset = self._get_mark_y(relative_pos, self.trace.count) scrollValue = 0 if y_offset > 0.5 * self.view.size().height(): scrollValue = y_offset - 0.5 * self.view.size().height() scrollValue = min(scrollValue, self.view.verticalScrollBar().maximum()) self.view.verticalScrollBar().setValue(scrollValue) self._use_precise_position = False def jump_next_insn(self): if self.curr_position + self.trace.count < self.trace.count - 1: #for some reason indexing is done backwards self.curr_position += 1 self._use_precise_position = True func_name = self.trace.trace_func[self.curr_position].func_name func = self._get_func_from_func_name(func_name) bbl_addr = self.trace.trace_func[self.curr_position].bbl_addr self._jump_bbl(func, bbl_addr) def jump_prev_insn(self): if self.curr_position + self.trace.count > 0: self.curr_position -= 1 self._use_precise_position = True func_name = self.trace.trace_func[self.curr_position].func_name func = self._get_func_from_func_name(func_name) bbl_addr = self.trace.trace_func[self.curr_position].bbl_addr self._jump_bbl(func, bbl_addr) def eventFilter(self, object, event): #specifically to catch arrow keys # more elegant solution to link w/ self.view's scroll bar keypressevent? if event.type() == QEvent.Type.KeyPress: if not (event.modifiers() & Qt.ShiftModifier): #shift + arrowkeys return False key = event.key() if key == Qt.Key_Up or key == Qt.Key_Left: self.jump_prev_insn() elif key == Qt.Key_Down or key == Qt.Key_Right: self.jump_next_insn() return True return False # pass through all other events def mousePressEvent(self, event): button = event.button() pos = self._to_logical_pos(event.pos()) if button == Qt.LeftButton and self._at_legend(pos): func = self._get_func_from_y(pos.y()) bbl_addr = self._get_bbl_from_y(pos.y()) self._use_precise_position = True self.curr_position = self._get_position(pos.y()) self._jump_bbl(func, bbl_addr) def _jump_bbl(self, func, bbl_addr): all_insn_addrs = self.workspace.instance.project.factory.block( bbl_addr).instruction_addrs # TODO: replace this with am_events perhaps? self.workspace.on_function_selected(func) self.selected_ins.clear() self.selected_ins.update(all_insn_addrs) self.selected_ins.am_event() # TODO: this ought to happen automatically as a result of the am_event self.disasm_view.current_graph.show_instruction(bbl_addr) def _get_mark_color(self, i, total): relative_gradient_pos = i * 1000 // total return self.legend_img.pixelColor(self.LEGEND_WIDTH // 2, relative_gradient_pos) def _get_mark_y(self, i, total): return self.TRACE_FUNC_Y + self.trace_func_unit_height * i def _show_trace_func(self, show_func_tag): x = self.TRACE_FUNC_X y = self.TRACE_FUNC_Y prev_name = None for position in self.trace.trace_func: bbl_addr = position.bbl_addr func_name = position.func_name l.debug('Draw function %x, %s', bbl_addr, func_name) color = self.trace.get_func_color(func_name) self.trace_func.addToGroup( self.scene.addRect(x, y, self.TRACE_FUNC_WIDTH, self.trace_func_unit_height, QPen(color), QBrush(color))) if show_func_tag is True and func_name != prev_name: tag = self.scene.addText(func_name, QFont("Source Code Pro", 7)) tag.setPos(x + self.TRACE_FUNC_WIDTH + self.TAG_SPACING, y - tag.boundingRect().height() // 2) self.trace_func.addToGroup(tag) anchor = self.scene.addLine( self.TRACE_FUNC_X + self.TRACE_FUNC_WIDTH, y, x + self.TRACE_FUNC_WIDTH + self.TAG_SPACING, y) self.trace_func.addToGroup(anchor) prev_name = func_name y += self.trace_func_unit_height def _make_legend_gradient(self, x1, y1, x2, y2): gradient = QLinearGradient(x1, y1, x2, y2) gradient.setColorAt(0.0, Qt.red) gradient.setColorAt(0.4, Qt.yellow) gradient.setColorAt(0.6, Qt.green) gradient.setColorAt(0.8, Qt.blue) gradient.setColorAt(1.0, Qt.darkBlue) return gradient def _show_legend(self): pen = QPen(Qt.transparent) gradient = self._make_legend_gradient( self.LEGEND_X, self.LEGEND_Y, self.LEGEND_X, self.LEGEND_Y + self.legend_height) brush = QBrush(gradient) self.legend = self.scene.addRect(self.LEGEND_X, self.LEGEND_Y, self.LEGEND_WIDTH, self.legend_height, pen, brush) reference_gradient = self._make_legend_gradient( 0, 0, self.LEGEND_WIDTH, 1000) base_img = QImage(self.LEGEND_WIDTH, 1000, QImage.Format.Format_ARGB32) p = QPainter(base_img) p.fillRect(base_img.rect(), reference_gradient) self.legend_img = base_img #reference shade def _set_mark_color(self): for p in range(self.trace.count): color = self._get_mark_color(p, self.trace.count) self.trace.set_mark_color(p, color) def _at_legend(self, pos): x = pos.x() y = pos.y() if self.TRACE_FUNC_X + self.LEGEND_X < x < self.view.width() and \ self.TRACE_FUNC_Y < y < self.TRACE_FUNC_Y + self.legend_height: return True else: return False def _to_logical_pos(self, pos): x_offset = self.view.horizontalScrollBar().value() y_offset = self.view.verticalScrollBar().value() return QPoint(pos.x() + x_offset, pos.y() + y_offset) def _get_position(self, y): y_relative = y - self.legend_height return int(y_relative // self.trace_func_unit_height) def _get_bbl_from_y(self, y): position = self._get_position(y) return self.trace.get_bbl_from_position(position) def _get_func_from_func_name(self, func_name): return self.workspace.instance.kb.functions.function(name=func_name) def _get_func_from_y(self, y): position = self._get_position(y) func_name = self.trace.get_func_name_from_position(position) return self._get_func_from_func_name(func_name)