예제 #1
0
 def _widgetToElement(self, widget: QtWidgets.QWidget) -> Element:
     from IceSpringMusicPlayer.common.replacerMixin import ReplacerMixin
     assert isinstance(widget, (ReplacerMixin, QtWidgets.QWidget))
     return Element(
         clazz=type(widget),
         vertical=isinstance(widget, QtWidgets.QSplitter) and widget.orientation() == QtCore.Qt.Orientation.Vertical,
         weight=widget.height() if isinstance(widget.parentWidget(), QtWidgets.QSplitter) and gg(
             widget.parentWidget()).orientation() == QtCore.Qt.Orientation.Vertical else widget.width(),
         config=widget.getWidgetConfig() if isinstance(widget, PluginWidgetMixin) else dict(),
         children=[self._widgetToElement(widget.widget(x)) for x in range(widget.count())] if isinstance(
             widget, SplitterWidget) else []
     )
예제 #2
0
    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()
예제 #3
0
class Robotic_Viewer_NG(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle('A Wonderfull Name')
        self.resize(1080,720)
        self.setWindowIcon(QIcon(QPixmap('smallLogo.png')))
        self.setStyleSheet("QMainWindow {background: '#f1fcb3';}")
        self.setMinimumWidth(960)
        self.setMinimumHeight(720)

        ## Création des widgets
        self.settingsWidget = QWidget(self)
        ## Configuration des signals/slots
        self.timeSimu = 90
        self.time = 0
        self.rythmSim = QTimer()
        self.rythmSim.timeout.connect(self.simulationStep)
        self.buildGameElements()
        ## Configuration des widgets
        self.mapPx = QImage('TableCDFR2020.png').scaledToWidth(self.width()*2/3,aspectmode='KeepAspectRatio')
        self.scaleFactor = 300/self.mapPx.width()
        self.buildSettingsGroupBox(self.settingsWidget)
        self.settingsWidget.setStyleSheet("background: '#94e4f7';")
        self.fitToWindow()

    def buildGameElements(self):
        # Création d'une stratégie de jeux        
        self.robotM = VirtualRobot('MarioBot')
        self.robotG = VirtualRobot('GuiguiBot')
        self.robots = [ self.robotM , self.robotG ]

        self.brain = Strategy()
        self.brain.addRobot(self.robotM)
        self.brain.addRobot(self.robotG)

        self.robotM_traget = Circle(0,0,5)
        self.robotG_traget = Circle(0,0,5)

        # Création d'action supplémentaires
        self.act1 = Action('ManuelM',90,25,np.deg2rad(0))
        self.act2 = Action('ManuelG',70,25,np.deg2rad(180))

        self.brain.addAction(self.act1.getID(),self.act1)
        self.brain.addAction(self.act2.getID(),self.act2)

        # Création d'element de jeux externes
        l_cercA = Circle(80,-30,30)
        l_cercB = Circle(30,330,30)
        l_cercC = Circle(80,330,30)
        l_cercD = Circle(30,-30,40)

        self.staticElts = [l_cercA,l_cercB,l_cercC,l_cercD]

        i = 0
        for elt in self.staticElts :
            elt.setID(elt.getID()+' '+repr(i))
            print(elt)
            i = i +1

        self.rstElmtPos()

    def rstElmtPos(self):
        self.robotM.setStaticState(90,25,np.deg2rad(0))
        self.robotG.setStaticState(70,25,np.deg2rad(180))

        self.repaint()

    def rstElmtStat(self):
        #self.staticElts = [self.cercA,self.cercB,self.cercC,self.cercD]
        self.repaint()

    def paintEvent(self, event):
        mapP = QPainter()
        mapP.begin(self)
        mapP.drawImage(self.centralGP.x(),self.centralGP.y(),self.mapPx)
        mapP.end()

        self.drawRobot(self.robotM,'blue')
        self.drawRobot(self.robotG,'purple')
        self.drawElts(self.staticElts,'black','pink')
        self.drawElts([self.robotM_traget,self.robotG_traget],'red','white')

    def drawElts(self,elt_list,colorElt,colotTxt):
        for elt in elt_list :
            l_pp = self.getRealFromUiCoord(elt.getX(),elt.getY())
            eltP = QPainter()
            eltP.begin(self)
            eltP.setBrush(QBrush(QColor(colorElt)))

            myPen = QPen()
            myPen.setBrush(QBrush(QColor(colotTxt)))

            if 'rect' in elt.getID() :
                l_pw = elt.getWidth()/self.scaleFactor
                l_pl = elt.getLength()/self.scaleFactor
                eltP.drawRect(l_pp.getX()-(l_pw/2),l_pp.getY()-(l_pl/2),l_pw,l_pl)

                eltP.setPen(myPen)
                eltP.drawText(l_pp.getX()-(l_pw/4),l_pp.getY(),l_pw,l_pl,1,elt.getID())
            elif 'circle' in elt.getID() :
                l_pr = elt.getRayon()/self.scaleFactor
                eltP.drawEllipse(l_pp.getX()-(l_pr/2),l_pp.getY()-(l_pr/2),l_pr,l_pr)

                eltP.setPen(myPen)
                eltP.drawText(l_pp.getX()-l_pr/3,l_pp.getY(),l_pr,l_pr,1,elt.getID())
            else :
                pass
            eltP.end()

    def drawRobot(self, robot, color):
        # Position computing
        relativePoint = self.getRealFromUiCoord(robot.getPosition().x,robot.getPosition().y)
        w_tr = robot.getWidth()/(2*self.scaleFactor)
        l_tr = robot.getLength()/(4*self.scaleFactor)
        x_center = relativePoint.x # w_tr
        y_center = relativePoint.y #- l_tr

        pts_list = QPoint(-w_tr,-l_tr), QPoint(-w_tr,l_tr), QPoint(0,l_tr*1.2), QPoint(w_tr,l_tr),QPoint(w_tr,-l_tr),QPoint(-w_tr,-l_tr)

        polyst = self.rotatePolygon(pts_list,robot.getAngle())
        poly = QPolygon(polyst)
        poly.translate(x_center,y_center)

        # Draw Item
        myPen = QPen()
        myPen.setBrush(QBrush(QColor(color)))
        myPen.setWidth(5)

        robotP = QPainter()
        robotP.begin(self)
        robotP.setPen(myPen)
        robotP.drawPolyline(poly)
        robotP.end()

    def rotatePolygon(self,polygon,teta):
        rotatedPolygon = []
        for corner in polygon :
            Vi = np.array([corner.x(),corner.y(),1])
            Mrot = np.array([ [np.cos(teta), np.sin(teta),0],
                              [-np.sin(teta), np.cos(teta),0],
                              [ 0,0,0] ])
            Vo = np.dot(Mrot,np.transpose(Vi))

            rotatedPolygon.append(QPoint(Vo[0],Vo[1]))
        return rotatedPolygon

    def buildSettingsGroupBox(self, a_widget) :
        self.settingsGroupBox = QGroupBox('Settings')
        l_gridLayout = QGridLayout()

        self.labelRegTitle = QLabel('Position')           # 0
        self.lineEditReg = QLineEdit()                      # 1
        self.lineEditReg.setText("X : 0 - Y : 0 - T : 0")   # 1
        self.pushBtnRegRst = QPushButton('Restart')         # 2
        self.pushBtnRegRst.clicked.connect(self.rstElmtPos) # 2
        self.labelRegTarget = QLabel('Target : not defined')  # 3
        self.selectedRobot = 0

        self.labelSimuTitle = QLabel('Simulation')      # 0
        self.pushBtnStartSimu = QPushButton('Start')    # 1
        self.pushBtnStartSimu.clicked.connect(self.startSimu)
        self.pushBtnStopSimu = QPushButton('Stop')      # 2
        self.pushBtnStopSimu.clicked.connect(self.stopSimu)
        self.labelSimuInfos = QLabel('Durée de simulation : '+repr(self.timeSimu)+' sec')   # 4
        self.hSlider= QSlider()                         # 3
        self.hSlider.setOrientation(Qt.Horizontal)      # 3
        self.hSlider.valueChanged.connect(self.sliderEvolution)
        self.hSlider.setValue(self.timeSimu)
        self.hSlider.setRange(0,120)

        self.labelPfTitle = QLabel('Path Finding')      # 0
        self.comboBoxAlgoPF = QComboBox()               # 1
        self.comboBoxAlgoPF.addItem('A*')               # 1
        self.comboBoxAlgoPF.addItem('Dijtra')           # 1
        self.comboBoxLenghtPF = QComboBox()             # 2
        self.comboBoxLenghtPF.addItem('Manathan')       # 2
        self.comboBoxLenghtPF.addItem('Euclidian')      # 2
        self.pushBtnPF = QPushButton('NOT USED')         # 3
        self.pushBtnPF.clicked.connect(self.rstElmtStat)
        self.labelPfElt = QLabel('')                    # 4

        self.labelStratTitle = QLabel('Stratégie')      # 0
        self.labelStratRbtG = QLabel('GuiGuiBot')       # 0
        self.comboBoxStratRbtG = QComboBox()            # 1
        for l_act in self.brain.getActionKeys() :
            self.comboBoxStratRbtG.addItem(l_act)
        self.comboBoxStratRbtG.currentIndexChanged.connect(self.updateStrategies)
        self.labelStratRbtM = QLabel('MarioBot')        # 2
        self.comboBoxStratRbtM = QComboBox()            # 3
        for l_act in self.brain.getActionKeys() :
            self.comboBoxStratRbtM.addItem(l_act)
        self.comboBoxStratRbtM.currentIndexChanged.connect(self.updateStrategies)

        l_gridLayout.addWidget(self.labelRegTitle,0,0)
        l_gridLayout.addWidget(self.lineEditReg,1,0)
        l_gridLayout.addWidget(self.pushBtnRegRst,2,0)
        l_gridLayout.addWidget(self.labelRegTarget,3,0)

        l_gridLayout.addWidget(self.labelSimuTitle,0,1)
        l_gridLayout.addWidget(self.pushBtnStartSimu,1,1)
        l_gridLayout.addWidget(self.pushBtnStopSimu,2,1)
        l_gridLayout.addWidget(self.hSlider,3,1)
        l_gridLayout.addWidget(self.labelSimuInfos,4,1)

        l_gridLayout.addWidget(self.labelStratTitle,0,2)
        l_gridLayout.addWidget(self.labelStratRbtG,1,2)
        l_gridLayout.addWidget(self.comboBoxStratRbtG,2,2)
        l_gridLayout.addWidget(self.labelStratRbtM,3,2)
        l_gridLayout.addWidget(self.comboBoxStratRbtM,4,2)

        l_gridLayout.addWidget(self.labelPfTitle,0,3)
        l_gridLayout.addWidget(self.comboBoxAlgoPF,1,3)
        l_gridLayout.addWidget(self.comboBoxLenghtPF,2,3)
        l_gridLayout.addWidget(self.pushBtnPF,3,3)
        l_gridLayout.addWidget(self.labelPfElt,4,3)

        l_gridLayout.setColumnStretch(0, a_widget.width()/4)
        l_gridLayout.setColumnStretch(1, a_widget.width()/4)
        l_gridLayout.setColumnStretch(2, a_widget.width()/4)
        l_gridLayout.setColumnStretch(3, a_widget.width()/4)

        self.settingsGroupBox.setLayout(l_gridLayout)
        vbox = QVBoxLayout()
        vbox.addWidget(self.settingsGroupBox)

        a_widget.setLayout(vbox)

    def startSimu(self):
        self.time = 0
        self.rythmSim.start(100)

    def stopSimu(self):
        self.rythmSim.stop()

    def updateStrategies(self):
        self.brain.assignAction(self.comboBoxStratRbtG.currentText(),self.robotG.getID())
        self.brain.assignAction(self.comboBoxStratRbtM.currentText(),self.robotM.getID())

    def simulationStep(self):
        if self.time < self.timeSimu :
            # Appel de la stratégie
            self.brain.beat()

            # Simulation des robots
            for rbt in self.robots :
                rbt.updateState(self.rythmSim.interval())               # Refresh position
                rbt.updateSensors(self.brain.getElements().values())    # Refresh sensors data

            # Mise à jour de l'interface graphique
            self.time = self.time + (self.rythmSim.interval()/1000)
            self.labelSimuInfos.setText('Simulation : '+repr(round(self.time,2))+'\t/ '+repr(self.timeSimu)+' sec')
            self.robotM_traget.setXY(self.robotM.getTarget().getPosition().getX(),self.robotM.getTarget().getPosition().getY())
            self.robotG_traget.setXY(self.robotG.getTarget().getPosition().getX(),self.robotG.getTarget().getPosition().getY())
            self.repaint()
        else :
            pass

    def fitToWindow(self):
        self.settingsWidget.setGeometry(0,0,self.width(),self.height()/4)
        self.centralGP = QPoint((self.width()-self.mapPx.width())/2, self.settingsWidget.height()+20)
        self.repaint()

    def getUiFromRealCoord(self,x,y):
        ty = -self.centralGP.x()
        tx = -self.centralGP.y()
        s = self.scaleFactor

        Vre = np.array([x,y,s])
        Mtr = np.array([ [ 0,s,tx],
                         [ s,0,ty],
                         [ 0,0,0] ])
        Vui = np.dot(Mtr,np.transpose(Vre))

        l_x = Vui[0]
        l_y = Vui[1]
        return Point(round(l_x),round(l_y))

    def getRealFromUiCoord(self,x,y):
        tx = (y/self.scaleFactor)
        ty = (x/self.scaleFactor)
        Vui = np.array([self.centralGP.x(),self.centralGP.y(),1])
        Mtr = np.array([ [ 1,0,tx],
                         [ 0,1,ty],
                         [ 0,0,1] ])
        Vre = np.dot(Mtr,np.transpose(Vui))

        l_x = Vre[0]
        l_y = Vre[1]
        return Point(round(l_x),round(l_y))

    def mousePressEvent(self, it):
        rel_pt = self.getUiFromRealCoord(it.x(),it.y())
        if it.button()== Qt.MouseButton.RightButton and self.selectedRobot != 0:
            self.labelRegTarget.setText('Manual target of '+self.selectedRobot.getID()+' X: '+repr(rel_pt.x)+' Y:'+repr(rel_pt.y))
            if 'M' in self.selectedRobot.getID():
                self.brain.modifyAction('ManuelM',State(rel_pt.x,rel_pt.y,np.pi))
            if 'G' in self.selectedRobot.getID():
                self.brain.modifyAction('ManuelG',State(rel_pt.x,rel_pt.y,np.pi))

        if it.button()== Qt.MouseButton.LeftButton :
            for rbt in self.robots :
                if rbt.getPosition().distanceFrom(rel_pt)<10 :
                    self.selectedRobot = rbt
                    self.labelRegTarget.setText(self.selectedRobot.getID()+ ' selected...')

    def refreshEltLabel(self):
        l_str = ''
        for i in self.brain.getElementsKeys():
            l_str = i+' '+l_str
        self.labelPfElt.setText(l_str)

    def sliderEvolution(self,it):
        self.timeSimu = self.hSlider.value()
        self.labelSimuInfos.setText('Durée de simulation :'+repr(self.timeSimu)+' sec')

    def wheelEvent(self,event):
        angle_delta = 10.0*(event.delta()/120)
        rel_pt = self.getUiFromRealCoord(event.x(),event.y())
        
        for rbt in self.robots :
            if rbt.getPosition().distanceFrom(rel_pt)<10 :
                rbt.setAngle(rbt.getAngle() + np.deg2rad(angle_delta))
                self.lineEditReg.setText(rbt.__str__()+' '+rbt.getPosition().__str__())
                self.repaint()

    def mouseMoveEvent(self,it):
        rel_pt = self.getUiFromRealCoord(it.x(),it.y())

        table = Rect(100,150,200,300)

        for rbt in self.robots:
            if rbt.getPosition().distanceFrom(rel_pt)<10 :
                rbt.setPosition(rel_pt.x,rel_pt.y)
                self.lineEditReg.setText(rbt.__str__()+' '+rbt.getPosition().__str__())
                self.repaint()

        for elt in self.staticElts:
            if elt.getPosition().distanceFrom(rel_pt)<10 :
                elt.setPosition(rel_pt.x,rel_pt.y)

                if table.contains(elt.getPosition()):
                    if not self.brain.containsElement(elt.getID()):
                        self.brain.addElement(elt.getID(),elt)
                        self.refreshEltLabel()
                    else :
                        pass
                else :
                    if self.brain.containsElement(elt.getID()):
                        self.brain.removeElement(elt.getID())
                        self.refreshEltLabel()
                    else:
                        pass
                self.lineEditReg.setText(elt.__str__())
            self.repaint()

    def resizeEvent(self, it):
        self.fitToWindow()
예제 #4
0
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
예제 #5
0
class MainWindow(QMainWindow):
    # Конструктор
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle('ВКР')
        self.setMinimumSize(800, 600)
        self.central_widget = QWidget(self)
        self.layout = QVBoxLayout(self.central_widget)
        self.modules_cb = QComboBox()
        self.modules_cb.currentIndexChanged.connect(self.set_module)
        self.buttons_widget = QWidget()
        self.scroll = QScrollArea()
        self.modules = []
        self.init_ui()

    # Метод инициализации UI
    def init_ui(self):
        file_menu = self.menuBar().addMenu('Файл')
        help_menu = self.menuBar().addMenu('Помощь')
        create_module_action = file_menu.addAction('Создать новый модуль')
        create_module_action.triggered.connect(self.create_module)
        add_module_action = file_menu.addAction('Добавить существующий модуль')
        add_module_action.triggered.connect(self.add_module)
        close_action = file_menu.addAction('Закрыть программу')
        close_action.triggered.connect(self.close_program)
        about_action = help_menu.addAction('О программе')
        about_action.triggered.connect(self.show_about)
        system_name = QLabel(
            f'Операционная система: {QSysInfo.prettyProductName()}',
            self.central_widget)
        system_name.setMaximumHeight(self.central_widget.height() * 0.7)
        system_name.setAlignment(Qt.AlignRight)
        self.layout.addWidget(system_name)
        self.layout.addWidget(self.modules_cb)
        bw_layout = QHBoxLayout(self.buttons_widget)
        edit_button = QPushButton('Редактировать модуль')
        edit_button.clicked.connect(self.edit_module)
        bw_layout.addWidget(edit_button)
        delete_button = QPushButton('Удалить модуль')
        delete_button.clicked.connect(self.delete_module)
        bw_layout.addWidget(delete_button)
        self.layout.addWidget(self.buttons_widget)
        self.layout.addWidget(self.scroll)
        self.setCentralWidget(self.central_widget)
        self.load_modules()
        if self.scroll.widget() is None:
            self.modules_cb.setVisible(False)
            self.buttons_widget.setVisible(False)
        self.scroll.setAlignment(Qt.AlignCenter)

    # Слот, обрабатывающий запрос пользователя на создание нового модуля (нажатие соответствующей кнопки)
    @Slot()
    def create_module(self):
        cmw = CreateModuleWidget(self)
        cmw.module_created.connect(self.add_created_module)
        cmw.show()

    @Slot(str)
    def add_created_module(self, module_full_name):
        if module_full_name != '':
            idx = module_full_name.rfind('/')
            if idx != -1:
                module_short_name = module_full_name[idx + 1:]
                self.check_module(module_full_name, module_short_name)

    # Слот, обрабатывающий событие изменения модуля
    @Slot(int)
    def update_edited_module(self, idx):
        self.show_module(self.modules[idx])

    # Слот, обрабатывающий запрос пользователя на изменение модуля (нажатие соответствующей кнопки)
    @Slot()
    def edit_module(self):
        password, ok = QInputDialog().getText(
            self, 'Ввод пароля', 'Введите пароль для редактирования модуля:',
            QLineEdit.Password)
        if ok:
            module = self.modules[self.modules_cb.currentIndex()]
            try:
                with open(module['full_name'], 'rb') as module_file:
                    crypto_type = module_file.read(3)
                    password_hash = module_file.read(md5().digest_size)
                    if password_hash != md5(password.encode('utf-8')).digest():
                        raise RuntimeError('Введён неправильный пароль.')
                    if crypto_type == b'aes':
                        content = aes_decrypt(module_file.read())
                    elif crypto_type == b'xor':
                        content = xor_str(module_file.read())
                    else:
                        raise RuntimeError(
                            'Неизвестный тип шифрования файла модуля.')
                emw = EditModuleWidget(module, crypto_type,
                                       password_hash, content,
                                       self.modules_cb.currentIndex(), self)
                emw.edited.connect(self.update_edited_module)
                emw.show()
            except IOError:
                mb = QMessageBox(self)
                mb.setWindowTitle('Ошибка')
                mb.setText('При открытии файла модуля возникла ошибка.')
                mb.show()
            except (SyntaxError, RuntimeError) as error:
                mb = QMessageBox(self)
                mb.setWindowTitle('Ошибка')
                mb.setText(str(error))
                mb.show()

    # Слот, обрабатывающий запрос пользователя на удаление модуля (нажатие соответствующей кнопки)
    @Slot()
    def delete_module(self):
        try:
            with open(argv[0].replace('main.py', 'data'), 'rb') as file:
                data = file.read()
            data_list = data.decode('utf-8').split('\n')[:-1]
            for i in range(0, len(data_list)):
                if data_list[i].find(self.modules[
                        self.modules_cb.currentIndex()]['full_name']) > -1:
                    data_list[i] = ''.encode('utf-8')
                else:
                    data_list[i] = (data_list[i] + '\n').encode('utf-8')
            with open(argv[0].replace('main.py', 'data'), 'wb') as file:
                file.writelines(data_list)
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText('Не удалось удалить данные о модуле.')
            mb.show()
        del self.modules[self.modules_cb.currentIndex()]
        self.modules_cb.removeItem(self.modules_cb.currentIndex())

    # Слот, обрабатывающий изменение выбранного модуля
    @Slot(int)
    def set_module(self, index):
        if index > -1:
            self.show_module(self.modules[index])
        else:
            self.scroll.widget().setParent(None)
            self.modules_cb.setVisible(False)
            self.buttons_widget.setVisible(False)

    # Слот, обрабатывающий запрос пользователя на показ информации о программе (нажатие соответствующей кнопки)
    @Slot()
    def show_about(self):
        mb = QMessageBox(self)
        mb.setWindowTitle('О программе')
        mb.setText(
            'Данная программа предназначена для создания, редактирования и выполнения модулей, '
            + 'взаимодействующих с операционной системой.')
        mb.show()

    # Слот, обрабатывающий запрос пользователя на закрытие программы (выбора соответствующего пункта меню)
    @Slot()
    def close_program(self):
        self.close()

    # Слот, обрабатывающий запрос пользователя на добавление существующего модуля (выбора соответствующего пункта меню)
    @Slot()
    def add_module(self):
        module_full_name = QFileDialog.getOpenFileName(self, 'Выберите модуль',
                                                       QDir.homePath(),
                                                       '*.module')[0]
        if module_full_name != '':
            idx = module_full_name.rfind('/')
            if idx != -1:
                module_short_name = module_full_name[idx + 1:]
                self.check_module(module_full_name, module_short_name)

    # Метод для проверки модуля на корректность перед добавлением
    def check_module(self, module_full_name, module_short_name):
        if len(self.modules) == 0:
            self.modules.append({
                'full_name': module_full_name,
                'short_name': module_short_name
            })
            self.modules_cb.setVisible(True)
            self.buttons_widget.setVisible(True)
            self.modules_cb.addItem(module_short_name)
            mb = QMessageBox(self)
            mb.setWindowTitle('Успешно')
            mb.setText('Модуль успешно добавлен.')
            mb.show()
            self.save_module_data(module_full_name, module_short_name)
        else:
            for m in self.modules:
                if m['full_name'] == module_full_name:
                    mb = QMessageBox(self)
                    mb.setWindowTitle('Ошибка')
                    mb.setText(f"Модуль '{module_short_name}' уже добавлен.")
                    mb.show()
                    return
            self.modules.append({
                'full_name': module_full_name,
                'short_name': module_short_name
            })
            self.modules_cb.addItem(module_short_name)
            mb = QMessageBox(self)
            mb.setWindowTitle('Успешно')
            mb.setText('Модуль успешно добавлен.')
            mb.show()
            self.save_module_data(module_full_name, module_short_name)

    # Метод сохранения данных о добавленных модулях
    def save_module_data(self, module_full_name, module_short_name):
        try:
            with open(argv[0].replace('main.py', 'data'), 'ab') as file:
                file.write((module_full_name + '|').encode('utf-8'))
                file.write((module_short_name + '\n').encode('utf-8'))
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText('Не удалось сохранить информацию о добавленном модуле.')
            mb.show()

    # Метод отображения модуля
    def show_module(self, module):
        try:
            with open(module['full_name'], 'rb') as module_file:
                crypto_type = module_file.read(3)
                password_hash = module_file.read(md5().digest_size)
                if crypto_type == b'aes':
                    content = aes_decrypt(module_file.read())
                elif crypto_type == b'xor':
                    content = xor_str(module_file.read())
                else:
                    raise RuntimeError(
                        'Неизвестный тип шифрования файла модуля.')
            parsed_data = parse(content)
            parsed_data['module_name'] = module['short_name']
            w = self.scroll.widget()
            if w is not None:
                w.setParent(None)
            mc = ModuleContentWidget(parsed_data, self.scroll)
            self.scroll.setWidget(mc)
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText('При открытии файла модуля возникла ошибка.')
            mb.show()
        except (SyntaxError, RuntimeError) as error:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText(str(error))
            mb.show()

    # Метод загрузки данных о добавленных ранее модулях
    def load_modules(self):
        try:
            with open(argv[0].replace('main.py', 'data'), 'rb') as file:
                data = file.read()
                str_data_list = data.decode('utf-8').split('\n')[:-1]
                for m in str_data_list:
                    self.modules.append({
                        'full_name': m.split('|')[0],
                        'short_name': m.split('|')[1]
                    })
                    self.modules_cb.addItem(self.modules[-1]['short_name'])
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText(
                'Не удалось получить информацию о добавленных ранее модулях.')
            mb.show()