Beispiel #1
0
 def down_list(tree: QTreeWidget):
     selected = tree.currentItem()
     if selected:
         index = tree.indexOfTopLevelItem(selected)
         name = selected.text(0)
         desc = selected.text(1)
         data = selected.data(2, 2)
         new = QTreeWidgetItem([name, desc])
         new.setData(2, 2, data)
         tree.takeTopLevelItem(index)
         tree.insertTopLevelItem(index + 1, new)
         tree.setCurrentItem(new)
Beispiel #2
0
class ObjectTreeDialog(QDialog):
    def __init__(self, parent=None, root_object=None):
        super(ObjectTreeDialog, self).__init__(parent)
        self.setWindowTitle("Object Tree")

        layout = QtWidgets.QVBoxLayout()

        # Tree widget for displaying our object hierarchy
        self.tree_widget = QTreeWidget()
        self.tree_widget_columns = [
            "TYPE", "OBJECT NAME", "TEXT", "ICONTEXT", "TITLE", "WINDOW_TITLE",
            "CLASSES", "POINTER_ADDRESS", "GEOMETRY"
        ]
        self.tree_widget.setColumnCount(len(self.tree_widget_columns))
        self.tree_widget.setHeaderLabels(self.tree_widget_columns)

        # Only show our type and object name columns.  The others we only use to store data so that
        # we can use the built-in QTreeWidget.findItems to query.
        for column_name in self.tree_widget_columns:
            if column_name == "TYPE" or column_name == "OBJECT NAME":
                continue

            column_index = self.tree_widget_columns.index(column_name)
            self.tree_widget.setColumnHidden(column_index, True)

        header = self.tree_widget.header()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)

        # Populate our object tree widget
        # If a root object wasn't specified, then use the Editor main window
        if not root_object:
            params = azlmbr.qt.QtForPythonRequestBus(
                azlmbr.bus.Broadcast, "GetQtBootstrapParameters")
            editor_id = QtWidgets.QWidget.find(params.mainWindowId)
            editor_main_window = wrapInstance(int(getCppPointer(editor_id)[0]),
                                              QtWidgets.QMainWindow)
            root_object = editor_main_window
        self.build_tree(root_object, self.tree_widget)

        # Listen for when the tree widget selection changes so we can update
        # selected item properties
        self.tree_widget.itemSelectionChanged.connect(
            self.on_tree_widget_selection_changed)

        # Split our tree widget with a properties view for showing more information about
        # a selected item. We also use a stacked layout for the properties view so that
        # when nothing has been selected yet, we can show a message informing the user
        # that something needs to be selected.
        splitter = QSplitter()
        splitter.addWidget(self.tree_widget)
        self.widget_properties = QWidget(self)
        self.stacked_layout = QtWidgets.QStackedLayout()
        self.widget_info = QWidget()
        form_layout = QtWidgets.QFormLayout()
        self.name_value = QLineEdit("")
        self.name_value.setReadOnly(True)
        self.type_value = QLabel("")
        self.geometry_value = QLabel("")
        self.text_value = QLabel("")
        self.icon_text_value = QLabel("")
        self.title_value = QLabel("")
        self.window_title_value = QLabel("")
        self.classes_value = QLabel("")
        form_layout.addRow("Name:", self.name_value)
        form_layout.addRow("Type:", self.type_value)
        form_layout.addRow("Geometry:", self.geometry_value)
        form_layout.addRow("Text:", self.text_value)
        form_layout.addRow("Icon Text:", self.icon_text_value)
        form_layout.addRow("Title:", self.title_value)
        form_layout.addRow("Window Title:", self.window_title_value)
        form_layout.addRow("Classes:", self.classes_value)
        self.widget_info.setLayout(form_layout)

        self.widget_properties.setLayout(self.stacked_layout)
        self.stacked_layout.addWidget(
            QLabel("Select an object to view its properties"))
        self.stacked_layout.addWidget(self.widget_info)
        splitter.addWidget(self.widget_properties)

        # Give our splitter stretch factor of 1 so it will expand to take more room over
        # the footer
        layout.addWidget(splitter, 1)

        # Create our popup widget for showing information when hovering over widgets
        self.hovered_widget = None
        self.inspect_mode = False
        self.inspect_popup = InspectPopup()
        self.inspect_popup.resize(100, 50)
        self.inspect_popup.hide()

        # Add a footer with a button to switch to widget inspect mode
        self.footer = QWidget()
        footer_layout = QtWidgets.QHBoxLayout()
        self.inspect_button = QPushButton("Pick widget to inspect")
        self.inspect_button.clicked.connect(self.on_inspect_clicked)
        footer_layout.addStretch(1)
        footer_layout.addWidget(self.inspect_button)
        self.footer.setLayout(footer_layout)
        layout.addWidget(self.footer)

        self.setLayout(layout)

        # Delete ourselves when the dialog is closed, so that we don't stay living in the background
        # since we install an event filter on the application
        self.setAttribute(Qt.WA_DeleteOnClose, True)

        # Listen to events at the application level so we can know when the mouse is moving
        app = QtWidgets.QApplication.instance()
        app.installEventFilter(self)

    def eventFilter(self, obj, event):
        # Look for mouse movement events so we can see what widget the mouse is hovered over
        event_type = event.type()
        if event_type == QEvent.MouseMove:
            global_pos = event.globalPos()

            # Make our popup follow the mouse, but we need to offset it by 1, 1 otherwise
            # the QApplication.widgetAt will always return our popup instead of the Editor
            # widget since it is on top
            self.inspect_popup.move(global_pos + QtCore.QPoint(1, 1))

            # Find out which widget is under our current mouse position
            hovered_widget = QtWidgets.QApplication.widgetAt(global_pos)
            if self.hovered_widget:
                # Bail out, this is the same widget we are already hovered on
                if self.hovered_widget is hovered_widget:
                    return False

            # Update our hovered widget and label
            self.hovered_widget = hovered_widget
            self.update_hovered_widget_popup()
        elif event_type == QEvent.KeyRelease:
            if event.key() == Qt.Key_Escape:
                # Cancel the inspect mode if the Escape key is pressed
                # We don't need to actually hide the inspect popup here because
                # it will be hidden already by the Escape action
                self.inspect_mode = False
        elif event_type == QEvent.MouseButtonPress or event_type == QEvent.MouseButtonRelease:
            # Trigger inspecting the currently hovered widget when the left mouse button is clicked
            # Don't continue processing this event
            if self.inspect_mode and event.button() == Qt.LeftButton:
                # Only trigger the inspect on the click release, but we want to also eat the press
                # event so that the widget we clicked on isn't stuck in a weird state (e.g. thinks its being dragged)
                # Also hide the inspect popup since it won't be hidden automatically by the mouse click since we are
                # consuming the event
                if event_type == event_type == QEvent.MouseButtonRelease:
                    self.inspect_popup.hide()
                    self.inspect_widget()
                return True

        # Pass every event through
        return False

    def build_tree(self, obj, parent_tree):
        if len(obj.children()) == 0:
            return
        for child in obj.children():
            object_type = type(child).__name__
            object_name = child.objectName()
            text = icon_text = title = window_title = geometry_str = classes = "(N/A)"
            if isinstance(child, QtGui.QWindow):
                title = child.title()
            if isinstance(child, QAction):
                text = child.text()
                icon_text = child.iconText()
            if isinstance(child, QWidget):
                window_title = child.windowTitle()
                if not (child.property("class") == ""):
                    classes = child.property("class")
            if isinstance(child, QAbstractButton):
                text = child.text()

            # Keep track of the pointer address for this object so we can search for it later
            pointer_address = str(int(getCppPointer(child)[0]))

            # Some objects might not have a geometry (e.g. actions, generic qobjects)
            if hasattr(child, 'geometry'):
                geometry_rect = child.geometry()
                geometry_str = "x: {x}, y: {y}, width: {width}, height: {height}".format(
                    x=geometry_rect.x(),
                    y=geometry_rect.y(),
                    width=geometry_rect.width(),
                    height=geometry_rect.height())

            child_tree = QTreeWidgetItem([
                object_type, object_name, text, icon_text, title, window_title,
                classes, pointer_address, geometry_str
            ])
            if isinstance(parent_tree, QTreeWidget):
                parent_tree.addTopLevelItem(child_tree)
            else:
                parent_tree.addChild(child_tree)
            self.build_tree(child, child_tree)

    def update_hovered_widget_popup(self):
        if self.inspect_mode and self.hovered_widget:
            if not self.inspect_popup.isVisible():
                self.inspect_popup.show()

            self.inspect_popup.update_widget(self.hovered_widget)
        else:
            self.inspect_popup.hide()

    def on_inspect_clicked(self):
        self.inspect_mode = True
        self.update_hovered_widget_popup()

    def on_tree_widget_selection_changed(self):
        selected_items = self.tree_widget.selectedItems()

        # If nothing is selected, then switch the stacked layout back to 0
        # to show the message
        if not selected_items:
            self.stacked_layout.setCurrentIndex(0)
            return

        # Update the selected widget properties and switch to the 1 index in
        # the stacked layout so that all the rows will be visible
        item = selected_items[0]
        self.name_value.setText(
            item.text(self.tree_widget_columns.index("OBJECT NAME")))
        self.type_value.setText(
            item.text(self.tree_widget_columns.index("TYPE")))
        self.geometry_value.setText(
            item.text(self.tree_widget_columns.index("GEOMETRY")))
        self.text_value.setText(
            item.text(self.tree_widget_columns.index("TEXT")))
        self.icon_text_value.setText(
            item.text(self.tree_widget_columns.index("ICONTEXT")))
        self.title_value.setText(
            item.text(self.tree_widget_columns.index("TITLE")))
        self.window_title_value.setText(
            item.text(self.tree_widget_columns.index("WINDOW_TITLE")))
        self.classes_value.setText(
            item.text(self.tree_widget_columns.index("CLASSES")))
        self.stacked_layout.setCurrentIndex(1)

    def inspect_widget(self):
        self.inspect_mode = False

        # Find the tree widget item that matches our hovered widget, and then set it as the current item
        # so that the tree widget will scroll to it, expand it, and select it
        widget_pointer_address = str(int(
            getCppPointer(self.hovered_widget)[0]))
        pointer_address_column = self.tree_widget_columns.index(
            "POINTER_ADDRESS")
        items = self.tree_widget.findItems(
            widget_pointer_address, Qt.MatchFixedString | Qt.MatchRecursive,
            pointer_address_column)
        if items:
            item = items[0]
            self.tree_widget.clearSelection()
            self.tree_widget.setCurrentItem(item)
        else:
            print("Unable to find widget")
Beispiel #3
0
class TraceWindow(QMainWindow):
    def __init__(self, qmp):

        QMainWindow.__init__(self)

        self.qmp = qmp

        os.system('rm /tmp/errors.log 2>/dev/null')

        self.trace_events = self.qmp.hmp_command('info trace-events')
        self.qmp.hmp_command('logfile /tmp/errors.log')

        self.trace_events = sorted(
            self.trace_events['return'].split('\r\n'))[1:]
        self.activated = []

        self.length = 100

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.disp_output)
        self.timer.start(100)

        self.init_ui()

    def init_ui(self):

        self.setWindowTitle('Trace Event Window')
        self.setGeometry(100, 100, 800, 600)

        bar = self.menuBar()

        file_ = bar.addMenu('File')
        export_log = QAction('Save to File',
                             self,
                             triggered=lambda: self.save_log())

        options = bar.addMenu('Options')
        auto_refresh = QAction(
            'Auto Refresh',
            self,
            checkable=True,
            triggered=lambda: self.timer.start(100)
            if auto_refresh.isChecked() else self.timer.stop())
        auto_refresh.setChecked(True)

        options.addAction(auto_refresh)
        file_.addAction(export_log)

        vgrid = QVBoxLayout()
        grid = QHBoxLayout()

        self.tree = QTreeWidget()
        self.tree.setHeaderLabels(['Name'])

        self.top = []
        self.lst = []

        for n, event in enumerate(self.trace_events):
            word = event.split('_')[0]
            if word not in self.top:
                self.top.append(word)
                item = QTreeWidgetItem(self.tree)
                self.lst.append(item)
                item.setText(0, word)
            subitem = QTreeWidgetItem(item)
            subitem.setText(0, '    ' + event.split(' : ')[0])
            # subitem.setCheckState(0, Qt.Unchecked)
            cbox = QCheckBox()
            cbox.stateChanged.connect(lambda state, text=subitem.text(0): self.
                                      handle_checked(state, text))
            self.tree.setItemWidget(subitem, 0, cbox)

        # self.tree.setColumnWidth(0, 25)

        self.tracelist = QLabel()
        self.disp_output()

        self.traceview = QScrollArea()
        self.traceview.setWidget(self.tracelist)
        self.traceview.setWidgetResizable(True)

        search = QHBoxLayout()

        self.search_bar = QLineEdit(self)

        self.completer = QCompleter(self.top, self)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)

        self.search_bar.setCompleter(self.completer)

        search_button = QPushButton('Search')
        search_button.clicked.connect(lambda: self.tree.setCurrentItem(
            self.lst[self.top.index(self.search_bar.text())]))

        expand = QPushButton('▼')
        expand.setFixedSize(QSize(25, 25))
        expand.clicked.connect(lambda: self.tree.expandAll())

        collapse = QPushButton('▲')
        collapse.setFixedSize(QSize(25, 25))
        collapse.clicked.connect(lambda: self.tree.collapseAll())

        self.search_bar.returnPressed.connect(lambda: search_button.click())

        search.addWidget(self.search_bar)
        search.addWidget(search_button)
        search.addWidget(expand)
        search.addWidget(collapse)

        self.digest = QLabel()

        vgrid.addLayout(search)
        vgrid.addWidget(self.tree)

        vgridwid = QWidget()
        vgridwid.setLayout(vgrid)

        split = QSplitter(Qt.Horizontal)

        split.addWidget(vgridwid)
        split.addWidget(self.traceview)

        split.setStretchFactor(1, 1)

        # grid.addLayout(vgrid)
        grid.addWidget(split)
        # grid.addWidget(self.tracelist)

        self.disp_output()

        center = QWidget()
        center.setLayout(grid)
        self.setCentralWidget(center)
        self.show()

    def disp_output(self):

        self.shorten_file()

        with open('/tmp/errors.log', 'r') as errors:

            self.digest = []
            lines = 0

            for line in errors:
                if re.match(r"\d+@\d+\.\d+:.*", line):
                    self.digest.append(line)
                    lines += 1

            if not self.digest:
                self.digest = ['<font color="grey">Empty...</font>']

            self.digest = ''.join(self.digest[-self.length:])

            self.tracelist.setText(self.digest)
            self.tracelist.setFont(QFont('Monospace', 10))
            self.tracelist.setTextInteractionFlags(Qt.TextSelectableByMouse)

    def shorten_file(self):

        with open('/tmp/errors.log', 'r+') as tracefile:

            content = ''.join(tracefile.readlines()[-(self.length * 3):])

            tracefile.seek(0)
            tracefile.truncate()

            tracefile.write(content)

    def save_log(self):

        name = QFileDialog.getSaveFileName(self, 'Save File', '',
                                           'Text files (*.txt)')

        log_file = open(name[0], 'w')
        log_file.write(self.digest)
        log_file.close()

    def handle_checked(self, state, text):

        if state:
            self.qmp.hmp_command('trace-event %s on' % text.strip())
            self.activated.append(text)
        else:
            self.qmp.hmp_command('trace-event %s off' % text.strip())
            self.activated.remove(text)

    def closeEvent(self, event):

        self.timer.stop()

        for e in self.activated:
            self.qmp.hmp_command('trace-event %s off' % e.strip())

        os.system('rm /tmp/errors.log')

        event.accept()