Пример #1
0
class MemDumpWindow(QWidget):

    kill_signal = Signal(bool)

    def __init__(self, qmp, base=0, max=constants['block_size']):
        super().__init__()

        self.flag = False
        self.threshold = 2048 # number of resident bytes

        self.qmp = qmp
        self.init_ui()

        self.baseAddress = base
        self.maxAddress = self.baseAddress

        self.delta = 4 # adds a small buffer area for scrolling action to happen

        self.sem = QSemaphore(1) # semaphore for controlling access to the enabling / disabling of the scroll listening
        self.pos = self.chr_display.verticalScrollBar().value()

        self.highlight_sem = QSemaphore(1) # semaphore for controlling access to highlighting state
        self.is_highlighted = False
        self.highlight_addr = 0

        self.endian = Endian.little
        self.endian_sem = QSemaphore(1)

        self.hash = randint(0, 0xfffffffffffffff)

        icon = QIcon('package/icons/nasa.png')
        self.setWindowIcon(icon)

        self.max_size = 0xfffffffffffffff

        self.qmp.pmem.connect(self.update_text)
        self.grab_data(val=self.baseAddress, size=min(max, constants['block_size'] + base)-self.baseAddress)

        self.t = MyThread(self)
        self.t.timing_signal.connect(lambda:self.grab_data(val=self.baseAddress, size=self.maxAddress-self.baseAddress, grouping=self.grouping.currentText(), refresh=True))
        self.qmp.stateChanged.connect(self.t.halt)
        self.t.running = self.qmp.running
        self.t.start()

        self.show()
        

    def init_ui(self):   
        self.hbox = QHBoxLayout() # holds widgets for refresh button, desired address, and size to grab
        self.vbox = QVBoxLayout() # main container
        self.lower_hbox = QSplitter() # holds memory views
        self.lower_container = QHBoxLayout() # holds  lower_hbox and the endian_vbox
        self.endian_vbox = QVBoxLayout()

        self.hbox.addWidget(QLabel('Address:'))
        self.address = QLineEdit()
        self.hbox.addWidget(self.address)

        self.hbox.addWidget(QLabel('Size:'))
        self.size = QLineEdit()
        self.hbox.addWidget(self.size)

        self.hbox.addWidget(QLabel('Grouping:'))
        self.grouping = QComboBox()
        self.grouping.addItems(['1','2','4','8'])
        self.hbox.addWidget(self.grouping)

        self.search = QPushButton('Search')
        self.search.clicked.connect(lambda:self.find(self.address.text(), constants['block_size']))
        self.hbox.addWidget(self.search)

        self.refresh = QPushButton('Refresh')
        self.refresh.clicked.connect(lambda:self.grab_data(val=self.address.text(), size=self.size.text(), grouping=self.grouping.currentText(), refresh=True))
        self.hbox.addWidget(self.refresh)

        self.save = QPushButton('Save')
        self.save.clicked.connect(lambda: self.save_to_file())
        self.disable_save(self.qmp.running)
        self.qmp.stateChanged.connect(self.disable_save)
        self.hbox.addWidget(self.save)

        self.auto_refresh = QCheckBox('Auto Refresh')
        self.auto_refresh.setCheckState(Qt.CheckState.Checked)
        self.auto_refresh.stateChanged.connect(self.auto_refresh_check)
        self.hbox.addWidget(self.auto_refresh)

        self.vbox.addLayout(self.hbox)
        
        # textbox for addresses
        self.addresses = QTextEdit()
        self.addresses.setReadOnly(True)
        self.addresses.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.addresses.setLineWrapMode(QTextEdit.NoWrap)
        self.addresses.setCurrentFont(QFont('Courier New'))
        self.addresses.setGeometry(0,0,150,500)
        self.lower_hbox.addWidget(self.addresses)

        # textbox for hex display of memory
        self.mem_display = QTextEdit()
        self.mem_display.setReadOnly(True)
        self.mem_display.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.mem_display.setLineWrapMode(QTextEdit.NoWrap)
        self.mem_display.setCurrentFont(QFont('Courier New'))
        self.mem_display.setGeometry(0,0,600,500)
        self.lower_hbox.addWidget(self.mem_display)
        
        # textbox for char display of memory
        self.chr_display = QTextEdit()
        self.chr_display.setReadOnly(True)
        self.chr_display.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.chr_display.setLineWrapMode(QTextEdit.NoWrap)
        self.chr_display.setCurrentFont(QFont('Courier New'))
        self.mem_display.setGeometry(0,0,400,500)
        self.lower_hbox.addWidget(self.chr_display)

        self.lower_container.addWidget(self.lower_hbox)

        self.mem_display.verticalScrollBar().valueChanged.connect(self.addresses.verticalScrollBar().setValue) #synchronizes addresses's scroll bar to mem_display's 
        self.addresses.verticalScrollBar().valueChanged.connect(self.mem_display.verticalScrollBar().setValue) #synchronizes mem_display's scroll to addresses's, allowing for searching for addresses to scrol mem_display to the desired point 
        self.chr_display.verticalScrollBar().valueChanged.connect(self.mem_display.verticalScrollBar().setValue)  
        self.mem_display.verticalScrollBar().valueChanged.connect(self.chr_display.verticalScrollBar().setValue)  

        self.chr_display.verticalScrollBar().valueChanged.connect(self.handle_scroll) # allows us to do scrolling

        # setting up endiannes selection buttons
        self.little = QRadioButton("Little Endian")
        self.little.click()
        self.little.clicked.connect(lambda:self.change_endian(Endian.little))
        self.big = QRadioButton("Big Endian")
        self.big.clicked.connect(lambda:self.change_endian(Endian.big))
        self.endian_vbox.addWidget(self.little)
        self.endian_vbox.addWidget(self.big)
        self.endian_vbox.addSpacing(400)

        self.lower_container.addLayout(self.endian_vbox)

        self.vbox.addLayout(self.lower_container)
        self.vbox.setSpacing(10)
        self.setLayout(self.vbox)
        self.setWindowTitle("Memory Dump")
        self.setGeometry(100, 100, 1550, 500)

    def disable_save(self, val):
        self.save.setEnabled(not val)

    def save_to_file(self):
        try:
            filename = QFileDialog().getSaveFileName(self, 'Save', '.', options=QFileDialog.DontUseNativeDialog)
        except Exception as e:
            return

        if filename[0] == '':
            return

        args = {
            'val': self.baseAddress,
            'size': self.maxAddress - self.baseAddress,
            'filename': filename[0]
        }

        self.qmp.command('pmemsave', args=args)
        



    def auto_refresh_check(self, value):
        if self.auto_refresh.checkState() == Qt.CheckState.Checked and not self.t.isRunning():
            self.t = MyThread(self)
            self.t.timing_signal.connect(lambda:self.grab_data(val=self.baseAddress, size=self.maxAddress-self.baseAddress, grouping=self.grouping.currentText(), refresh=True))
            self.t.start()
        elif self.auto_refresh.checkState() == Qt.CheckState.Unchecked:
            self.kill_signal.emit(True)


    def closeEvent(self, event):
        self.kill_signal.emit(True)
        self.qmp.pmem.disconnect(self.update_text)
        while True:
            if self.sem.tryAcquire(1, 1):
                break
            self.sem.release(10)
        event.accept()


    def update_text(self, value):
        if not value or value['hash'] != self.hash: # semaphore must be held before entering this function
            return
        byte = value['vals']
        if self.refresh:
            self.clear_highlight()
            self.addresses.clear()  # clearing to refresh data, other regions will be refilled through scrolling
            self.mem_display.clear()
            self.chr_display.clear()
            
        s = [''] * ceil((len(byte) / (16))) # hex representation of memory
        addresses =  '' # addresses
        count = self.baseAddress # keeps track of each 16 addresses
        if self.pos == self.max:  # scrolling down
            count = self.maxAddress 

        self.maxAddress = max(count + (len(byte)), self.maxAddress)
 

        first = True
        chars = [''] * len(s) # char represenation of memory
        index = 0
        self.endian_sem.acquire()
        nums = ''
        for tup in byte:
            b = tup['val']
            if count % 16 == 0:
                if first:
                    addresses += f'0x{count:08x}' 
                    first = False
                else:
                    addresses += f'\n0x{count:08x}' 

                    index += 1
            count += 1
             
            if self.endian == Endian.big:
                if tup['ismapped']:
                    nums = f'{b:02x}' + nums
                    chars[index] += f'{char_convert(b):3}'
                else:
                    nums = '**' + nums
                    chars[index] += f'{".":3}'

            elif self.endian == Endian.little:  
                if tup['ismapped']:     
                    nums += f'{b:02x}'      
                    chars[index] = f'{char_convert(b):3}' + chars[index]
                else:
                    nums = '**' + nums
                    chars[index] = f'{".":3}' + chars[index]

            if count % self.group == 0:
                if self.endian == Endian.big:
                    s[index] += nums + ' '
                elif self.endian == Endian.little:
                     s[index] = nums + ' ' + s[index]
                nums = ''
                #print(f'"{s[index]}"')
        self.endian_sem.release()

        s = '\n'.join(s)
        chars = '\n'.join(chars)

        scroll_goto = self.pos

        if self.pos > self.max - self.delta:  # scrolling down
            self.addresses.append(addresses)
            self.mem_display.append(s)
            self.chr_display.append(chars)
            if self.flag:
                self.flag = False
                scroll_goto = (1 - constants['block_size'] / self.threshold) * self.max
            else:
                scroll_goto = self.max

        elif self.pos < self.min + self.delta:    # scrolling  up
            addr_cur = self.addresses.textCursor()
            mem_cur = self.mem_display.textCursor()
            chr_cur = self.chr_display.textCursor()

            addr_cur.setPosition(0)
            mem_cur.setPosition(0)
            chr_cur.setPosition(0)

            self.addresses.setTextCursor(addr_cur)
            self.mem_display.setTextCursor(mem_cur)
            self.chr_display.setTextCursor(chr_cur)

            self.addresses.insertPlainText(addresses + '\n')
            self.mem_display.insertPlainText(s + '\n')
            self.chr_display.insertPlainText(chars + '\n')
            
            if self.flag:
                self.flag = False
                scroll_goto = (constants['block_size'] / self.threshold) * self.max
            else:
                scroll_goto = self.chr_display.verticalScrollBar().maximum() - self.max

        else:
            self.addresses.setPlainText(addresses)
            self.mem_display.setPlainText(s)
            self.chr_display.setPlainText(chars)

        self.highlight_sem.acquire()
        if self.is_highlighted:
            self.highlight_sem.release()
            self.highlight(self.highlight_addr, self.group)
        else:
            self.highlight_sem.release()

        self.chr_display.verticalScrollBar().setValue(scroll_goto)
        self.chr_display.verticalScrollBar().valueChanged.connect(self.handle_scroll)
        self.sem.release()


    def grab_data(self, val=0, size=constants['block_size'], grouping=1, refresh=False):       
        if val == None:
            val = 0
        if size == None:
            size = 1024

        if type(val) == str:
            try:
                val = int(val, 0)
            except Exception:
                val = 0
        
        if type(size) == str:
            try:
                size = int(size, 0)
            except Exception:
                size = constants['block_size']

        if type(grouping) == str:
            try:
                grouping = int(grouping, 0)
            except:
                grouping = 1

        if grouping not in [1, 2, 4, 8]:
            grouping = 1

        if val < 0:
            val = 0
        if val >= self.max_size:
            return
        if size < 0:
            size = constants['block_size']
        if size > self.threshold:
            size = self.threshold
        

        self.sem.acquire()

        val = val - (val % 16)
        if val < self.baseAddress or refresh:
            self.baseAddress = val
        if size % 16 != 0:
            size = size + (16 - (size % 16))
        if val + size > self.max_size:
            size = self.max_size - val
        
        try:
            self.chr_display.verticalScrollBar().valueChanged.disconnect(self.handle_scroll)
        except:
            pass
        self.pos = self.chr_display.verticalScrollBar().value()
        self.max = self.chr_display.verticalScrollBar().maximum()
        self.min = self.chr_display.verticalScrollBar().minimum()
        self.group = grouping

        self.refresh = refresh
        if refresh: 
            self.maxAddress = self.baseAddress
            
            self.highlight_sem.acquire()
            if self.is_highlighted and self.highlight_addr not in range(val, val + size):
                self.is_highlighted = False
                self.pos = 0
            self.highlight_sem.release()

        args = {
                'addr': val,
                'size': size,
                'hash': self.hash,
                'grouping': 1
                }
        self.qmp.command('get-pmem', args=args)


    def find(self, addr, size):
        try:
            addr = int(addr, 0)
        except ValueError as e:
            print(e)
            return
        if self.baseAddress <= addr and addr <= self.maxAddress:
            group = self.grouping.currentText()
            try:
                group = int(group, 0)
                if group not in [1, 2, 4, 8]:
                    group = 1
            except:
                group = 1
            self.highlight(addr, group)
            
        else:
            self.highlight_sem.acquire()
            self.is_highlighted = True
            self.highlight_addr = addr
            self.highlight_sem.release()
            self.grab_data(val=addr, size=size, refresh=True )


    def clear_highlight(self):
        fmt = QTextCharFormat()
        fmt.setFont('Courier New')
        # clearing past highlights
        addr_cur = self.addresses.textCursor()  # getting cursors
        mem_cur = self.mem_display.textCursor()
        chr_cur = self.chr_display.textCursor()

        addr_cur.select(QTextCursor.Document)   # selecting entire document
        mem_cur.select(QTextCursor.Document)
        chr_cur.select(QTextCursor.Document)
        
        addr_cur.setCharFormat(fmt) # adding format
        mem_cur.setCharFormat(fmt)
        chr_cur.setCharFormat(fmt)


    def highlight(self, addr, group):
        self.clear_highlight()
        
        # adding new highlights
        fmt = QTextCharFormat()
        fmt.setBackground(Qt.cyan)
        fmt.setFont('Courier New')

        addr_block = self.addresses.document().findBlockByLineNumber((addr - self.baseAddress) // 16) # gets linenos 
        mem_block = self.mem_display.document().findBlockByLineNumber((addr - self.baseAddress) // 16)
        chr_block = self.chr_display.document().findBlockByLineNumber((addr - self.baseAddress) // 16)

        addr_cur = self.addresses.textCursor()  # getting cursors
        mem_cur = self.mem_display.textCursor()
        chr_cur = self.chr_display.textCursor()


        char_offset = 0
        mem_offset = 0
        self.endian_sem.acquire()
        if self.endian == Endian.big:
            mem_offset = ((addr % 16) // group) * (2 * group + 1)
            char_offset = (addr % 16) * 3
        elif self.endian == Endian.little:
            mem_offset = (((16 / group) - 1 )* (2 * group + 1)) - ((addr % 16) // group) * (2 * group + 1)
            char_offset = (15*3) - ((addr % 16) * 3)
        self.endian_sem.release()
        addr_cur.setPosition(addr_block.position()) # getting positions
        mem_cur.setPosition(mem_block.position() + mem_offset) # gives character offset within 16 byte line
        chr_cur.setPosition(chr_block.position() + char_offset) # sets position of char

        chr_text = self.chr_display.toPlainText()
        if chr_text[chr_cur.position()] == '\\' and chr_cur.position() + 1 < len(chr_text) and chr_text[chr_cur.position() + 1] in ['0', 'n', 't']:
            chr_cur.setPosition(chr_cur.position() + 2, mode=QTextCursor.KeepAnchor) 
        else:
            chr_cur.setPosition(chr_cur.position() + 1, mode=QTextCursor.KeepAnchor) 


        addr_cur.select(QTextCursor.LineUnderCursor)    # selects whole line
        mem_cur.select(QTextCursor.WordUnderCursor)     # selects just one word

        addr_cur.setCharFormat(fmt) # setting format
        mem_cur.setCharFormat(fmt)
        chr_cur.setCharFormat(fmt)

        self.addresses.setTextCursor(addr_cur)
        self.mem_display.setTextCursor(mem_cur)
        self.chr_display.setTextCursor(chr_cur)
        
        self.highlight_sem.acquire()
        self.is_highlighted = True
        self.highlight_addr = addr
        self.highlight_sem.release()


    def handle_scroll(self):
        if self.baseAddress > 0 and self.chr_display.verticalScrollBar().value() < self.chr_display.verticalScrollBar().minimum() + self.delta:
            size = constants['block_size']
            if self.maxAddress - self.baseAddress >= self.threshold:
                self.flag = True
                self.grab_data(val=self.baseAddress - constants['block_size'], size=self.threshold, refresh=True, grouping=self.grouping.currentText())
                return
            if self.baseAddress < size:
                size = self.baseAddress
            self.grab_data(val=self.baseAddress-size, size=size, grouping=self.grouping.currentText())
        elif self.chr_display.verticalScrollBar().value() > self.chr_display.verticalScrollBar().maximum() - self.delta and self.maxAddress <= self.max_size:
            if self.maxAddress - self.baseAddress >= self.threshold:
                self.flag = True
                self.grab_data(val=self.baseAddress + constants['block_size'], size=self.threshold, refresh=True, grouping=self.grouping.currentText())
            else:
                self.grab_data(val=self.maxAddress, grouping=self.grouping.currentText())
      
      
    def change_endian(self, endian):
        self.endian_sem.acquire()
        if endian == Endian.little:
            self.endian = endian
        elif endian == Endian.big:
            self.endian = endian
        self.endian_sem.release()
Пример #2
0
class ManageRelationshipsDialog(AddRelationshipsDialogBase):
    """A dialog to query user's preferences for managing relationships.
    """
    def __init__(self, parent, db_mngr, *db_maps):
        """Init class.

        Args:
            parent (DataStoreForm): data store widget
            db_mngr (SpineDBManager): the manager to do the removal
            db_maps (iter): DiffDatabaseMapping instances
        """
        super().__init__(parent, db_mngr, *db_maps)
        self.setWindowTitle("Manage relationships")
        self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.remove_rows_button.setToolTip(
            "<p>Remove selected relationships.</p>")
        self.remove_rows_button.setIconSize(QSize(24, 24))
        self.db_map = db_maps[0]
        self.relationships = dict()
        layout = self.header_widget.layout()
        self.db_combo_box = QComboBox(self)
        layout.addSpacing(32)
        layout.addWidget(QLabel("Database"))
        layout.addWidget(self.db_combo_box)
        self.splitter = QSplitter(self)
        self.add_button = QToolButton(self)
        self.add_button.setToolTip(
            "<p>Add relationships by combining selected available objects.</p>"
        )
        self.add_button.setIcon(QIcon(":/icons/menu_icons/cubes_plus.svg"))
        self.add_button.setIconSize(QSize(24, 24))
        self.add_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.add_button.setText(">>")
        label_available = QLabel("Available objects")
        label_existing = QLabel("Existing relationships")
        self.layout().addWidget(self.header_widget, 0, 0, 1, 4,
                                Qt.AlignHCenter)
        self.layout().addWidget(label_available, 1, 0)
        self.layout().addWidget(label_existing, 1, 2)
        self.layout().addWidget(self.splitter, 2, 0)
        self.layout().addWidget(self.add_button, 2, 1)
        self.layout().addWidget(self.table_view, 2, 2)
        self.layout().addWidget(self.remove_rows_button, 2, 3)
        self.layout().addWidget(self.button_box, 3, 0, -1, -1)
        self.hidable_widgets = [
            self.add_button,
            label_available,
            label_existing,
            self.table_view,
            self.remove_rows_button,
        ]
        for widget in self.hidable_widgets:
            widget.hide()
        self.existing_items_model = MinimalTableModel(self, lazy=False)
        self.new_items_model = MinimalTableModel(self, lazy=False)
        self.model.sub_models = [
            self.new_items_model, self.existing_items_model
        ]
        self.db_combo_box.addItems([db_map.codename for db_map in db_maps])
        self.reset_relationship_class_combo_box(db_maps[0].codename)
        self.connect_signals()

    def make_model(self):
        return CompoundTableModel(self)

    def splitter_widgets(self):
        return [self.splitter.widget(i) for i in range(self.splitter.count())]

    def connect_signals(self):
        """Connect signals to slots."""
        super().connect_signals()
        self.db_combo_box.currentTextChanged.connect(
            self.reset_relationship_class_combo_box)
        self.add_button.clicked.connect(self.add_relationships)

    @Slot(str)
    def reset_relationship_class_combo_box(self, database):
        self.db_map = self.keyed_db_maps[database]
        self.relationship_class_keys = list(
            self.db_map_rel_cls_lookup[self.db_map])
        self.rel_cls_combo_box.addItems(
            [f"{name}" for name, _ in self.relationship_class_keys])
        self.rel_cls_combo_box.setCurrentIndex(-1)

    @Slot(bool)
    def add_relationships(self, checked=True):
        object_names = [[item.text(0) for item in wg.selectedItems()]
                        for wg in self.splitter_widgets()]
        candidate = list(product(*object_names))
        existing = self.new_items_model._main_data + self.existing_items_model._main_data
        to_add = list(set(candidate) - set(existing))
        count = len(to_add)
        self.new_items_model.insertRows(0, count)
        self.new_items_model._main_data[0:count] = to_add
        self.model.refresh()

    @Slot(int)
    def reset_model(self, index):
        """Setup model according to current relationship class selected in combobox.
        """
        self.class_name, self.object_class_name_list = self.relationship_class_keys[
            index]
        object_class_name_list = self.object_class_name_list.split(",")
        self.model.set_horizontal_header_labels(object_class_name_list)
        self.existing_items_model.set_horizontal_header_labels(
            object_class_name_list)
        self.new_items_model.set_horizontal_header_labels(
            object_class_name_list)
        self.relationships.clear()
        for db_map in self.db_maps:
            relationship_classes = self.db_map_rel_cls_lookup[db_map]
            rel_cls = relationship_classes.get(
                (self.class_name, self.object_class_name_list), None)
            if rel_cls is None:
                continue
            for relationship in self.db_mngr.get_items_by_field(
                    db_map, "relationship", "class_id", rel_cls["id"]):
                key = tuple(relationship["object_name_list"].split(","))
                self.relationships[key] = relationship
        existing_items = list(self.relationships)
        self.existing_items_model.reset_model(existing_items)
        self.model.refresh()
        self.model.modelReset.emit()
        for wg in self.splitter_widgets():
            wg.deleteLater()
        for name in object_class_name_list:
            tree_widget = QTreeWidget(self)
            tree_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)
            tree_widget.setColumnCount(1)
            tree_widget.setIndentation(0)
            header_item = QTreeWidgetItem([name])
            header_item.setTextAlignment(0, Qt.AlignHCenter)
            tree_widget.setHeaderItem(header_item)
            objects = self.db_mngr.get_items_by_field(self.db_map, "object",
                                                      "class_name", name)
            items = [QTreeWidgetItem([obj["name"]]) for obj in objects]
            tree_widget.addTopLevelItems(items)
            tree_widget.resizeColumnToContents(0)
            self.splitter.addWidget(tree_widget)
        sizes = [wg.columnWidth(0) for wg in self.splitter_widgets()]
        self.splitter.setSizes(sizes)
        for widget in self.hidable_widgets:
            widget.show()

    def resize_window_to_columns(self, height=None):
        table_view_width = (self.table_view.frameWidth() * 2 +
                            self.table_view.verticalHeader().width() +
                            self.table_view.horizontalHeader().length())
        self.table_view.setMinimumWidth(table_view_width)
        self.table_view.setMinimumHeight(
            self.table_view.verticalHeader().defaultSectionSize() * 16)
        margins = self.layout().contentsMargins()
        if height is None:
            height = self.sizeHint().height()
        self.resize(
            margins.left() + margins.right() + table_view_width +
            self.add_button.width() + self.splitter.width(),
            height,
        )

    @Slot()
    def accept(self):
        """Collect info from dialog and try to add items."""
        keys_to_remove = set(self.relationships) - set(
            self.existing_items_model._main_data)
        to_remove = [self.relationships[key] for key in keys_to_remove]
        self.db_mngr.remove_items({self.db_map: {"relationship": to_remove}})
        to_add = [[self.class_name, object_name_list]
                  for object_name_list in self.new_items_model._main_data]
        self.db_mngr.import_data({self.db_map: {
            "relationships": to_add
        }},
                                 command_text="Add relationships")
        super().accept()
Пример #3
0
def main():
    app = QApplication()
    window = QMainWindow()
    window.setMinimumSize(600, 600)

    tree = DirTreeWidget(window)
    tree.setRootDirectoryPaths(['C:\\', 'D:\\'])
    # tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
    # tree.itemSelectionChanged.connect(lambda x, y: print(tree.selectedItems()))
    # tree.itemDoubleClicked.connect(lambda idx: print(f'doubleclick: {idx}'))
    tree.setContextMenuPolicy(Qt.CustomContextMenu)

    def _ctx_menu(point):
        def _open_directory(path):
            subprocess.call(f'explorer "{path()}"', shell=True)

        menu_items = {
            u'フォルダを開く': lambda path: _open_directory(path),
        }

        menu = QMenu()
        for label in menu_items.keys():
            menu.addAction(label)

        executed_action = menu.exec_(tree.mapToGlobal(point))
        action = menu_items[executed_action.text()]

        item = tree.currentItem()
        action(item.path)

    tree.customContextMenuRequested.connect(_ctx_menu)

    files = FileListWidget(window)
    files.setViewMode(QFileListViewMode.ListMode)
    files.setSelectionMode(QAbstractItemView.ExtendedSelection)
    # files.itemSelectionChanged.connect(lambda x, y: print(tree.selectedItems()))
    # files.itemClicked.connect(lambda idx: print(f'click: {idx}'))
    # files.itemDoubleClicked.connect(lambda idx: print(f'doubleclick: {idx}'))

    iconLoader = QFileIconLoader(None)

    def _updateFiles(index):
        item = tree.itemFromIndex(index)

        filePaths = list(item.path().glob('*'))
        iconLoader.reset(filePaths)

        def _set_icon(result):
            FileListModel.icons[result.filePath] = result.icon
            files.model().refresh()

        iconLoader.loaded.connect(_set_icon)
        # iconLoader.completed.connect(print)
        iconLoader.loadAsync()

        files.setDirectoryPath(tree.itemFromIndex(index).path())

    tree.itemSelectionChanged.connect(
        lambda x, y: _updateFiles(tree.selectedIndexes()[0]))

    def _changeDir(index):
        item = files.itemFromIndex(index)
        print(item.path())

    files.itemDoubleClicked.connect(_changeDir)

    splitter = QSplitter()
    splitter.addWidget(tree)
    splitter.addWidget(files)

    window.setCentralWidget(splitter)
    window.show()
    app.exec_()
Пример #4
0
 def testGetRange(self):
     splitter = QSplitter()
     _min, _max = splitter.getRange(0)
     self.assert_(isinstance(_min, int))
     self.assert_(isinstance(_max, int))
Пример #5
0
    def __init__(self, parent, data):
        if not type(data) == binaryninja.binaryview.BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data

        self.debug_state = binjaplug.get_state(data)
        memory_view = self.debug_state.memory_view
        self.debug_state.ui.debug_view = self

        QWidget.__init__(self, parent)
        self.controls = ControlsWidget.DebugControlsWidget(
            self, "Controls", data, self.debug_state)
        View.__init__(self)

        self.setupView(self)

        self.current_offset = 0

        self.splitter = QSplitter(Qt.Orientation.Horizontal, self)

        frame = ViewFrame.viewFrameForWidget(self)
        self.memory_editor = LinearView(memory_view, frame)
        self.binary_editor = DisassemblyContainer(frame, data, frame)

        self.binary_text = TokenizedTextView(self, memory_view)
        self.is_raw_disassembly = False

        # TODO: Handle these and change views accordingly
        # Currently they are just disabled as the DisassemblyContainer gets confused
        # about where to go and just shows a bad view
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Hex Editor", UIAction())
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Linear Disassembly", UIAction())
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Types View", UIAction())

        self.memory_editor.actionHandler().bindAction("View in Hex Editor",
                                                      UIAction())
        self.memory_editor.actionHandler().bindAction(
            "View in Disassembly Graph", UIAction())
        self.memory_editor.actionHandler().bindAction("View in Types View",
                                                      UIAction())

        small_font = QApplication.font()
        small_font.setPointSize(11)

        bv_layout = QVBoxLayout()
        bv_layout.setSpacing(0)
        bv_layout.setContentsMargins(0, 0, 0, 0)

        bv_label = QLabel("Loaded File")
        bv_label.setFont(small_font)
        bv_layout.addWidget(bv_label)
        bv_layout.addWidget(self.binary_editor)

        self.bv_widget = QWidget()
        self.bv_widget.setLayout(bv_layout)

        disasm_layout = QVBoxLayout()
        disasm_layout.setSpacing(0)
        disasm_layout.setContentsMargins(0, 0, 0, 0)

        disasm_label = QLabel("Raw Disassembly at PC")
        disasm_label.setFont(small_font)
        disasm_layout.addWidget(disasm_label)
        disasm_layout.addWidget(self.binary_text)

        self.disasm_widget = QWidget()
        self.disasm_widget.setLayout(disasm_layout)

        memory_layout = QVBoxLayout()
        memory_layout.setSpacing(0)
        memory_layout.setContentsMargins(0, 0, 0, 0)

        memory_label = QLabel("Debugged Process")
        memory_label.setFont(small_font)
        memory_layout.addWidget(memory_label)
        memory_layout.addWidget(self.memory_editor)

        self.memory_widget = QWidget()
        self.memory_widget.setLayout(memory_layout)

        self.splitter.addWidget(self.bv_widget)
        self.splitter.addWidget(self.memory_widget)

        # Equally sized
        self.splitter.setSizes([0x7fffffff, 0x7fffffff])

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.controls)
        layout.addWidget(self.splitter, 100)
        self.setLayout(layout)

        self.needs_update = True
        self.update_timer = QTimer(self)
        self.update_timer.setInterval(200)
        self.update_timer.setSingleShot(False)
        self.update_timer.timeout.connect(lambda: self.updateTimerEvent())

        self.add_scripting_ref()
Пример #6
0
class MainView(QWidget):
    def __init__(self):
        """
        MainView of the application, contains three Widgets divided by splitters
        from the left:
        GroupView containg all groups info
        FeedView containg all feed
        ArticleBox containg selected article and its content
        """
        super().__init__()
        self.selected_group = None
        self.group_view = GroupView(self)
        self.feed_view = FeedView(self)
        self.article_box = ArticleBox(self)
        self.feed_view.selectionModel().selectionChanged.connect(
            self.set_article)
        self.group_view.itemClicked.connect(
            lambda: self.refresh_feed(self.group_view.selectedItems()[0]))
        self.refresh_groups()
        all_group = self.group_view.groups["All"]
        all_group.setExpanded(True)
        self.refresh_feed(self.group_view.groups["All"])
        self.__split = QSplitter(parent=self)
        self.__split.addWidget(self.group_view)
        self.__split.addWidget(self.feed_view)
        self.__split.addWidget(self.article_box)
        self.__split.setHandleWidth(4)
        self.__main_layout = QHBoxLayout()
        self.__main_layout.addWidget(self.__split)
        self.setLayout(self.__main_layout)

    def get_user_groups(self, update=False):
        """
        Load all user groups from database to gui

        Args:
            update (bool, optional): Enforces selecting group to the first group, Defaults to False.
        """
        group_dict = self.entry["groups"]
        active_exists = False
        popular_name = URLHandler.popular_name
        self.group_view.clear()
        if popular_name in group_dict:
            self.get_single_group(group_dict, popular_name)
            group_dict.pop(popular_name)
        for group in group_dict:
            active_exists = self.get_single_group(group_dict, group)
        ix = self.group_view.model().index(0, 0)
        if not active_exists or update:
            self.group_view.selectionModel().setCurrentIndex(
                ix, QItemSelectionModel.SelectCurrent)
            self.group_view.expand(ix)

    def get_single_group(self, group_dict, group):
        """
        Add single group to group view, returns active groups

        Args:
            group_dict (dict): dictionary of user groups from database
            group (string): group name

        Returns:
            bool: true if selected group is the group that is being added
        """
        active_exists = False
        indexes = group_dict[group]
        urls = []
        if self.selected_group == group:
            active_exists = True
        for index in indexes:
            urls.append(self.entry['urls'][index]["actual_url"])
        self.group_view.add_group(group, urls, indexes)
        return active_exists

    def set_group(self, item, update=False):
        """
        Sets group to provided in item arg
        If update is set to True group will update even if it is not currenlt selected,
        used for refreshing

        Args:
            item (QItem): selected group 
            update (bool, optional): Enforces group update, Defaults to False.
        """
        if (self.selected_group != item.text(0) or update):
            self.feed_view.clear_list()
            self.selected_group = item.text(0)
            art_list = []
            if item.rss_type == "group":
                for url_name in self.group_view.urls:
                    if item.text(0) in url_name:
                        sub_item = self.group_view.urls[url_name]
                        url = self.entry['urls'][sub_item.url_index]
                        art_list.extend(self.get_gui_articles(url))
            elif item.rss_type == "url":
                url = self.entry['urls'][item.url_index]
                art_list.extend(self.get_gui_articles(url))
            art_list = sorted(art_list,
                              key=lambda x: (not x["seen"], x["date"]))
            for article in art_list:
                self.feed_view.append_message(**article)

    def get_gui_articles(self, url):
        """
        Returns all articles from selected url

        Args:
            url (string): url

        Returns:
            list: list of article objects
        """
        site = url["rss_title"]
        art_list = []
        for article in url["articles"]:
            article_bundle = {
                "date": article["pub_date_parsed"],
                "title": article["title"],
                "desc": article["desc"],
                "seen": article["seen"],
                "link": article["link"],
                "site": site,
            }
            art_list.append(article_bundle)
        return art_list

    def set_article(self, current):
        """
        Sets selected article to seen and updates data of articlebox
        to view newest content

        Args:
            current (QItem): item conteining row with selected article
        """
        row = [qmi.row() for qmi in self.feed_view.selectedIndexes()][0]
        item = self.feed_view.model().item(row)
        self.article_box.set_data(**item.article_bundle)
        self.feed_view.set_seen(item, True)

        URLHandler.set_article_seen(item.article_bundle['link'], True)

    def refresh_groups(self, download=False):
        """
        Refreshes groups after adding/removing group or url

        Args:
            download (bool, optional): Option to fetch newset article while refreshing content. Defaults to False.
        """
        dbh = DatabaseHandler()
        self.entry = dbh.get_entry(CredentialsHandler.lastUsername)
        self.get_user_groups(update=True)
        if download:
            self.group_view.menu_refresh_callback(
                self.group_view.selectedItems()[0])

    def refresh_feed(self, item):
        """Refreshes content of FeedView

        Args:
            item (QItem): Item of selected group from groupview
        """
        dbh = DatabaseHandler()
        self.entry = dbh.get_entry(CredentialsHandler.lastUsername)
        self.set_group(item, True)
Пример #7
0
 def getSplitter(self):
     splitter = QSplitter()
     splitter.addWidget(self.editBox)
     return splitter
Пример #8
0
    def initUi(self):
        hbox = QSplitter(Qt.Horizontal)
        vbox = QSplitter(Qt.Vertical)
        hbox.addWidget(self.ato_panel)
        hbox.addWidget(vbox)
        vbox.addWidget(self.liberation_map)
        vbox.addWidget(self.info_panel)

        # Will make the ATO sidebar as small as necessary to fit the content. In
        # practice this means it is sized by the hints in the panel.
        hbox.setSizes([1, 10000000])
        vbox.setSizes([600, 100])

        vbox = QVBoxLayout()
        vbox.setMargin(0)
        vbox.addWidget(QTopPanel(self.game_model))
        vbox.addWidget(hbox)

        central_widget = QWidget()
        central_widget.setLayout(vbox)
        self.setCentralWidget(central_widget)
Пример #9
0
class SSUResolverPanel(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent=parent, f=Qt.Window)
        self.setWindowTitle(self.tr("SSU Resolver"))
        self.distribution_types = [
            (DistributionType.Normal, self.tr("Normal")),
            (DistributionType.Weibull, self.tr("Weibull")),
            (DistributionType.SkewNormal, self.tr("Skew Normal"))
        ]
        self.load_dataset_dialog = LoadDatasetDialog(parent=self)
        self.load_dataset_dialog.dataset_loaded.connect(self.on_dataset_loaded)
        self.classic_setting = ClassicResolverSettingWidget(parent=self)
        self.neural_setting = NNResolverSettingWidget(parent=self)
        self.manual_panel = ManualFittingPanel(parent=self)
        self.manual_panel.manual_fitting_finished.connect(
            self.on_fitting_succeeded)
        self.async_worker = AsyncWorker()
        self.async_worker.background_worker.task_succeeded.connect(
            self.on_fitting_succeeded)
        self.async_worker.background_worker.task_failed.connect(
            self.on_fitting_failed)
        self.normal_msg = QMessageBox(self)
        self.dataset = None
        self.task_table = {}
        self.task_results = {}
        self.failed_task_ids = []
        self.__continuous_flag = False
        self.init_ui()

    def init_ui(self):
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.main_layout = QGridLayout(self)
        # self.main_layout.setContentsMargins(0, 0, 0, 0)
        # control group
        self.control_group = QGroupBox(self.tr("Control"))
        self.control_layout = QGridLayout(self.control_group)
        self.resolver_label = QLabel(self.tr("Resolver"))
        self.resolver_combo_box = QComboBox()
        self.resolver_combo_box.addItems(["classic", "neural"])
        self.control_layout.addWidget(self.resolver_label, 0, 0)
        self.control_layout.addWidget(self.resolver_combo_box, 0, 1)
        self.load_dataset_button = QPushButton(qta.icon("fa.database"),
                                               self.tr("Load Dataset"))
        self.load_dataset_button.clicked.connect(self.on_load_dataset_clicked)
        self.configure_fitting_button = QPushButton(
            qta.icon("fa.gears"), self.tr("Configure Fitting Algorithm"))
        self.configure_fitting_button.clicked.connect(
            self.on_configure_fitting_clicked)
        self.control_layout.addWidget(self.load_dataset_button, 1, 0)
        self.control_layout.addWidget(self.configure_fitting_button, 1, 1)
        self.distribution_label = QLabel(self.tr("Distribution Type"))
        self.distribution_combo_box = QComboBox()
        self.distribution_combo_box.addItems(
            [name for _, name in self.distribution_types])
        self.component_number_label = QLabel(self.tr("N<sub>components</sub>"))
        self.n_components_input = QSpinBox()
        self.n_components_input.setRange(1, 10)
        self.n_components_input.setValue(3)
        self.control_layout.addWidget(self.distribution_label, 2, 0)
        self.control_layout.addWidget(self.distribution_combo_box, 2, 1)
        self.control_layout.addWidget(self.component_number_label, 3, 0)
        self.control_layout.addWidget(self.n_components_input, 3, 1)

        self.n_samples_label = QLabel(self.tr("N<sub>samples</sub>"))
        self.n_samples_display = QLabel(self.tr("Unknown"))
        self.control_layout.addWidget(self.n_samples_label, 4, 0)
        self.control_layout.addWidget(self.n_samples_display, 4, 1)
        self.sample_index_label = QLabel(self.tr("Sample Index"))
        self.sample_index_input = QSpinBox()
        self.sample_index_input.valueChanged.connect(
            self.on_sample_index_changed)
        self.sample_index_input.setEnabled(False)
        self.control_layout.addWidget(self.sample_index_label, 5, 0)
        self.control_layout.addWidget(self.sample_index_input, 5, 1)
        self.sample_name_label = QLabel(self.tr("Sample Name"))
        self.sample_name_display = QLabel(self.tr("Unknown"))
        self.control_layout.addWidget(self.sample_name_label, 6, 0)
        self.control_layout.addWidget(self.sample_name_display, 6, 1)

        self.manual_test_button = QPushButton(qta.icon("fa.sliders"),
                                              self.tr("Manual Test"))
        self.manual_test_button.setEnabled(False)
        self.manual_test_button.clicked.connect(self.on_manual_test_clicked)
        self.load_reference_button = QPushButton(qta.icon("mdi.map-check"),
                                                 self.tr("Load Reference"))
        self.load_reference_button.clicked.connect(
            lambda: self.reference_view.load_dump(mark_ref=True))
        self.control_layout.addWidget(self.manual_test_button, 7, 0)
        self.control_layout.addWidget(self.load_reference_button, 7, 1)

        self.single_test_button = QPushButton(qta.icon("fa.play-circle"),
                                              self.tr("Single Test"))
        self.single_test_button.setEnabled(False)
        self.single_test_button.clicked.connect(self.on_single_test_clicked)
        self.continuous_test_button = QPushButton(
            qta.icon("mdi.playlist-play"), self.tr("Continuous Test"))
        self.continuous_test_button.setEnabled(False)
        self.continuous_test_button.clicked.connect(
            self.on_continuous_test_clicked)
        self.control_layout.addWidget(self.single_test_button, 8, 0)
        self.control_layout.addWidget(self.continuous_test_button, 8, 1)

        self.test_previous_button = QPushButton(
            qta.icon("mdi.skip-previous-circle"), self.tr("Test Previous"))
        self.test_previous_button.setEnabled(False)
        self.test_previous_button.clicked.connect(
            self.on_test_previous_clicked)
        self.test_next_button = QPushButton(qta.icon("mdi.skip-next-circle"),
                                            self.tr("Test Next"))
        self.test_next_button.setEnabled(False)
        self.test_next_button.clicked.connect(self.on_test_next_clicked)
        self.control_layout.addWidget(self.test_previous_button, 9, 0)
        self.control_layout.addWidget(self.test_next_button, 9, 1)

        # chart group
        self.chart_group = QGroupBox(self.tr("Chart"))
        self.chart_layout = QGridLayout(self.chart_group)
        self.result_chart = MixedDistributionChart(show_mode=True,
                                                   toolbar=False)
        self.chart_layout.addWidget(self.result_chart, 0, 0)

        # table group
        self.table_group = QGroupBox(self.tr("Table"))
        self.reference_view = ReferenceResultViewer()
        self.result_view = FittingResultViewer(self.reference_view)
        self.result_view.result_marked.connect(
            lambda result: self.reference_view.add_references([result]))
        self.table_tab = QTabWidget()
        self.table_tab.addTab(self.result_view, qta.icon("fa.cubes"),
                              self.tr("Result"))
        self.table_tab.addTab(self.reference_view, qta.icon("fa5s.key"),
                              self.tr("Reference"))
        self.result_layout = QGridLayout(self.table_group)
        self.result_layout.addWidget(self.table_tab, 0, 0)

        # pack all group
        self.splitter1 = QSplitter(Qt.Orientation.Vertical)
        self.splitter1.addWidget(self.control_group)
        self.splitter1.addWidget(self.chart_group)
        self.splitter2 = QSplitter(Qt.Orientation.Horizontal)
        self.splitter2.addWidget(self.splitter1)
        self.splitter2.addWidget(self.table_group)
        self.main_layout.addWidget(self.splitter2, 0, 0)

    @property
    def distribution_type(self) -> DistributionType:
        distribution_type, _ = self.distribution_types[
            self.distribution_combo_box.currentIndex()]
        return distribution_type

    @property
    def n_components(self) -> int:
        return self.n_components_input.value()

    def show_message(self, title: str, message: str):
        self.normal_msg.setWindowTitle(title)
        self.normal_msg.setText(message)
        self.normal_msg.exec_()

    def show_info(self, message: str):
        self.show_message(self.tr("Info"), message)

    def show_warning(self, message: str):
        self.show_message(self.tr("Warning"), message)

    def show_error(self, message: str):
        self.show_message(self.tr("Error"), message)

    def on_load_dataset_clicked(self):
        self.load_dataset_dialog.show()

    def on_dataset_loaded(self, dataset: GrainSizeDataset):
        self.dataset = dataset
        self.n_samples_display.setText(str(dataset.n_samples))
        self.sample_index_input.setRange(1, dataset.n_samples)
        self.sample_index_input.setEnabled(True)
        self.manual_test_button.setEnabled(True)
        self.single_test_button.setEnabled(True)
        self.continuous_test_button.setEnabled(True)
        self.test_previous_button.setEnabled(True)
        self.test_next_button.setEnabled(True)

    def on_configure_fitting_clicked(self):
        if self.resolver_combo_box.currentText() == "classic":
            self.classic_setting.show()
        else:
            self.neural_setting.show()

    def on_sample_index_changed(self, index):
        self.sample_name_display.setText(self.dataset.samples[index - 1].name)

    def generate_task(self, query_ref=True):
        sample_index = self.sample_index_input.value() - 1
        sample = self.dataset.samples[sample_index]

        resolver = self.resolver_combo_box.currentText()
        if resolver == "classic":
            setting = self.classic_setting.setting
        else:
            setting = self.neural_setting.setting

        query = self.reference_view.query_reference(sample)  # type: SSUResult
        if not query_ref or query is None:
            task = SSUTask(sample,
                           self.distribution_type,
                           self.n_components,
                           resolver=resolver,
                           resolver_setting=setting)
        else:
            keys = ["mean", "std", "skewness"]
            # reference = [{key: comp.logarithmic_moments[key] for key in keys} for comp in query.components]
            task = SSUTask(
                sample,
                query.distribution_type,
                query.n_components,
                resolver=resolver,
                resolver_setting=setting,
                # reference=reference)
                initial_guess=query.last_func_args)
        return task

    def on_fitting_succeeded(self, fitting_result: SSUResult):
        # update chart
        self.result_chart.show_model(fitting_result.view_model)
        self.result_view.add_result(fitting_result)
        self.task_results[fitting_result.task.uuid] = fitting_result
        if self.__continuous_flag:
            if self.sample_index_input.value() == self.dataset.n_samples:
                self.on_continuous_test_clicked()
            else:
                self.sample_index_input.setValue(
                    self.sample_index_input.value() + 1)
                self.do_test()
                return
        self.manual_test_button.setEnabled(True)
        self.single_test_button.setEnabled(True)
        self.continuous_test_button.setEnabled(True)
        self.test_previous_button.setEnabled(True)
        self.test_next_button.setEnabled(True)

    def on_fitting_failed(self, failed_info: str, task: SSUTask):
        self.failed_task_ids.append(task.uuid)
        if self.__continuous_flag:
            self.on_continuous_test_clicked()
        self.manual_test_button.setEnabled(True)
        self.single_test_button.setEnabled(True)
        self.continuous_test_button.setEnabled(True)
        self.test_previous_button.setEnabled(True)
        self.test_next_button.setEnabled(True)
        self.show_error(failed_info)

    def do_test(self):
        self.manual_test_button.setEnabled(False)
        self.single_test_button.setEnabled(False)
        self.test_previous_button.setEnabled(False)
        self.test_next_button.setEnabled(False)
        if not self.__continuous_flag:
            self.continuous_test_button.setEnabled(False)
        task = self.generate_task()
        self.task_table[task.uuid] = task
        self.async_worker.execute_task(task)

    def on_manual_test_clicked(self):
        task = self.generate_task(query_ref=False)
        self.manual_panel.setup_task(task)
        self.manual_panel.show()

    def on_single_test_clicked(self):
        self.do_test()

    def on_continuous_test_clicked(self):
        if self.__continuous_flag:
            self.__continuous_flag = not self.__continuous_flag
            self.continuous_test_button.setText(self.tr("Continuous Test"))
        else:
            self.continuous_test_button.setText(self.tr("Cancel"))
            self.__continuous_flag = not self.__continuous_flag
            self.do_test()

    def on_test_previous_clicked(self):
        current = self.sample_index_input.value()
        if current == 1:
            return
        self.sample_index_input.setValue(current - 1)
        self.do_test()

    def on_test_next_clicked(self):
        current = self.sample_index_input.value()
        if current == self.dataset.n_samples:
            return
        self.sample_index_input.setValue(current + 1)
        self.do_test()
Пример #10
0
    def init_ui(self):
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.main_layout = QGridLayout(self)
        # self.main_layout.setContentsMargins(0, 0, 0, 0)
        # control group
        self.control_group = QGroupBox(self.tr("Control"))
        self.control_layout = QGridLayout(self.control_group)
        self.resolver_label = QLabel(self.tr("Resolver"))
        self.resolver_combo_box = QComboBox()
        self.resolver_combo_box.addItems(["classic", "neural"])
        self.control_layout.addWidget(self.resolver_label, 0, 0)
        self.control_layout.addWidget(self.resolver_combo_box, 0, 1)
        self.load_dataset_button = QPushButton(qta.icon("fa.database"),
                                               self.tr("Load Dataset"))
        self.load_dataset_button.clicked.connect(self.on_load_dataset_clicked)
        self.configure_fitting_button = QPushButton(
            qta.icon("fa.gears"), self.tr("Configure Fitting Algorithm"))
        self.configure_fitting_button.clicked.connect(
            self.on_configure_fitting_clicked)
        self.control_layout.addWidget(self.load_dataset_button, 1, 0)
        self.control_layout.addWidget(self.configure_fitting_button, 1, 1)
        self.distribution_label = QLabel(self.tr("Distribution Type"))
        self.distribution_combo_box = QComboBox()
        self.distribution_combo_box.addItems(
            [name for _, name in self.distribution_types])
        self.component_number_label = QLabel(self.tr("N<sub>components</sub>"))
        self.n_components_input = QSpinBox()
        self.n_components_input.setRange(1, 10)
        self.n_components_input.setValue(3)
        self.control_layout.addWidget(self.distribution_label, 2, 0)
        self.control_layout.addWidget(self.distribution_combo_box, 2, 1)
        self.control_layout.addWidget(self.component_number_label, 3, 0)
        self.control_layout.addWidget(self.n_components_input, 3, 1)

        self.n_samples_label = QLabel(self.tr("N<sub>samples</sub>"))
        self.n_samples_display = QLabel(self.tr("Unknown"))
        self.control_layout.addWidget(self.n_samples_label, 4, 0)
        self.control_layout.addWidget(self.n_samples_display, 4, 1)
        self.sample_index_label = QLabel(self.tr("Sample Index"))
        self.sample_index_input = QSpinBox()
        self.sample_index_input.valueChanged.connect(
            self.on_sample_index_changed)
        self.sample_index_input.setEnabled(False)
        self.control_layout.addWidget(self.sample_index_label, 5, 0)
        self.control_layout.addWidget(self.sample_index_input, 5, 1)
        self.sample_name_label = QLabel(self.tr("Sample Name"))
        self.sample_name_display = QLabel(self.tr("Unknown"))
        self.control_layout.addWidget(self.sample_name_label, 6, 0)
        self.control_layout.addWidget(self.sample_name_display, 6, 1)

        self.manual_test_button = QPushButton(qta.icon("fa.sliders"),
                                              self.tr("Manual Test"))
        self.manual_test_button.setEnabled(False)
        self.manual_test_button.clicked.connect(self.on_manual_test_clicked)
        self.load_reference_button = QPushButton(qta.icon("mdi.map-check"),
                                                 self.tr("Load Reference"))
        self.load_reference_button.clicked.connect(
            lambda: self.reference_view.load_dump(mark_ref=True))
        self.control_layout.addWidget(self.manual_test_button, 7, 0)
        self.control_layout.addWidget(self.load_reference_button, 7, 1)

        self.single_test_button = QPushButton(qta.icon("fa.play-circle"),
                                              self.tr("Single Test"))
        self.single_test_button.setEnabled(False)
        self.single_test_button.clicked.connect(self.on_single_test_clicked)
        self.continuous_test_button = QPushButton(
            qta.icon("mdi.playlist-play"), self.tr("Continuous Test"))
        self.continuous_test_button.setEnabled(False)
        self.continuous_test_button.clicked.connect(
            self.on_continuous_test_clicked)
        self.control_layout.addWidget(self.single_test_button, 8, 0)
        self.control_layout.addWidget(self.continuous_test_button, 8, 1)

        self.test_previous_button = QPushButton(
            qta.icon("mdi.skip-previous-circle"), self.tr("Test Previous"))
        self.test_previous_button.setEnabled(False)
        self.test_previous_button.clicked.connect(
            self.on_test_previous_clicked)
        self.test_next_button = QPushButton(qta.icon("mdi.skip-next-circle"),
                                            self.tr("Test Next"))
        self.test_next_button.setEnabled(False)
        self.test_next_button.clicked.connect(self.on_test_next_clicked)
        self.control_layout.addWidget(self.test_previous_button, 9, 0)
        self.control_layout.addWidget(self.test_next_button, 9, 1)

        # chart group
        self.chart_group = QGroupBox(self.tr("Chart"))
        self.chart_layout = QGridLayout(self.chart_group)
        self.result_chart = MixedDistributionChart(show_mode=True,
                                                   toolbar=False)
        self.chart_layout.addWidget(self.result_chart, 0, 0)

        # table group
        self.table_group = QGroupBox(self.tr("Table"))
        self.reference_view = ReferenceResultViewer()
        self.result_view = FittingResultViewer(self.reference_view)
        self.result_view.result_marked.connect(
            lambda result: self.reference_view.add_references([result]))
        self.table_tab = QTabWidget()
        self.table_tab.addTab(self.result_view, qta.icon("fa.cubes"),
                              self.tr("Result"))
        self.table_tab.addTab(self.reference_view, qta.icon("fa5s.key"),
                              self.tr("Reference"))
        self.result_layout = QGridLayout(self.table_group)
        self.result_layout.addWidget(self.table_tab, 0, 0)

        # pack all group
        self.splitter1 = QSplitter(Qt.Orientation.Vertical)
        self.splitter1.addWidget(self.control_group)
        self.splitter1.addWidget(self.chart_group)
        self.splitter2 = QSplitter(Qt.Orientation.Horizontal)
        self.splitter2.addWidget(self.splitter1)
        self.splitter2.addWidget(self.table_group)
        self.main_layout.addWidget(self.splitter2, 0, 0)
Пример #11
0
    def __init__(self, path_to_rom=""):
        super(MainWindow, self).__init__()

        self.setWindowIcon(icon("foundry.ico"))

        file_menu = QMenu("File")

        open_rom_action = file_menu.addAction("&Open ROM")
        open_rom_action.triggered.connect(self.on_open_rom)
        self.open_m3l_action = file_menu.addAction("&Open M3L")
        self.open_m3l_action.triggered.connect(self.on_open_m3l)

        file_menu.addSeparator()

        save_rom_action = file_menu.addAction("&Save ROM")
        save_rom_action.triggered.connect(self.on_save_rom)
        save_rom_as_action = file_menu.addAction("&Save ROM as ...")
        save_rom_as_action.triggered.connect(self.on_save_rom_as)
        """
        file_menu.AppendSeparator()
        """
        self.save_m3l_action = file_menu.addAction("&Save M3L")
        self.save_m3l_action.triggered.connect(self.on_save_m3l)
        """
        file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "")
        """
        file_menu.addSeparator()
        settings_action = file_menu.addAction("&Settings")
        settings_action.triggered.connect(show_settings)
        file_menu.addSeparator()
        exit_action = file_menu.addAction("&Exit")
        exit_action.triggered.connect(lambda _: self.close())

        self.menuBar().addMenu(file_menu)
        """
        edit_menu = wx.Menu()

        edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "")
        edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "")
        edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "")
        edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "")
        edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "")
        edit_menu.AppendSeparator()
        edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "")
        edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "")
        """

        level_menu = QMenu("Level")

        select_level_action = level_menu.addAction("&Select Level")
        select_level_action.triggered.connect(self.open_level_selector)
        """
        level_menu.Append(ID_GOTO_NEXT_AREA, "&Go to next Area", "")
        level_menu.AppendSeparator()
        """
        self.reload_action = level_menu.addAction("&Reload Level")
        self.reload_action.triggered.connect(self.reload_level)
        level_menu.addSeparator()
        self.edit_header_action = level_menu.addAction("&Edit Header")
        self.edit_header_action.triggered.connect(self.on_header_editor)
        """
        level_menu.Append(ID_EDIT_POINTERS, "&Edit Pointers", "")
        """

        self.menuBar().addMenu(level_menu)

        object_menu = QMenu("Objects")

        view_blocks_action = object_menu.addAction("&View Blocks")
        view_blocks_action.triggered.connect(self.on_block_viewer)
        view_objects_action = object_menu.addAction("&View Objects")
        view_objects_action.triggered.connect(self.on_object_viewer)
        """
        object_menu.AppendSeparator()
        object_menu.Append(ID_CLONE_OBJECT_ENEMY, "&Clone Object/Enemy", "")
        object_menu.AppendSeparator()
        object_menu.Append(ID_ADD_3_BYTE_OBJECT, "&Add 3 Byte Object", "")
        object_menu.Append(ID_ADD_4_BYTE_OBJECT, "&Add 4 Byte Object", "")
        object_menu.Append(ID_ADD_ENEMY, "&Add Enemy", "")
        object_menu.AppendSeparator()
        object_menu.Append(ID_DELETE_OBJECT_ENEMY, "&Delete Object/Enemy", "")
        object_menu.Append(ID_DELETE_ALL, "&Delete All", "")
        """

        self.menuBar().addMenu(object_menu)

        view_menu = QMenu("View")
        view_menu.triggered.connect(self.on_menu)

        action = view_menu.addAction("Mario")
        action.setProperty(ID_PROP, ID_MARIO)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_mario"])

        action = view_menu.addAction("&Jumps on objects")
        action.setProperty(ID_PROP, ID_JUMP_OBJECTS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jump_on_objects"])

        action = view_menu.addAction("Items in blocks")
        action.setProperty(ID_PROP, ID_ITEM_BLOCKS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_items_in_blocks"])

        action = view_menu.addAction("Invisible items")
        action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_invisible_items"])

        view_menu.addSeparator()

        action = view_menu.addAction("Jump Zones")
        action.setProperty(ID_PROP, ID_JUMPS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jumps"])

        action = view_menu.addAction("&Grid lines")
        action.setProperty(ID_PROP, ID_GRID_LINES)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_grid"])

        action = view_menu.addAction("Resize Type")
        action.setProperty(ID_PROP, ID_RESIZE_TYPE)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_expansion"])

        view_menu.addSeparator()

        action = view_menu.addAction("&Block Transparency")
        action.setProperty(ID_PROP, ID_TRANSPARENCY)
        action.setCheckable(True)
        action.setChecked(SETTINGS["block_transparency"])

        view_menu.addSeparator()
        view_menu.addAction("&Save Screenshot of Level").triggered.connect(
            self.on_screenshot)
        """
        view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "")
        view_menu.Append(ID_TOOLBAR, "&Toolbar", "")
        view_menu.AppendSeparator()
        view_menu.Append(ID_ZOOM, "&Zoom", "")
        view_menu.AppendSeparator()
        view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "")
        view_menu.Append(ID_PALETTE, "&Palette", "")
        view_menu.AppendSeparator()
        view_menu.Append(ID_MORE, "&More", "")
        """

        self.menuBar().addMenu(view_menu)

        help_menu = QMenu("Help")
        """
        help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "")
        help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "")
        help_menu.AppendSeparator()
        help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "")
        help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "")
        help_menu.AppendSeparator()
        """
        update_action = help_menu.addAction("Check for updates")
        update_action.triggered.connect(self.on_check_for_update)

        help_menu.addSeparator()

        video_action = help_menu.addAction("Feature Video on YouTube")
        video_action.triggered.connect(lambda: open_url(feature_video_link))

        discord_action = help_menu.addAction("SMB3 Rom Hacking Discord")
        discord_action.triggered.connect(lambda: open_url(discord_link))

        help_menu.addSeparator()

        about_action = help_menu.addAction("&About")
        about_action.triggered.connect(self.on_about)

        self.menuBar().addMenu(help_menu)

        self.level_selector = LevelSelector(parent=self)

        self.block_viewer = None
        self.object_viewer = None

        self.level_ref = LevelRef()
        self.level_ref.data_changed.connect(self._on_level_data_changed)

        self.context_menu = ContextMenu(self.level_ref)
        self.context_menu.triggered.connect(self.on_menu)

        self.level_view = LevelView(self, self.level_ref, self.context_menu)

        self.scroll_panel = QScrollArea()
        self.scroll_panel.setWidgetResizable(True)
        self.scroll_panel.setWidget(self.level_view)

        self.setCentralWidget(self.scroll_panel)

        self.spinner_panel = SpinnerPanel(self, self.level_ref)
        self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in)
        self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out)
        self.spinner_panel.object_change.connect(self.on_spin)

        self.object_list = ObjectList(self, self.level_ref, self.context_menu)

        self.object_dropdown = ObjectDropdown(self)
        self.object_dropdown.object_selected.connect(
            self._on_placeable_object_selected)

        self.level_size_bar = LevelSizeBar(self, self.level_ref)
        self.enemy_size_bar = EnemySizeBar(self, self.level_ref)

        self.jump_list = JumpList(self, self.level_ref)
        self.jump_list.add_jump.connect(self.on_jump_added)
        self.jump_list.edit_jump.connect(self.on_jump_edit)
        self.jump_list.remove_jump.connect(self.on_jump_removed)

        splitter = QSplitter(self)
        splitter.setOrientation(Qt.Vertical)

        splitter.addWidget(self.object_list)
        splitter.setStretchFactor(0, 1)
        splitter.addWidget(self.jump_list)

        splitter.setChildrenCollapsible(False)

        level_toolbar = QToolBar("Level Info Toolbar", self)
        level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        level_toolbar.setOrientation(Qt.Horizontal)
        level_toolbar.setFloatable(False)

        level_toolbar.addWidget(self.spinner_panel)
        level_toolbar.addWidget(self.object_dropdown)
        level_toolbar.addWidget(self.level_size_bar)
        level_toolbar.addWidget(self.enemy_size_bar)
        level_toolbar.addWidget(splitter)

        level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea)

        self.addToolBar(Qt.RightToolBarArea, level_toolbar)

        self.object_toolbar = ObjectToolBar(self)
        self.object_toolbar.object_selected.connect(
            self._on_placeable_object_selected)

        object_toolbar = QToolBar("Object Toolbar", self)
        object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        object_toolbar.setFloatable(False)

        object_toolbar.addWidget(self.object_toolbar)
        object_toolbar.setAllowedAreas(Qt.LeftToolBarArea
                                       | Qt.RightToolBarArea)

        self.addToolBar(Qt.LeftToolBarArea, object_toolbar)

        menu_toolbar = QToolBar("Menu Toolbar", self)
        menu_toolbar.setOrientation(Qt.Horizontal)
        menu_toolbar.setIconSize(QSize(20, 20))

        menu_toolbar.addAction(
            icon("settings.svg"),
            "Editor Settings").triggered.connect(show_settings)
        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("folder.svg"),
                               "Open ROM").triggered.connect(self.on_open_rom)
        menu_toolbar.addAction(
            icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom)
        menu_toolbar.addSeparator()

        self.undo_action = menu_toolbar.addAction(icon("rotate-ccw.svg"),
                                                  "Undo Action")
        self.undo_action.triggered.connect(self.level_ref.undo)
        self.undo_action.setEnabled(False)
        self.redo_action = menu_toolbar.addAction(icon("rotate-cw.svg"),
                                                  "Redo Action")
        self.redo_action.triggered.connect(self.level_ref.redo)
        self.redo_action.setEnabled(False)

        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("play-circle.svg"),
                               "Play Level").triggered.connect(self.on_play)
        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("zoom-out.svg"),
                               "Zoom Out").triggered.connect(
                                   self.level_view.zoom_out)
        menu_toolbar.addAction(icon("zoom-in.svg"),
                               "Zoom In").triggered.connect(
                                   self.level_view.zoom_in)
        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("tool.svg"),
                               "Edit Level Header").triggered.connect(
                                   self.on_header_editor)
        self.jump_destination_action = menu_toolbar.addAction(
            icon("arrow-right-circle.svg"), "Go to Jump Destination")
        self.jump_destination_action.triggered.connect(
            self._go_to_jump_destination)
        menu_toolbar.addSeparator()
        # menu_toolbar.addAction(icon("help-circle.svg"), "What's this?")

        self.addToolBar(Qt.TopToolBarArea, menu_toolbar)

        self.status_bar = ObjectStatusBar(self, self.level_ref)
        self.setStatusBar(self.status_bar)

        self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self,
                                         self.remove_selected_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self,
                  self.level_ref.redo)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self,
                  self.level_view.zoom_in)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self,
                  self.level_view.zoom_out)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self,
                  self.level_view.select_all)

        if not self.on_open_rom(path_to_rom):
            self.deleteLater()

        self.showMaximized()
Пример #12
0
class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, appctxt=None):
        super().__init__()
        
        self.appcontext = appctxt
        self.BASE_TITLE = 'PyMarkup'
        self.REFRESH_DELAY_MS = 200
        self.modified = False
        self.refresh_timer = QtCore.QTimer()
        self.refresh_timer.setSingleShot(True)
        self.refresh_timer.setInterval(self.REFRESH_DELAY_MS)
        self.refresh_timer.timeout.connect(self.Refresh)

        self.settings = self.LoadSettings()
        
        self.init_widgets()
        self.init_layout()
        self.init_mainmenu()

        self.python_path = None

        self.save_html = True # Always true unless disabled in debug mode
        
        try:
            self.css = load_css(self.GetPath('template_css'))
        except KeyError:
            self.css = ''

        self.LoadResources()

        # print('Singles:',len(self.singles.keys()))
        # self.singles = {}
        
        # print('Macros:',len(self.macros.keys()))
        self.macros = {}

        # print('Images:',len(self.images.keys()))
        self.images = {}

        self.subitems = {}

        try: self.LoadFileFromPath(self.settings['paths']['lastopened'])
        except KeyError: pass

        self.Refresh()


    def GetPath(self,key):

        default_names = {
            'template_css': 'template.css',
            'template_html': 'template.html',
            'singles': 'singles.csv',
            'table_products': 'singles.csv',
            'table_macros': 'macros.csv',
            'img_folder': 'img',
        }

        try:
            path = self.settings['paths'][key]
            if os.path.exists(path):
                # print('Returning path for {}: {}'.format(key,self.settings['paths'][key]))
                return path
        except KeyError:
            pass

        try: return os.path.join(self.settings['paths']['sysfolder'],default_names[key])
        except KeyError: pass

        return None


    # Load two tables and images
    def LoadResources(self):
        self.singles = read_csv(self.GetPath('table_products'))
        return
        self.subitems = get_subitems(self.singles)
        self.macros = read_csv_adv(self.GetPath('table_macros'))
        self.images = get_images(self.GetPath('img_folder'))
        self.ConsoleLog('Database aggiornato.')

    
    def ScheduleRefresh(self):
        # print('Refresh timer started for 100 ms')
        self.modified = True
        self.UpdateTitle()
        self.refresh_timer.start()


    def init_widgets(self):

        if ADD_WEBENGINE:
            self.browser = Browser(self)
            # self.browser.setZoomFactor(1.5) # Valid values: 0.25 to 5.0
            self.browser.setZoomFactor(self.settings['browserzoomfactor'])

        # self.texteditor = QtWidgets.QPlainTextEdit()
        self.texteditor = QCodeEditor()
        self.texteditor.setPlainText('cliente = "Asd"')
        # self.texteditor.setPlainText(self.settings['texteditor'])
        self.texteditor.zoomIn(self.settings['zoom_texteditor'])
        self.texteditor.textChanged.connect(self.ScheduleRefresh)

        self.console = QtWidgets.QPlainTextEdit()
        self.console.setReadOnly(True)
        self.console.zoomIn(self.settings['zoom_console'])


    def init_mainmenu(self):
        menubar = self.menuBar()
        # filemenu = menubar.addMenu('File')
        # viewmenu = menubar.addMenu('Visualizza')
        
        menus = {
            'File': [
                ['Nuovo file', 'Ctrl+N', self.NewFile],
                ['Apri...', 'Ctrl+O', self.ChooseFile],
                ['Salva', 'Ctrl+S', self.Save],
                ['Salva con nome...', 'Ctrl+Shift+S', self.SaveAs],
                [],
                ['Esporta PDF (preventivo)', 'Ctrl+E', self.RenderPDF_estimate],
                ['Esporta PDF (proforma)', 'Ctrl+T', self.RenderPDF_proforma],
                [],
                ['Chiudi', 'Ctrl+Shift+Q',          self.close],
            ],
            'Visualizza': [
                ['Ingrandisci editor',None,self.ZoomInEditor],
                ['Riduci editor',None,self.ZoomOutEditor],
                [],
                ['Ingrandisci anteprima',None,self.ZoomInBrowser],
                ['Riduci anteprima',None,self.ZoomOutBrowser],
            ],
            'Strumenti': [
                ['Aggiorna anteprima', 'Ctrl+R',    self.Refresh],
                ['Aggiorna database', 'Ctrl+L',     self.LoadResources],
                ['Impostazioni...', 'Ctrl+I',       self.OpenSettingsDialog],
            ]
        }

        if DEBUG:
            menus['Debug'] = [
                # ['Print pwd', 'Ctrl+P',          self.PrintPwd],
                ['Which Python',        None,   self.RunSubprocess],
                ['Print this folder',   None,   self.PrintThisFolder],
                ['Save html on',        None,   self.EnableSaveHTML],
                ['Save html off',       None,   self.DisableSaveHTML],
                ['Print settings',      None,   self.PrintSettings],
            ]

        for menu_name,entries in menus.items():
            menu = menubar.addMenu(menu_name)
            for data in entries:
                if len(data)>0:
                    label,shortcut,function = data
                    # label,shortcut,function = item
                    new_action = QAction(label,self)
                    if shortcut:
                        new_action.setShortcut(shortcut)
                    new_action.triggered.connect(function)
                    menu.addAction(new_action)
                else:
                    menu.addSeparator()

    def EnableSaveHTML(self):
        self.save_html = True;

    def DisableSaveHTML(self):
        self.save_html = False;

    def PrintSettings(self):
        settings = json.dumps(self.settings,indent=4)
        self.ConsoleLog(settings)

        paths = [
            'template_css',
            'template_html',
            'singles',
            'table_products',
            'table_macros',
            'img_folder',
        ]
        print('Paths:')
        for path in paths:
            print(self.GetPath(path))
        print('\nSettings:')
        print(settings)

    def PrintPwd(self):
        path = os.path.abspath('./')
        self.ConsoleLog(path)

    def PrintThisFolder(self):
        self.ConsoleLog(THIS_FOLDER);

    def ZoomInEditor(self):
        self.texteditor.zoomIn()

    def ZoomOutEditor(self):
        self.texteditor.zoomOut()

    def ZoomInBrowser(self):
        self.browser.setZoomFactor(self.browser.zoomFactor() + 0.1)

    def ZoomOutBrowser(self):
        self.browser.setZoomFactor(self.browser.zoomFactor() - 0.1)


    def Dummy(self):
        return


    def GetPythonPath(self):
        cmd = 'which python3'
        response = run(cmd, shell=True, stdout=PIPE, stderr=PIPE)
        return response.stdout.decode('utf-8')


    def RunSubprocess(self):

        self.ConsoleLog(self.GetPythonPath())

        '''
        cmd1 = '/Users/pc/Dev/pymarkup_installer_redux/local/sys_folder/xhtml2pdf.command'
        cmd2 = 'pwd > /Users/pc/Dev/pymarkup_installer_redux/local/sys_folder/pwd.txt'
        cmd = 'which python3'

        response = run(cmd, shell=True, stdout=PIPE, stderr=PIPE)
        #self.ConsoleLog(str(response))

        log = [cmd, response.returncode, response.stdout.decode('utf-8'), response.stderr]
        #log = [cmd, response]
        self.ConsoleLog('\n'.join([str(x) for x in log]))
        '''
        '''
        process = os.popen("which python")
        result = process.read()
        print(result)
        self.ConsoleLog(result)
        '''


    def ConfirmClose(self):
        # print('ConfirmClose')
        if self.modified:
            dialog = QMessageBox()
            # dialog.setIcon(QMessageBox.Question)
            dialog.setWindowTitle('Modifiche non salvate')
            dialog.setInformativeText(u"Abbandonare le modifiche in corso?")
            dialog.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
            choice = dialog.exec_()
            return choice == QMessageBox.Ok
        return True


    def ChooseFile(self):
        if self.ConfirmClose():
            chosen_path = QtWidgets.QFileDialog.getOpenFileName(self, 'Apri file', '', '*.toml',)
            if chosen_path[0] != '':
                self.settings['paths']['lastopened'] = chosen_path[0]
                self.LoadFileFromPath(chosen_path[0])
                self.modified = False
                self.UpdateTitle()


    def LoadFileFromPath(self,path):
        if os.path.exists(path):
            with open(path, 'r+') as file:
                self.texteditor.setPlainText(file.read())
            self.modified = False
            self.UpdateTitle()


    def NewFile(self):
        if self.ConfirmClose():
            self.modified = False
            self.settings['paths']['lastopened'] = ''
            self.setWindowTitle(self.BASE_TITLE+' - senza titolo')
            self.texteditor.setPlainText('')
            self.Refresh()
            self.DisplayHTML('')
            self.ConsoleLog('Nuovo file creato.')


    def Save(self):
        path = self.settings['paths']['lastopened']
        if os.path.exists(path):
            try:
                with open(path,'w+') as file:
                    file.write(self.texteditor.toPlainText())
                self.ConsoleLog('File salvato:\n'+path)
                self.modified = False
                self.UpdateTitle()
            except Exception as e:
                self.ConsoleLog(e)


    def SaveAs(self):

        path,_ = QtWidgets.QFileDialog().getSaveFileName(self, "Salva file", '', "Toml (*.toml)")
        if path != '':
            try:
                with open(path,'w+') as file:
                    file.write(self.texteditor.toPlainText())
                self.settings['paths']['lastopened'] = path
                self.modified = False
                self.ConsoleLog('File salvato:\n'+path)
                self.UpdateTitle()

            except Exception as e:
                print(str(e))
                self.ConsoleLog(str(e))


    def UpdateTitle(self):
        path = self.settings['paths']['lastopened']
        new_title = '{} - {}'.format(self.BASE_TITLE,path)
        if self.modified:
            new_title += ' (modificato)'
        self.setWindowTitle(new_title)


    def init_layout(self):
        self.setWindowTitle(self.BASE_TITLE)

        '''
        left_layout = QtWidgets.QVBoxLayout()
        left_layout.addWidget(self.texteditor)
        left_layout.addWidget(self.console)
        left_layout_widget = QtWidgets.QWidget()
        left_layout_widget.setLayout(left_layout)
        '''
        
        self.layout_left_splitter = QSplitter()
        self.layout_left_splitter.setOrientation(QtCore.Qt.Vertical)
        self.layout_left_splitter.addWidget(self.texteditor)
        self.layout_left_splitter.addWidget(self.console)

        self.layout_panes = QSplitter()
        self.layout_panes.addWidget(self.layout_left_splitter)

        if ADD_WEBENGINE:
            self.layout_panes.addWidget(self.browser)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.layout_panes)

        # Set size for left splitter
        for settings_key,splitter in [
            ['left_splitter_sizes', self.layout_left_splitter],
            ['splitter_sizes',      self.layout_panes],
        ]:
            splitter_size = self.settings.get(settings_key)
            if splitter_size is not None:
                splitter.setSizes(splitter_size)

        # Set size for main splitter
        # splitter_sizes = self.settings.get('splitter_sizes')
        # if splitter_sizes is not None:
        #     self.layout_panes.setSizes(splitter_sizes)

        centralWidget = QtWidgets.QWidget()
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)

        self.setGeometry(*self.settings['geometry'])


    def RenderHTML(self, **kwargs):
        
        toml_text = self.texteditor.toPlainText()
        toml_model = read_toml(toml_text, css=self.css)
        order_model = merge(toml_model, self.singles, self.subitems, self.macros, self.images)
        # print('TOML merge OK')
        
        template_path = 'template.html'
        # print('Template path:',template_path)
        path_html  = self.GetPath('template_html')
        path_css  = self.GetPath('template_css')
        return render_html(order_model, path_html=path_html, path_css=path_css, **kwargs)


    def RenderPDF(self, **kwargs):

        folder,filename = os.path.split(self.settings['paths']['lastopened'])
        name,_ = os.path.splitext(filename)
        defaultpath = os.path.join(folder,name+'.pdf')
        # print('Last opened folder/filename:',folder)

        savepath,_ = QtWidgets.QFileDialog().getSaveFileName(self, "Salva PDF", defaultpath, "PDF (*.pdf)")
        if savepath == '':
            return
        
        path_sys = self.settings['paths']['sysfolder']
        html = self.RenderHTML(**kwargs)

        htmlpath = os.path.join(path_sys,'tmp.html')

        # Save 'tmp.html'
        # MAY BE DISABLED FOR DEBUGGING
        if self.save_html:
            with open(htmlpath, 'w+', encoding='UTF-8') as file:
                file.write(html)

        sys_folder = self.settings['paths']['sysfolder']
        #path_script = os.path.join(self.settings['paths']['renderpdf'], 'Contents/MacOS/renderpdf')
        path_script = os.path.join(sys_folder, 'renderpdf.py')
        #self.ConsoleLog(html)

        print('-----')
        print('RenderPDF paths:')
        print(path_script, os.path.exists(path_script))
        print(htmlpath, os.path.exists(htmlpath))
        print(savepath)
        print('-----')

        #render_pdf(path_script,htmlpath,savepath)
        #python_path = '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3'

        python_path = self.settings['paths']['python']
        command = f'{python_path} "{path_script}" --args "{savepath}"'
        
        #command = f'xhtml2pdf {htmlpath} {savepath}'

        '''
        cmd_lines = [
            'from xhtml2pdf import pisa',
            f'resultFile = open("{savepath}", "w+b")',
            f'htmlFile = open("{htmlpath}","r+")',
            f'pisa.CreatePDF(htmlFile.read(),dest=resultFile)',
            'resultFile.close()',
            'htmlFile.close()',
        ]
        command = "python3 -c '" + ';'.join(cmd_lines) + "'"
        '''

        #command = f'python3 "{path_script}" "{savepath}"'

        # Path to renderpdf.command (as a proxy to execute renderpdf.py)
        #command = os.path.join(sys_folder, 'xhtml2pdf.command')

        print('Calling command:')
        print(command)
        print('-----')
        try:
            #result = call(command,shell=True) # shell=True is needed because subprocess.call is just a wrapper for Popen
            result = call(command, shell=True)
            self.ConsoleLog(command)
            #self.ConsoleLog('Xhtml2 call result: '+str(result))
            #self.ConsoleLog('PDF salvato: '+savepath)
        except FileNotFoundError as e:
            print('Subprocess.call raised an exception:')
            print(e)
            self.ConsoleLog(str(e))
        except Exception as e:
            self.ConsoleLog(str(e))
            with open(os.path.join(sys_folder,'applog.txt'),'r+') as file:
                file.write(str(e))

        # os.startfile('rendered.pdf')


    def RenderPDF_estimate(self):
        self.RenderPDF(preview=False,render_estimate=True)

        
    def RenderPDF_proforma(self):
        self.RenderPDF(preview=False,render_proforma=True)


    def DisplayHTML(self, html):
        # print('Display HTML')
        self.browser.SetHTML(html)


    def Refresh(self):
        # print('Refresh')
        try:
            html = self.RenderHTML(preview=True, render_estimate=True)
            self.DisplayHTML(html)
            self.ConsoleLog('OK')
            # self.ConsoleLog('Render HTML OK. Html length: {}\n{}'.format(len(html),html))
        except Exception as e:
            print(e)
            self.ConsoleLog(str(e))


    def ConsoleLog(self,msg):
        self.console.setPlainText(msg)


    def LoadSettings(self):
        default_settings = {
            'geometry': [500,300,1400,1000],
            # 'texteditor': '',
            'splitter_sizes': None,
            'left_splitter_sizes': None,
            'browserzoomfactor': 1,
            'paths': {
                'lastopened': '',
                'python': '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3',
            },
            'zoom_texteditor': 2,
            'zoom_console': 2,
        }

        qsettings = QtCore.QSettings('Company','Appname')
        jsontext = qsettings.value('settings',None)
        # print('Json settings from QSettings:',jsontext)

        settings = json.loads(jsontext) if jsontext else {}
        for key in default_settings.keys():
            if key in settings:
                default_settings[key] = settings[key]

        # Check all paths and delete them if they are broken
        for key,path in default_settings['paths'].items():
            if not os.path.exists(path):
                default_settings['paths'][key] = ''

        return default_settings


    def SaveSettings(self):
        # print('Saving settings')
        
        g = self.geometry()
        self.settings['geometry'] = [g.x(), g.y() ,g.width(), g.height()]
        # self.settings['texteditor'] = self.texteditor.toPlainText()
        self.settings['splitter_sizes'] = self.layout_panes.sizes()
        self.settings['left_splitter_sizes'] = self.layout_left_splitter.sizes()
        self.settings['browserzoomfactor'] = self.browser.zoomFactor()

        qsettings = QtCore.QSettings('Company','Appname')
        qsettings.setValue('settings', json.dumps(self.settings))


    def OpenSettingsDialog(self):
        dialog = SettingsDialog(self)
        dialog.exec_()


    def closeEvent(self,event):
        # print('closeEvent')
        if self.ConfirmClose():
            event.ignore()
            self.SaveSettings()
            # print('Closing program (settings saved)')
            event.accept()
        else:
            event.ignore()


    '''
Пример #13
0
    def initUI(self):

        hbox = QHBoxLayout(self)

        topleft = QFrame(self)
        topleft.setFrameShape(QFrame.StyledPanel)

        topright = QFrame(self)
        topright.setFrameShape(QFrame.StyledPanel)

        bottom = QFrame(self)
        bottom.setFrameShape(QFrame.StyledPanel)

        splitter1 = QSplitter(Qt.Horizontal)
        splitter1.addWidget(topleft)
        splitter1.addWidget(topright)

        splitter2 = QSplitter(Qt.Vertical)
        splitter2.addWidget(splitter1)
        splitter2.addWidget(bottom)

        hbox.addWidget(splitter2)
        self.setLayout(hbox)

        self.setGeometry(300, 300, 450, 400)
        self.setWindowTitle('QSplitter')
        self.show()
Пример #14
0
    def init_meta_tab(self):
        # Center panels -------------------------------------------------------
        self.groups_list = []

        # Left-side panel: forms
        self.btn_load_meta = QPushButton('Load metafile')
        self.btn_load_meta.setIcon(self.style().standardIcon(
            QStyle.SP_ArrowDown))
        self.btn_load_meta.clicked.connect(
            lambda: self.load_meta_file(filename=None))
        self.btn_load_meta.setToolTip(
            "The YAML file with metadata for this conversion.\n"
            "You can customize the metadata in the forms below.")
        self.btn_save_meta = QPushButton('Save metafile')
        self.btn_save_meta.setIcon(self.style().standardIcon(
            QStyle.SP_DriveFDIcon))
        self.btn_save_meta.clicked.connect(self.save_meta_file)
        self.btn_run_conversion = QPushButton('Run conversion')
        self.btn_run_conversion.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPlay))
        self.btn_run_conversion.clicked.connect(self.run_conversion)
        self.btn_form_editor = QPushButton('Form -> Editor')
        self.btn_form_editor.clicked.connect(self.form_to_editor)

        self.lbl_nwb_file = QLabel('Output nwb file:')
        self.lbl_nwb_file.setToolTip(
            "Path to the NWB file that will be created.")
        self.lin_nwb_file = QLineEdit('')
        self.btn_nwb_file = QPushButton()
        self.btn_nwb_file.setIcon(self.style().standardIcon(
            QStyle.SP_DialogOpenButton))
        self.btn_nwb_file.clicked.connect(self.load_nwb_file)

        l_grid1 = QGridLayout()
        l_grid1.setColumnStretch(3, 1)
        l_grid1.addWidget(self.btn_load_meta, 0, 0, 1, 1)
        l_grid1.addWidget(self.btn_save_meta, 0, 1, 1, 1)
        l_grid1.addWidget(self.btn_run_conversion, 0, 2, 1, 1)
        l_grid1.addWidget(QLabel(), 0, 3, 1, 1)
        l_grid1.addWidget(self.btn_form_editor, 0, 4, 1, 2)
        l_grid1.addWidget(self.lbl_nwb_file, 1, 0, 1, 1)
        l_grid1.addWidget(self.lin_nwb_file, 1, 1, 1, 3)
        l_grid1.addWidget(self.btn_nwb_file, 1, 4, 1, 1)

        # Adds custom files/dir paths fields
        if len(self.source_paths.keys()) == 0:
            self.lbl_source_file = QLabel('source files:')
            self.lin_source_file = QLineEdit('')
            self.btn_source_file = QPushButton()
            self.btn_source_file.setIcon(self.style().standardIcon(
                QStyle.SP_DialogOpenButton))
            self.btn_source_file.clicked.connect(self.load_source_files)
            l_grid1.addWidget(self.lbl_source_file, 3, 0, 1, 1)
            l_grid1.addWidget(self.lin_source_file, 3, 1, 1, 3)
            l_grid1.addWidget(self.btn_source_file, 3, 4, 1, 1)
        else:
            self.group_source_paths = QGroupBox('Source paths')
            self.grid_source = QGridLayout()
            self.grid_source.setColumnStretch(3, 1)
            ii = -1
            for k, v in self.source_paths.items():
                ii += 1
                lbl_src = QLabel(k + ':')
                setattr(self, 'lbl_src_' + str(ii), lbl_src)
                lin_src = QLineEdit('')
                setattr(self, 'lin_src_' + str(ii), lin_src)
                btn_src = QPushButton()
                btn_src.setIcon(self.style().standardIcon(
                    QStyle.SP_DialogOpenButton))
                setattr(self, 'btn_src_' + str(ii), btn_src)
                if v['type'] == 'file':
                    btn_src.clicked.connect(
                        (lambda x: lambda: self.load_source_files(x[0], x[1]))(
                            [ii, k]))
                else:
                    btn_src.clicked.connect(
                        (lambda x: lambda: self.load_source_dir(x[0], x[1]))(
                            [ii, k]))
                self.grid_source.addWidget(lbl_src, ii, 0, 1, 1)
                self.grid_source.addWidget(lin_src, ii, 1, 1, 3)
                self.grid_source.addWidget(btn_src, ii, 4, 1, 1)
            self.group_source_paths.setLayout(self.grid_source)
            l_grid1.addWidget(self.group_source_paths, 3, 0, 1, 6)

        # Adds custom kwargs checkboxes
        if len(self.kwargs_fields.keys()) > 0:
            self.group_kwargs = QGroupBox('KWARGS')
            self.grid_kwargs = QGridLayout()
            self.grid_kwargs.setColumnStretch(4, 1)
            ii = -1
            for k, v in self.kwargs_fields.items():
                ii += 1
                chk_kwargs = QCheckBox(k)
                chk_kwargs.setChecked(v)
                chk_kwargs.clicked.connect(
                    (lambda x: lambda: self.update_kwargs(x[0], x[1]))([ii,
                                                                        k]))
                setattr(self, 'chk_kwargs_' + str(ii), chk_kwargs)
                self.grid_kwargs.addWidget(chk_kwargs, ii // 4, ii % 4, 1, 1)
            self.group_kwargs.setLayout(self.grid_kwargs)
            l_grid1.addWidget(self.group_kwargs, 4, 0, 1, 6)

        self.l_vbox1 = QVBoxLayout()
        self.l_vbox1.addStretch()
        scroll_aux = QWidget()
        scroll_aux.setLayout(self.l_vbox1)
        l_scroll = QScrollArea()
        l_scroll.setWidget(scroll_aux)
        l_scroll.setWidgetResizable(True)

        self.l_vbox2 = QVBoxLayout()
        self.l_vbox2.addLayout(l_grid1)
        self.l_vbox2.addWidget(l_scroll)

        # Right-side panel
        # Metadata text
        editor_label = QLabel('Metafile preview:')
        r_grid1 = QGridLayout()
        r_grid1.setColumnStretch(1, 1)
        r_grid1.addWidget(editor_label, 0, 0, 1, 1)
        r_grid1.addWidget(QLabel(), 0, 1, 1, 1)
        self.editor = QTextEdit()
        r_vbox1 = QVBoxLayout()
        r_vbox1.addLayout(r_grid1)
        r_vbox1.addWidget(self.editor)

        # Logger
        log_label = QLabel('Log:')
        r_grid2 = QGridLayout()
        r_grid2.setColumnStretch(1, 1)
        r_grid2.addWidget(log_label, 0, 0, 1, 1)
        r_grid2.addWidget(QLabel(), 0, 1, 1, 1)
        self.logger = QTextEdit()
        self.logger.setReadOnly(True)
        r_vbox2 = QVBoxLayout()
        r_vbox2.addLayout(r_grid2)
        r_vbox2.addWidget(self.logger)

        r_vsplitter = QSplitter(QtCore.Qt.Vertical)
        ru_w = QWidget()
        ru_w.setLayout(r_vbox1)
        rb_w = QWidget()
        rb_w.setLayout(r_vbox2)
        r_vsplitter.addWidget(ru_w)
        r_vsplitter.addWidget(rb_w)

        # Metadata/conversion tab Layout
        self.left_w = QWidget()
        self.left_w.setLayout(self.l_vbox2)
        self.splitter = QSplitter(QtCore.Qt.Horizontal)
        self.splitter.addWidget(self.left_w)
        self.splitter.addWidget(r_vsplitter)

        self.metadata_layout = QVBoxLayout()
        self.metadata_layout.addWidget(self.splitter)
        self.tab_metadata = QWidget()
        self.tab_metadata.setLayout(self.metadata_layout)
        self.tabs.addTab(self.tab_metadata, 'Metadata/Conversion')

        # Background color
        p = self.palette()
        p.setColor(self.backgroundRole(), QtCore.Qt.white)
        self.setPalette(p)
Пример #15
0
class AppMainWindow(QMainWindow):
    """ The main window the app will be displayed in. """
    def __init__(self, min_size: QSize, log_handlers: [StreamHandler],
                 lang: LangEnum):
        self._logger = getLogger(__name__)
        for h in log_handlers:
            self._logger.addHandler(h)
        self._logger.debug("Initializing")
        super().__init__()
        self._icon = QIcon(image_file_path + "rs_icon.png")
        font = QFont()
        font.setPointSize(10)
        self.setFont(font)
        self.setMinimumSize(min_size)
        self.setWindowIcon(self._icon)
        self.setCentralWidget(CentralWidget(self))

        self._control_frame = QFrame(self)
        self._control_frame.setFrameShape(QFrame.NoFrame)
        self._control_layout = QHBoxLayout(self._control_frame)
        self._splitter = QSplitter(Qt.Vertical, self)
        # self.centralWidget().layout().addLayout(self._control_layout)
        self.centralWidget().layout().addWidget(self._splitter)
        self._splitter.addWidget(self._control_frame)

        self.close_check = False
        self._checker = QMessageBox()
        self._close_callback = None
        self._help_window = None

        self._strings = dict()
        self.set_lang(lang)
        self._setup_checker_buttons()
        self._restore_window()
        self._logger.debug("Initialized")

    def set_lang(self, lang: LangEnum) -> None:
        """
        Set the language of this view item.
        :param lang: The language enum to use.
        :return None:
        """
        self._strings = strings[lang]
        self._set_texts()

    def set_close_check(self, check: bool) -> None:
        """
        Set whether to check with user before closing app.
        :param check:
        :return:
        """
        self.close_check = check

    def closeEvent(self, event: QCloseEvent) -> None:
        """
        Check if user really wants to close the app and only if so alert close and close.
        :param event: The close event.
        :return: None.
        """
        self._logger.debug("running")
        if self.close_check:
            user_input = self._checker.exec_() == QMessageBox.Yes
            if not user_input:
                event.ignore()
                return
        settings = QSettings(company_name, app_name)
        settings.setValue(window_geometry, self.saveGeometry())
        settings.setValue(window_state, self.saveState())
        if self._close_callback:
            self._close_callback()
        event.accept()
        self._logger.debug("done")

    def add_mdi_area(self, mdi_area: QMdiArea) -> None:
        """
        Add MDI area to the main window.
        :param mdi_area: The MDI area to add.
        :return: None.
        """
        self._logger.debug("running")
        self._splitter.addWidget(mdi_area)
        self._logger.debug("done")

    def add_control_bar_widget(self, widget, stretch: int = 0) -> None:
        """
        Add widget to the control layout.
        :param widget: The widget to add.
        :param stretch: stretch factor
        :return: None.
        """
        self._logger.debug("running")
        self._control_layout.addWidget(widget, stretch)
        self._logger.debug("done")

    def add_spacer_item(self, stretch) -> None:
        """
        Add spacer item to maintain control bar format.
        :return: None.
        """
        self._logger.debug("running")
        self._control_layout.addStretch(stretch)
        self._logger.debug("done")

    def add_close_handler(self, func: classmethod) -> None:
        """
        Add handler to handle close events.
        :param func: The handler.
        :return: None.
        """
        self._logger.debug("running")
        self._close_callback = func
        self._logger.debug("done")

    def add_menu_bar(self, widget) -> None:
        """
        Add menu bar to main window.
        :param widget: The menu bar.
        :return: None.
        """
        self._logger.debug("running")
        self.setMenuBar(widget)
        self._logger.debug("done")

    def show_help_window(self, title, msg) -> None:
        """
        Show a pop up message window.
        :param title: The title of the window.
        :param msg: The message to be shown.
        :return: None
        """
        self._logger.debug("running")
        self._help_window = HelpWindow(title, msg)
        self._help_window.setWindowIcon(self._icon)
        self._help_window.show()
        self._logger.debug("done")

    def _restore_window(self) -> None:
        """
        Restore window state and geometry from previous session if exists.
        :return None:
        """
        settings = QSettings(company_name, app_name)
        if not settings.contains(window_geometry):
            settings.setValue(window_geometry, self.saveGeometry())
        if not settings.contains(window_state):
            settings.setValue(window_state, self.saveState())
        self.restoreGeometry(settings.value(window_geometry))
        self.restoreState(settings.value(window_state))

    def _set_texts(self) -> None:
        """
        Set the texts for the main window.
        :return: None.
        """
        self._logger.debug("running")
        self.setWindowTitle(self._strings[StringsEnum.TITLE])
        self._checker.setWindowTitle(self._strings[StringsEnum.CLOSE_TITLE])
        self._checker.setText(self._strings[StringsEnum.CLOSE_APP_CONFIRM])
        self._logger.debug("done")

    def _setup_checker_buttons(self) -> None:
        """
        Setup window for close check.
        :return: None.
        """
        self._logger.debug("running")
        self._checker.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
        self._checker.setDefaultButton(QMessageBox.Cancel)
        self._checker.setEscapeButton(QMessageBox.Cancel)
        self._logger.debug("done")
    def _setup(self):
        self.serial_input = SerialInputGui()
        self.serial_device = SerialDevicesGui()
        self.serial_monitor = SerialMonitorGui(self)

        hsplitter = QSplitter()
        hsplitter.setRubberBand(-1)
        hsplitter.setHandleWidth(10)
        hsplitter.setOrientation(Qt.Horizontal)
        hsplitter.addWidget(self.serial_monitor)
        hsplitter.addWidget(self.serial_device)

        vsplitter = QSplitter()
        vsplitter.setRubberBand(-1)
        vsplitter.setHandleWidth(10)
        vsplitter.setOrientation(Qt.Vertical)
        vsplitter.addWidget(hsplitter)
        vsplitter.addWidget(self.serial_input)

        self.layout.addWidget(vsplitter, 0, 0, 1, 1)
Пример #17
0
class DebugView(QWidget, View):
    class DebugViewHistoryEntry(HistoryEntry):
        def __init__(self, memory_addr, address, is_raw):
            HistoryEntry.__init__(self)

            self.memory_addr = memory_addr
            self.address = address
            self.is_raw = is_raw

        def __repr__(self):
            if self.is_raw:
                return "<raw history: {}+{:0x} (memory: {:0x})>".format(
                    self.address['module'], self.address['offset'],
                    self.memory_addr)
            return "<code history: {:0x} (memory: {:0x})>".format(
                self.address, self.memory_addr)

    def __init__(self, parent, data):
        if not type(data) == BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data

        self.debug_state = binjaplug.get_state(data)
        memory_view = self.debug_state.memory_view
        self.debug_state.ui.debug_view = self

        QWidget.__init__(self, parent)
        self.controls = ControlsWidget.DebugControlsWidget(
            self, "Controls", data, self.debug_state)
        View.__init__(self)

        self.setupView(self)

        self.current_offset = 0

        self.splitter = QSplitter(Qt.Orientation.Horizontal, self)

        frame = ViewFrame.viewFrameForWidget(self)
        self.memory_editor = LinearView(memory_view, frame)
        self.binary_editor = DisassemblyContainer(frame, data, frame)

        self.binary_text = TokenizedTextView(self, memory_view)
        self.is_raw_disassembly = False
        self.raw_address = 0

        self.is_navigating_history = False
        self.memory_history_addr = 0

        # TODO: Handle these and change views accordingly
        # Currently they are just disabled as the DisassemblyContainer gets confused
        # about where to go and just shows a bad view
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Hex Editor", UIAction())
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Linear Disassembly", UIAction())
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Types View", UIAction())

        self.memory_editor.actionHandler().bindAction("View in Hex Editor",
                                                      UIAction())
        self.memory_editor.actionHandler().bindAction(
            "View in Disassembly Graph", UIAction())
        self.memory_editor.actionHandler().bindAction("View in Types View",
                                                      UIAction())

        small_font = QApplication.font()
        small_font.setPointSize(11)

        bv_layout = QVBoxLayout()
        bv_layout.setSpacing(0)
        bv_layout.setContentsMargins(0, 0, 0, 0)

        bv_label = QLabel("Loaded File")
        bv_label.setFont(small_font)
        bv_layout.addWidget(bv_label)
        bv_layout.addWidget(self.binary_editor)

        self.bv_widget = QWidget()
        self.bv_widget.setLayout(bv_layout)

        disasm_layout = QVBoxLayout()
        disasm_layout.setSpacing(0)
        disasm_layout.setContentsMargins(0, 0, 0, 0)

        disasm_label = QLabel("Raw Disassembly at PC")
        disasm_label.setFont(small_font)
        disasm_layout.addWidget(disasm_label)
        disasm_layout.addWidget(self.binary_text)

        self.disasm_widget = QWidget()
        self.disasm_widget.setLayout(disasm_layout)

        memory_layout = QVBoxLayout()
        memory_layout.setSpacing(0)
        memory_layout.setContentsMargins(0, 0, 0, 0)

        memory_label = QLabel("Debugged Process")
        memory_label.setFont(small_font)
        memory_layout.addWidget(memory_label)
        memory_layout.addWidget(self.memory_editor)

        self.memory_widget = QWidget()
        self.memory_widget.setLayout(memory_layout)

        self.splitter.addWidget(self.bv_widget)
        self.splitter.addWidget(self.memory_widget)

        # Equally sized
        self.splitter.setSizes([0x7fffffff, 0x7fffffff])

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.controls)
        layout.addWidget(self.splitter, 100)
        self.setLayout(layout)

        self.needs_update = True
        self.update_timer = QTimer(self)
        self.update_timer.setInterval(200)
        self.update_timer.setSingleShot(False)
        self.update_timer.timeout.connect(lambda: self.updateTimerEvent())

        self.add_scripting_ref()

    def add_scripting_ref(self):
        # Hack: The interpreter is just a thread, so look through all threads
        # and assign our state to the interpreter's locals
        for thread in threading.enumerate():
            if type(thread) == PythonScriptingInstance.InterpreterThread:
                thread.locals["dbg"] = self.debug_state

    def getData(self):
        return self.bv

    def getFont(self):
        return binaryninjaui.getMonospaceFont(self)

    def getCurrentOffset(self):
        if not self.is_raw_disassembly:
            return self.binary_editor.getDisassembly().getCurrentOffset()
        return self.raw_address

    def getSelectionOffsets(self):
        if not self.is_raw_disassembly:
            return self.binary_editor.getDisassembly().getSelectionOffsets()
        return (self.raw_address, self.raw_address)

    def getCurrentFunction(self):
        if not self.is_raw_disassembly:
            return self.binary_editor.getDisassembly().getCurrentFunction()
        return None

    def getCurrentBasicBlock(self):
        if not self.is_raw_disassembly:
            return self.binary_editor.getDisassembly().getCurrentBasicBlock()
        return None

    def getCurrentArchitecture(self):
        if not self.is_raw_disassembly:
            return self.binary_editor.getDisassembly().getCurrentArchitecture()
        return None

    def getCurrentLowLevelILFunction(self):
        if not self.is_raw_disassembly:
            return self.binary_editor.getDisassembly(
            ).getCurrentLowLevelILFunction()
        return None

    def getCurrentMediumLevelILFunction(self):
        if not self.is_raw_disassembly:
            return self.binary_editor.getDisassembly(
            ).getCurrentMediumLevelILFunction()
        return None

    def getHistoryEntry(self):
        if self.is_navigating_history:
            return None
        memory_addr = self.memory_editor.getCurrentOffset()
        if memory_addr != self.memory_history_addr:
            self.memory_history_addr = memory_addr
        if self.is_raw_disassembly and self.debug_state.connected:
            rel_addr = self.debug_state.modules.absolute_addr_to_relative(
                self.raw_address)
            return DebugView.DebugViewHistoryEntry(memory_addr, rel_addr, True)
        else:
            address = self.binary_editor.getDisassembly().getCurrentOffset()
            return DebugView.DebugViewHistoryEntry(memory_addr, address, False)

    def navigateToHistoryEntry(self, entry):
        self.is_navigating_history = True
        if hasattr(entry, 'is_raw'):
            self.memory_editor.navigate(entry.memory_addr)
            if entry.is_raw:
                if self.debug_state.connected:
                    address = self.debug_state.modules.relative_addr_to_absolute(
                        entry.address)
                    self.navigate_raw(address)
            else:
                self.navigate_live(entry.address)

        View.navigateToHistoryEntry(self, entry)
        self.is_navigating_history = False

    def navigate(self, addr):
        if self.debug_state.memory_view.is_local_addr(addr):
            local_addr = self.debug_state.memory_view.remote_addr_to_local(
                addr)
            if self.debug_state.bv.read(local_addr, 1) and len(
                    self.debug_state.bv.get_functions_containing(
                        local_addr)) > 0:
                return self.navigate_live(local_addr)

        # This runs into conflicts if some other address space is mapped over
        # where the local BV is currently loaded, but this is was less likely
        # than the user navigating to a function from the UI
        if self.debug_state.bv.read(addr, 1) and len(
                self.debug_state.bv.get_functions_containing(addr)) > 0:
            return self.navigate_live(addr)

        return self.navigate_raw(addr)

    def navigate_live(self, addr):
        self.show_raw_disassembly(False)
        return self.binary_editor.getDisassembly().navigate(addr)

    def navigate_raw(self, addr):
        if not self.debug_state.connected:
            # Can't navigate to remote addr when disconnected
            return False
        self.raw_address = addr
        self.show_raw_disassembly(True)
        self.load_raw_disassembly(addr)
        return True

    def notifyMemoryChanged(self):
        self.needs_update = True

    def updateTimerEvent(self):
        if self.needs_update:
            self.needs_update = False

            # Refresh the editor
            if not self.debug_state.connected:
                self.memory_editor.navigate(0)
                return

            # self.memory_editor.navigate(self.debug_state.stack_pointer)

    def showEvent(self, event):
        if not event.spontaneous():
            self.update_timer.start()
            self.add_scripting_ref()

    def hideEvent(self, event):
        if not event.spontaneous():
            self.update_timer.stop()

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True

    def load_raw_disassembly(self, start_ip):
        # Read a few instructions from rip and disassemble them
        inst_count = 50

        arch_dis = self.debug_state.remote_arch
        rip = self.debug_state.ip

        # Assume the worst, just in case
        read_length = arch_dis.max_instr_length * inst_count
        data = self.debug_state.memory_view.read(start_ip, read_length)

        lines = []

        # Append header line
        tokens = [
            InstructionTextToken(
                InstructionTextTokenType.TextToken,
                "(Code not backed by loaded file, showing only raw disassembly)"
            )
        ]
        contents = DisassemblyTextLine(tokens, start_ip)
        line = LinearDisassemblyLine(LinearDisassemblyLineType.BasicLineType,
                                     None, None, 0, contents)
        lines.append(line)

        total_read = 0
        for i in range(inst_count):
            line_addr = start_ip + total_read
            (insn_tokens,
             length) = arch_dis.get_instruction_text(data[total_read:],
                                                     line_addr)

            if insn_tokens is None:
                insn_tokens = [
                    InstructionTextToken(InstructionTextTokenType.TextToken,
                                         "??")
                ]
                length = arch_dis.instr_alignment
                if length == 0:
                    length = 1

            tokens = []
            color = HighlightStandardColor.NoHighlightColor
            if line_addr == rip:
                if self.debug_state.breakpoints.contains_absolute(start_ip +
                                                                  total_read):
                    # Breakpoint & pc
                    tokens.append(
                        InstructionTextToken(
                            InstructionTextTokenType.TagToken,
                            self.debug_state.ui.get_breakpoint_tag_type().icon
                            + ">",
                            width=5))
                    color = HighlightStandardColor.RedHighlightColor
                else:
                    # PC
                    tokens.append(
                        InstructionTextToken(
                            InstructionTextTokenType.TextToken, " ==> "))
                    color = HighlightStandardColor.BlueHighlightColor
            else:
                if self.debug_state.breakpoints.contains_absolute(start_ip +
                                                                  total_read):
                    # Breakpoint
                    tokens.append(
                        InstructionTextToken(
                            InstructionTextTokenType.TagToken,
                            self.debug_state.ui.get_breakpoint_tag_type().icon,
                            width=5))
                    color = HighlightStandardColor.RedHighlightColor
                else:
                    # Regular line
                    tokens.append(
                        InstructionTextToken(
                            InstructionTextTokenType.TextToken, "     "))
            # Address
            tokens.append(
                InstructionTextToken(
                    InstructionTextTokenType.AddressDisplayToken,
                    hex(line_addr)[2:], line_addr))
            tokens.append(
                InstructionTextToken(InstructionTextTokenType.TextToken, "  "))
            tokens.extend(insn_tokens)

            # Convert to linear disassembly line
            contents = DisassemblyTextLine(tokens, line_addr, color=color)
            line = LinearDisassemblyLine(
                LinearDisassemblyLineType.CodeDisassemblyLineType, None, None,
                0, contents)
            lines.append(line)

            total_read += length

        # terrible workaround for libshiboken conversion issue
        for line in lines:
            # line is LinearDisassemblyLine
            last_tok = line.contents.tokens[-1]
            #if last_tok.type != InstructionTextTokenType.PossibleAddressToken: continue
            #if last_tok.width != 18: continue # strlen("0xFFFFFFFFFFFFFFF0")
            if last_tok.size != 8: continue
            #print('fixing: %s' % line)
            last_tok.value &= 0x7FFFFFFFFFFFFFFF

        self.binary_text.setLines(lines)

    def show_raw_disassembly(self, raw):
        if raw != self.is_raw_disassembly:
            self.splitter.replaceWidget(
                0, self.disasm_widget if raw else self.bv_widget)
            self.is_raw_disassembly = raw

    def refresh_raw_disassembly(self):
        if not self.debug_state.connected:
            # Can't navigate to remote addr when disconnected
            return

        if self.is_raw_disassembly:
            self.load_raw_disassembly(self.getCurrentOffset())
Пример #18
0
class DebugView(QWidget, View):
    def __init__(self, parent, data):
        if not type(data) == binaryninja.binaryview.BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data

        self.debug_state = binjaplug.get_state(data)
        memory_view = self.debug_state.memory_view
        self.debug_state.ui.debug_view = self

        QWidget.__init__(self, parent)
        self.controls = ControlsWidget.DebugControlsWidget(
            self, "Controls", data, self.debug_state)
        View.__init__(self)

        self.setupView(self)

        self.current_offset = 0

        self.splitter = QSplitter(Qt.Orientation.Horizontal, self)

        frame = ViewFrame.viewFrameForWidget(self)
        self.memory_editor = LinearView(memory_view, frame)
        self.binary_editor = DisassemblyContainer(frame, data, frame)

        self.binary_text = TokenizedTextView(self, memory_view)
        self.is_raw_disassembly = False

        # TODO: Handle these and change views accordingly
        # Currently they are just disabled as the DisassemblyContainer gets confused
        # about where to go and just shows a bad view
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Hex Editor", UIAction())
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Linear Disassembly", UIAction())
        self.binary_editor.getDisassembly().actionHandler().bindAction(
            "View in Types View", UIAction())

        self.memory_editor.actionHandler().bindAction("View in Hex Editor",
                                                      UIAction())
        self.memory_editor.actionHandler().bindAction(
            "View in Disassembly Graph", UIAction())
        self.memory_editor.actionHandler().bindAction("View in Types View",
                                                      UIAction())

        small_font = QApplication.font()
        small_font.setPointSize(11)

        bv_layout = QVBoxLayout()
        bv_layout.setSpacing(0)
        bv_layout.setContentsMargins(0, 0, 0, 0)

        bv_label = QLabel("Loaded File")
        bv_label.setFont(small_font)
        bv_layout.addWidget(bv_label)
        bv_layout.addWidget(self.binary_editor)

        self.bv_widget = QWidget()
        self.bv_widget.setLayout(bv_layout)

        disasm_layout = QVBoxLayout()
        disasm_layout.setSpacing(0)
        disasm_layout.setContentsMargins(0, 0, 0, 0)

        disasm_label = QLabel("Raw Disassembly at PC")
        disasm_label.setFont(small_font)
        disasm_layout.addWidget(disasm_label)
        disasm_layout.addWidget(self.binary_text)

        self.disasm_widget = QWidget()
        self.disasm_widget.setLayout(disasm_layout)

        memory_layout = QVBoxLayout()
        memory_layout.setSpacing(0)
        memory_layout.setContentsMargins(0, 0, 0, 0)

        memory_label = QLabel("Debugged Process")
        memory_label.setFont(small_font)
        memory_layout.addWidget(memory_label)
        memory_layout.addWidget(self.memory_editor)

        self.memory_widget = QWidget()
        self.memory_widget.setLayout(memory_layout)

        self.splitter.addWidget(self.bv_widget)
        self.splitter.addWidget(self.memory_widget)

        # Equally sized
        self.splitter.setSizes([0x7fffffff, 0x7fffffff])

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.controls)
        layout.addWidget(self.splitter, 100)
        self.setLayout(layout)

        self.needs_update = True
        self.update_timer = QTimer(self)
        self.update_timer.setInterval(200)
        self.update_timer.setSingleShot(False)
        self.update_timer.timeout.connect(lambda: self.updateTimerEvent())

        self.add_scripting_ref()

    def add_scripting_ref(self):
        # Hack: The interpreter is just a thread, so look through all threads
        # and assign our state to the interpreter's locals
        for thread in threading.enumerate():
            if type(thread) == PythonScriptingInstance.InterpreterThread:
                thread.locals["dbg"] = self.debug_state

    def getData(self):
        return self.bv

    def getCurrentOffset(self):
        return self.binary_editor.getDisassembly().getCurrentOffset()

    def getFont(self):
        return binaryninjaui.getMonospaceFont(self)

    def navigate(self, addr):
        return self.binary_editor.getDisassembly().navigate(addr)

    def notifyMemoryChanged(self):
        self.needs_update = True

    def updateTimerEvent(self):
        if self.needs_update:
            self.needs_update = False

            # Refresh the editor
            if not self.debug_state.connected:
                self.memory_editor.navigate(0)
                return

            self.memory_editor.navigate(self.debug_state.stack_pointer)

    def showEvent(self, event):
        if not event.spontaneous():
            self.update_timer.start()
            self.add_scripting_ref()

    def hideEvent(self, event):
        if not event.spontaneous():
            self.update_timer.stop()

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True

    def setRawDisassembly(self, raw=False, lines=[]):
        if raw != self.is_raw_disassembly:
            self.splitter.replaceWidget(
                0, self.disasm_widget if raw else self.bv_widget)
            self.is_raw_disassembly = raw

        # terrible workaround for libshiboken conversion issue
        for line in lines:
            # line is LinearDisassemblyLine
            last_tok = line.contents.tokens[-1]
            #if last_tok.type != InstructionTextTokenType.PossibleAddressToken: continue
            #if last_tok.width != 18: continue # strlen("0xFFFFFFFFFFFFFFF0")
            if last_tok.size != 8: continue
            #print('fixing: %s' % line)
            last_tok.value &= 0x7FFFFFFFFFFFFFFF

        self.binary_text.setLines(lines)
Пример #19
0
class ProcessMonitorUI:
    def __init__(self, window: ProcessMonitorWindow):
        self.window = window

        self.main_widget = QWidget(window)
        self.main_layout = QVBoxLayout()
        self.layout_connection = QHBoxLayout()
        self.txt_conenction = QLineEdit()
        self.btn_connect = QPushButton()
        self.layout_connection.addWidget(self.txt_conenction)
        self.layout_connection.addWidget(self.btn_connect)
        self.process_list = ProcessListWidget()

        self.tabs_output = ProcessOutputTabsWidget()

        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.setMinimumSize(680, 540)
        policy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        policy.setHorizontalStretch(0)
        policy.setVerticalStretch(0)
        policy.setHeightForWidth(self.splitter.sizePolicy().hasHeightForWidth())
        self.splitter.setSizePolicy(policy)
        self.splitter.setStretchFactor(0, 1)
        self.splitter.setStretchFactor(1, 0)

        self.main_layout.addLayout(self.layout_connection)
        self.splitter.addWidget(self.process_list)
        self.splitter.addWidget(self.tabs_output)
        self.main_layout.addWidget(self.splitter)

        self.main_widget.setLayout(self.main_layout)
        self.statusbar = QStatusBar(window)

        self.txt_conenction.setPlaceholderText("ws://127.0.0.1:8766")
        self.txt_conenction.setText("ws://127.0.0.1:8766")
        self.btn_connect.setText("Connect")

        window.setCentralWidget(self.main_widget)
        window.setStatusBar(self.statusbar)
        window.setWindowTitle("Process Monitor")
        window.setWindowIcon(window.style().standardIcon(QStyle.SP_BrowserReload))
        self.set_disconnected_ui("Click on Connect to establish a connection")

    def set_disconnected_ui(self, msg: str):
        self.process_list.setDisabled(True)
        self.btn_connect.setDisabled(False)
        self.statusbar.showMessage(f"Disconnected. {msg}")

    def set_connected_ui(self):
        self.process_list.setDisabled(False)
        self.btn_connect.setDisabled(True)
        self.statusbar.showMessage("Connection established.")

    def handle_output(self, output: OutputEvent):
        self.tabs_output.append_output(output.uid, output.output)
Пример #20
0
    def _initCentralWidget(self):
        centralView = QWidget()
        centralView.setLayout(QHBoxLayout())
        centralView.layout().setSpacing(0)
        centralView.layout().setContentsMargins(0, 0, 0, 0)

        self.browser = Browser(self.document, self.subject, self.powermode)

        centralView.layout().addWidget(self.browser)

        canvas = QSplitter()
        canvas.setFrameStyle(QFrame.NoFrame | QFrame.Plain)
        canvas.setOrientation(Qt.Vertical)
        canvas.setRubberBand(-1)
        self.editor = Editor(self.subject, self.powermode)
        self.console = Console(self.subject)

        canvas.addWidget(self.editor)
        canvas.addWidget(self.console)

        centralView.layout().addWidget(canvas)
        canvas.setSizes([4, 4, 1, 1])

        self.setCentralWidget(centralView)
Пример #21
0
qw_tree = QTreeWidget()
qw_tree.setHeaderLabels(["test"])


def tree_item_clicked(item, column):
    print(item, column, item.type())
    # TreeがクリックされたときにTextEditを切り替える
    qw_stack.setCurrentIndex(item.type())


for i in range(3):
    text = 'page No.' + str(i + 1)
    qw_tree_item = QTreeWidgetItem([text], type=i)
    qw_tree.addTopLevelItem(qw_tree_item)

    qw_text_edit = QTextEdit()
    qw_text_edit.append(text)

    stack_idx = qw_stack.addWidget(qw_text_edit)
    # print(stack_idx)

# Treeがクリックされたときに呼ばれる関数を登録
qw_tree.itemClicked.connect(tree_item_clicked)

qw_splitter = QSplitter()
qw_splitter.addWidget(qw_tree)
qw_splitter.addWidget(qw_stack)

qw_splitter.show()

sys.exit(app.exec_())
Пример #22
0
class MainWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.workbenchModel = WorkbenchModel(self)
        self.graph = flow.dag.OperationDag()
        self.operationMenu = OperationMenu()
        self.frameInfoPanel = FramePanel(parent=self,
                                         w=self.workbenchModel,
                                         opModel=self.operationMenu.model())
        self.workbenchView = WorkbenchView()
        self.workbenchView.setModel(self.workbenchModel)
        self.workbenchModel.emptyRowInserted.connect(
            self.workbenchView.startEditNoSelection)

        tabs = QTabWidget(self)

        attributeTab = AttributePanel(self.workbenchModel, self)
        chartsTab = ViewPanel(self.workbenchModel, self)
        self.graphScene = GraphScene(self)
        self._flowView = GraphView(self.graphScene, self)
        self.controller = GraphController(self.graph, self.graphScene,
                                          self._flowView, self.workbenchModel,
                                          self)

        tabs.addTab(attributeTab, '&Attribute')
        tabs.addTab(chartsTab, '&Visualise')
        tabs.addTab(self._flowView, 'F&low')
        self.__curr_tab = tabs.currentIndex()

        self.__leftSide = QSplitter(Qt.Vertical)
        self.__leftSide.addWidget(self.frameInfoPanel)
        self.__leftSide.addWidget(self.workbenchView)

        # layout = QHBoxLayout()
        # layout.addWidget(leftSplit, 2)
        # layout.addWidget(tabs, 8)
        splitter = QSplitter(Qt.Horizontal, self)
        splitter.addWidget(self.__leftSide)
        splitter.addWidget(tabs)
        layout = QHBoxLayout(self)
        layout.addWidget(splitter)

        tabs.currentChanged.connect(self.changeTabsContext)
        self.workbenchView.selectedRowChanged[str, str].connect(
            attributeTab.onFrameSelectionChanged)
        self.workbenchView.selectedRowChanged[str, str].connect(
            chartsTab.onFrameSelectionChanged)
        self.workbenchView.selectedRowChanged[str, str].connect(
            self.frameInfoPanel.onFrameSelectionChanged)
        self.workbenchView.rightClick.connect(self.createWorkbenchPopupMenu)

    @Slot(int)
    def changeTabsContext(self, tab_index: int) -> None:
        if tab_index == 2:
            self.__leftSide.replaceWidget(0, self.operationMenu)
            self.frameInfoPanel.hide()
            self.operationMenu.show()
            self.__curr_tab = 2
        elif self.__curr_tab == 2 and tab_index != 2:
            self.__leftSide.replaceWidget(0, self.frameInfoPanel)
            self.operationMenu.hide()
            self.frameInfoPanel.show()
            self.__curr_tab = tab_index

    @Slot(QModelIndex)
    def createWorkbenchPopupMenu(self, index: QModelIndex) -> None:
        # Create a popup menu when workbench is right-clicked over a valid frame name
        # Menu display delete and remove options
        frameName: str = index.data(Qt.DisplayRole)
        pMenu = QMenu(self)
        # Reuse MainWindow actions
        csvAction = self.parentWidget().aWriteCsv
        pickleAction = self.parentWidget().aWritePickle
        # Set correct args for the clicked row
        csvAction.setOperationArgs(w=self.workbenchModel, frameName=frameName)
        pickleAction.setOperationArgs(w=self.workbenchModel,
                                      frameName=frameName)
        deleteAction = QAction('Remove', pMenu)
        deleteAction.triggered.connect(
            lambda: self.workbenchModel.removeRow(index.row()))
        pMenu.addActions([csvAction, pickleAction, deleteAction])
        pMenu.popup(QtGui.QCursor.pos())

    def createNewFlow(self, graph: flow.dag.OperationDag) -> None:
        self.graph = graph
        oldScene = self._flowView.scene()
        self.graphScene = GraphScene(self)
        self._flowView.setScene(self.graphScene)
        oldScene.deleteLater()
        self.controller.deleteLater()
        self.controller = GraphController(self.graph, self.graphScene,
                                          self._flowView, self.workbenchModel,
                                          self)
Пример #23
0
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.resize(1280, 720)
        self.central_widget = QWidget(MainWindow)

        self.splitter = QSplitter(self.central_widget)
        self.splitter.setChildrenCollapsible(False)
        self.splitter.setOpaqueResize(True)

        self.main_grid_layout = QGridLayout(self.central_widget)
        self.main_grid_layout.addWidget(self.splitter)
        self.main_grid_layout.setSizeConstraint(QLayout.SetDefaultConstraint)

        self.tab_widget = QTabWidget(self.central_widget)
        self.tab_widget.setMinimumSize(QSize(500, 0))
        self._set_up_component_tree_view()
        self.splitter.addWidget(self.tab_widget)

        self._set_up_3d_view()

        MainWindow.setCentralWidget(self.central_widget)

        self._set_up_menus(MainWindow)
        self.tab_widget.setCurrentIndex(0)
        QMetaObject.connectSlotsByName(MainWindow)
        self.splitter.setStretchFactor(0, 0)
        self.splitter.setStretchFactor(1, 1)

    def _set_up_3d_view(self):
        self.sceneWidget.setMinimumSize(QSize(600, 0))
        self.splitter.addWidget(self.sceneWidget)

    def _set_up_component_tree_view(self):
        self.sceneWidget = InstrumentView(self.splitter)
        self.component_tree_view_tab = ComponentTreeViewTab(
            scene_widget=self.sceneWidget, parent=self
        )
        self.tab_widget.addTab(self.component_tree_view_tab, "")

    def _set_up_menus(self, MainWindow: QObject):
        self.menu_bar = QMenuBar()
        self.menu_bar.setGeometry(QRect(0, 0, 1280, 720))
        self.file_menu = QMenu(self.menu_bar)
        MainWindow.setMenuBar(self.menu_bar)
        self.status_bar = QStatusBar(MainWindow)
        MainWindow.setStatusBar(self.status_bar)
        self.open_json_file_action = QAction(MainWindow)
        self.open_json_file_action.setShortcut(QKeySequence("Ctrl+O"))
        self.export_to_filewriter_JSON_action = QAction(MainWindow)
        self.export_to_filewriter_JSON_action.setShortcut(QKeySequence("Ctrl+S"))
        self.file_menu.addAction(self.open_json_file_action)
        self.file_menu.addAction(self.export_to_filewriter_JSON_action)

        self.view_menu = QMenu(self.menu_bar)
        self.show_action_labels = QAction(MainWindow)
        self.show_action_labels.setCheckable(True)
        self.show_action_labels.setChecked(True)
        self.simple_tree_view = QAction(MainWindow)
        self.simple_tree_view.setCheckable(True)
        self.about_window = QAction(MainWindow)
        self.view_menu.addAction(self.about_window)
        self.view_menu.addAction(self.show_action_labels)
        self.view_menu.addAction(self.simple_tree_view)

        self.menu_bar.addAction(self.file_menu.menuAction())
        self.menu_bar.addAction(self.view_menu.menuAction())
        self._set_up_titles(MainWindow)

    def _set_up_titles(self, MainWindow):
        MainWindow.setWindowTitle("NeXus Constructor")
        self.tab_widget.setTabText(
            self.tab_widget.indexOf(self.component_tree_view_tab), "Nexus Structure"
        )
        self.file_menu.setTitle("File")
        self.open_json_file_action.setText("Open File writer JSON file")
        self.export_to_filewriter_JSON_action.setText("Export to File writer JSON")

        self.view_menu.setTitle("View")
        self.show_action_labels.setText("Show Button Labels")
        self.simple_tree_view.setText("Use Simple Tree Model View")
        self.about_window.setText("About")

        self.menu_bar.setNativeMenuBar(False)
Пример #24
0
# ---------------------------
# Splitterにwidgetをインデックスで指定した位置に追加する
# ---------------------------
import sys
from PySide2.QtWidgets import QApplication, QSplitter, QTextEdit

app = QApplication(sys.argv)

qw_text_edit_left = QTextEdit()
qw_text_edit_left.append('left')

qw_text_edit_right = QTextEdit()
qw_text_edit_right.append('right')

qw_splitter = QSplitter()
qw_splitter.addWidget(qw_text_edit_left)
qw_splitter.addWidget(qw_text_edit_right)

qw_text_edit_center = QTextEdit()
qw_text_edit_center.append('center')
qw_splitter.insertWidget(1, qw_text_edit_center) # index 1 の位置にwidgetを追加

print(qw_splitter.indexOf(qw_text_edit_left))
print(qw_splitter.indexOf(qw_text_edit_center))
print(qw_splitter.indexOf(qw_text_edit_right))

qw_splitter.show()

sys.exit(app.exec_())
Пример #25
0
class ManualFittingPanel(QDialog):
    manual_fitting_finished = Signal(SSUResult)

    def __init__(self, parent=None):
        super().__init__(parent=parent, f=Qt.Window)
        self.setWindowTitle(self.tr("SSU Manual Fitting Panel"))
        self.control_widgets = []
        self.input_widgets = []
        self.last_task = None
        self.last_result = None
        self.async_worker = AsyncWorker()
        self.async_worker.background_worker.task_succeeded.connect(
            self.on_task_succeeded)
        self.initialize_ui()
        self.normal_msg = QMessageBox(self)
        self.chart_timer = QTimer()
        self.chart_timer.timeout.connect(self.update_chart)
        self.chart_timer.setSingleShot(True)

    def initialize_ui(self):
        self.main_layout = QGridLayout(self)

        self.chart_group = QGroupBox(self.tr("Chart"))
        self.chart_layout = QGridLayout(self.chart_group)
        self.chart = MixedDistributionChart(show_mode=True, toolbar=False)
        self.chart_layout.addWidget(self.chart)

        self.control_group = QGroupBox(self.tr("Control"))
        self.control_layout = QGridLayout(self.control_group)
        self.try_button = QPushButton(qta.icon("mdi.test-tube"),
                                      self.tr("Try"))
        self.try_button.clicked.connect(self.on_try_clicked)
        self.control_layout.addWidget(self.try_button, 1, 0, 1, 4)
        self.confirm_button = QPushButton(qta.icon("ei.ok-circle"),
                                          self.tr("Confirm"))
        self.confirm_button.clicked.connect(self.on_confirm_clicked)
        self.control_layout.addWidget(self.confirm_button, 2, 0, 1, 4)

        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.addWidget(self.chart_group)
        self.splitter.addWidget(self.control_group)
        self.main_layout.addWidget(self.splitter)

    def change_n_components(self, n_components: int):
        for widget in self.control_widgets:
            self.control_layout.removeWidget(widget)
            widget.hide()
        self.control_widgets.clear()
        self.input_widgets.clear()

        widgets = []
        slider_range = (0, 1000)
        input_widgets = []
        mean_range = (-5, 15)
        std_range = (0.0, 10)
        weight_range = (0, 10)
        names = [self.tr("Mean"), self.tr("STD"), self.tr("Weight")]
        ranges = [mean_range, std_range, weight_range]
        slider_values = [500, 100, 100]
        input_values = [0.0, 1.0, 1.0]

        for i in range(n_components):
            group = QGroupBox(f"C{i+1}")
            group.setMinimumWidth(200)
            group_layout = QGridLayout(group)
            inputs = []
            for j, (name, range_, slider_value, input_value) in enumerate(
                    zip(names, ranges, slider_values, input_values)):
                label = QLabel(name)
                slider = QSlider()
                slider.setRange(*slider_range)
                slider.setValue(slider_value)
                slider.setOrientation(Qt.Horizontal)
                input_ = QDoubleSpinBox()
                input_.setRange(*range_)
                input_.setDecimals(3)
                input_.setSingleStep(0.01)
                input_.setValue(input_value)
                slider.valueChanged.connect(self.on_value_changed)
                input_.valueChanged.connect(self.on_value_changed)
                slider.valueChanged.connect(
                    lambda x, input_=input_, range_=range_: input_.setValue(
                        x / 1000 * (range_[-1] - range_[0]) + range_[0]))
                input_.valueChanged.connect(
                    lambda x, slider=slider, range_=range_: slider.setValue(
                        (x - range_[0]) / (range_[-1] - range_[0]) * 1000))

                group_layout.addWidget(label, j, 0)
                group_layout.addWidget(slider, j, 1)
                group_layout.addWidget(input_, j, 2)
                inputs.append(input_)

            self.control_layout.addWidget(group, i + 5, 0, 1, 4)
            widgets.append(group)
            input_widgets.append(inputs)

        self.control_widgets = widgets
        self.input_widgets = input_widgets

    @property
    def n_components(self) -> int:
        return len(self.input_widgets)

    @property
    def expected(self):
        reference = []
        weights = []
        for i, (mean, std, weight) in enumerate(self.input_widgets):
            reference.append(
                dict(mean=mean.value(), std=std.value(), skewness=0.0))
            weights.append(weight.value())
        weights = np.array(weights)
        fractions = weights / np.sum(weights)
        return reference, fractions

    def show_message(self, title: str, message: str):
        self.normal_msg.setWindowTitle(title)
        self.normal_msg.setText(message)
        self.normal_msg.exec_()

    def show_info(self, message: str):
        self.show_message(self.tr("Info"), message)

    def show_warning(self, message: str):
        self.show_message(self.tr("Warning"), message)

    def show_error(self, message: str):
        self.show_message(self.tr("Error"), message)

    def on_confirm_clicked(self):
        if self.last_result is not None:
            for component, (mean, std,
                            weight) in zip(self.last_result.components,
                                           self.input_widgets):
                mean.setValue(component.logarithmic_moments["mean"])
                std.setValue(component.logarithmic_moments["std"])
                weight.setValue(component.fraction * 10)
            self.manual_fitting_finished.emit(self.last_result)

            self.last_result = None
            self.last_task = None
            self.try_button.setEnabled(False)
            self.confirm_button.setEnabled(False)
            self.hide()

    def on_task_failed(self, info: str, task: SSUTask):
        self.show_error(info)

    def on_task_succeeded(self, result: SSUResult):
        self.chart.show_model(result.view_model)
        self.last_result = result
        self.confirm_button.setEnabled(True)

    def on_try_clicked(self):
        if self.last_task is None:
            return
        new_task = copy.copy(self.last_task)
        reference, fractions = self.expected
        initial_guess = BaseDistribution.get_initial_guess(
            self.last_task.distribution_type, reference, fractions=fractions)
        new_task.initial_guess = initial_guess
        self.async_worker.execute_task(new_task)

    def on_value_changed(self):
        self.chart_timer.stop()
        self.chart_timer.start(10)

    def update_chart(self):
        if self.last_task is None:
            return
        reference, fractions = self.expected
        for comp_ref in reference:
            if comp_ref["std"] == 0.0:
                return
        # print(reference)
        initial_guess = BaseDistribution.get_initial_guess(
            self.last_task.distribution_type, reference, fractions=fractions)
        result = SSUResult(self.last_task, initial_guess)
        self.chart.show_model(result.view_model, quick=True)

    def setup_task(self, task: SSUTask):
        self.last_task = task
        self.try_button.setEnabled(True)
        if self.n_components != task.n_components:
            self.change_n_components(task.n_components)
        reference, fractions = self.expected
        initial_guess = BaseDistribution.get_initial_guess(
            task.distribution_type, reference, fractions=fractions)
        result = SSUResult(task, initial_guess)
        self.chart.show_model(result.view_model, quick=False)
Пример #26
0
class ProjectWidget(QSplitter):

    treeCurrentItemChanged = Signal(Container)

    def __init__(self, parent, f=...):
        super().__init__(parent, f)
        self.project = Project()
        self.current_tree_item: Optional[Container] = None

        self.main_splitter = self  # inheritance from QSplitter may not be preserved
        self.main_splitter.setOrientation(Qt.Horizontal)
        self.left_splitter = QSplitter(Qt.Vertical)
        self.right_splitter = QSplitter(Qt.Vertical)
        self.main_splitter.addWidget(self.left_splitter)
        self.main_splitter.addWidget(self.right_splitter)

        # self.w_parameters = ParametersWidget(self.project.bundle)
        self.w_parameters_tree = QTreeView()
        self.w_parameters_tree.setModel(self.project.parameters_model)
        delegate = ParametersTableDelegate()
        self.w_parameters_tree.setItemDelegate(delegate)
        self.selection_model_parameters = self.w_parameters_tree.selectionModel(
        )
        self.selection_model_parameters.currentRowChanged.connect(
            self._on_tree_row_changed)

        self.w_curves_tree = QTreeView()
        self.w_curves_tree.setModel(self.project.curves_model)
        delegate = CurvesTableDelegate()
        self.w_curves_tree.setItemDelegate(delegate)
        self.selection_model_curves = self.w_curves_tree.selectionModel()
        self.selection_model_curves.currentRowChanged.connect(
            self._on_tree_row_changed)

        self.w_light_plot = LightPlotWidget(self.project.curves_model)
        self.w_rv_plot = RvPlotWidget(self.project.curves_model)

        self.left_splitter.addWidget(self.w_parameters_tree)
        self.left_splitter.addWidget(self.w_curves_tree)

        self.right_splitter.addWidget(self.w_light_plot)
        self.right_splitter.addWidget(self.w_rv_plot)

    def resizeEvent(self, arg__1: PySide2.QtGui.QResizeEvent):
        super().resizeEvent(arg__1)

    def save_session(self, settings: QSettings):
        settings.beginGroup('project_splitter')
        settings.setValue('horiz', self.main_splitter.saveState())
        settings.setValue('vert_trees', self.left_splitter.saveState())
        settings.setValue('vert_plots', self.right_splitter.saveState())
        settings.endGroup()
        settings.beginGroup('tree_columns')
        settings.setValue('params',
                          self.w_parameters_tree.header().saveState())
        settings.setValue('curves', self.w_curves_tree.header().saveState())
        settings.endGroup()

    def restore_session(self, settings: QSettings):
        settings.beginGroup('project_splitter')
        try:
            self.main_splitter.restoreState(settings.value("horiz"))
            self.left_splitter.restoreState(settings.value("vert_trees"))
            self.right_splitter.restoreState(settings.value("vert_plots"))
        except AttributeError:
            pass
        settings.endGroup()
        settings.beginGroup('tree_columns')
        try:
            self.w_parameters_tree.header().restoreState(
                settings.value("params"))
            self.w_curves_tree.header().restoreState(settings.value("curves"))
        except AttributeError:
            pass
        settings.endGroup()

    @Slot(QModelIndex, QModelIndex)
    def _on_tree_row_changed(self, current: QModelIndex,
                             previous: QModelIndex):
        model = current.model()

        prev_item, prev_column = model.item_and_column(previous)
        curr_item, curr_column = model.item_and_column(current)

        logger().info(f'Selection changed {prev_item} -> {curr_item}')
        self.current_tree_item = curr_item

        self.treeCurrentItemChanged.emit(curr_item)

    @Slot()
    def add_lc(self):
        self.project.add_lc()

    @Slot()
    def add_rv(self):
        self.project.add_rv()

    @Slot()
    def del_curve(self):
        to_del = self.current_tree_item
        while to_del is not None and not isinstance(to_del, CurveContainer):
            to_del = to_del.parent()
        if QMessageBox.question(self, 'Deleting Curve Confirmation', f'Delete the curve "{to_del.objectName()}"?') \
                != QMessageBox.Yes:
            return
        self.project.delete_curve(to_del)
Пример #27
0
    def _init_widgets(self):

        # Subclass QPlainTextEdit
        class SmartPlainTextEdit(QPlainTextEdit):
            def __init__(self, parent, callback):
                super(SmartPlainTextEdit, self).__init__(parent)
                self._callback = callback

            def keyPressEvent(self, event):
                super(SmartPlainTextEdit, self).keyPressEvent(event)
                if event.key() == Qt.Key_Return:
                    if not (event.modifiers() == Qt.ShiftModifier):
                        self._callback()

        # Gui stuff
        splitter = QSplitter(self)

        output_wid = QWidget(splitter)
        output_wid.setLayout(QVBoxLayout(output_wid))
        self._history_text = QTextEdit(output_wid)
        self._history_text.setCurrentFont(QFont('Times', 10))
        self._history_text.setFontFamily('Source Code Pro')
        output_wid.layout().addWidget(QLabel("History"))
        output_wid.layout().addWidget(self._history_text)

        input_wid = QWidget(splitter)
        input_wid.setLayout(QVBoxLayout(input_wid))
        self._command = SmartPlainTextEdit(input_wid, self._send_command)
        input_wid.layout().addWidget(QLabel("Command"))
        input_wid.layout().addWidget(
            QLabel(
                "Press Enter to send the command. Press Shift + Enter to add a newline."
            ))
        input_wid.layout().addWidget(self._command)

        splitter.setOrientation(Qt.Vertical)
        splitter.addWidget(output_wid)
        splitter.addWidget(input_wid)
        splitter.setSizes([300, 100])

        send_button = QPushButton(self, text="Send")
        clear_history_button = QPushButton(self, text="Clear History")
        interact_button = QPushButton(self, text="Interact")
        send_button.clicked.connect(self._send_command)
        clear_history_button.clicked.connect(self._history_text.clear)
        interact_button.clicked.connect(
            lambda: self.initialize(self.workspace.instance.img_name))
        buttons = QSplitter(self)
        buttons.addWidget(interact_button)
        buttons.addWidget(clear_history_button)
        buttons.addWidget(send_button)
        buttons.setSizes([100, 200, 600])

        self.setLayout(QVBoxLayout(self))
        self.layout().addWidget(splitter)
        self.layout().addWidget(buttons)
Пример #28
0
    def init_ui(self):   
        self.hbox = QHBoxLayout() # holds widgets for refresh button, desired address, and size to grab
        self.vbox = QVBoxLayout() # main container
        self.lower_hbox = QSplitter() # holds memory views
        self.lower_container = QHBoxLayout() # holds  lower_hbox and the endian_vbox
        self.endian_vbox = QVBoxLayout()

        self.hbox.addWidget(QLabel('Address:'))
        self.address = QLineEdit()
        self.hbox.addWidget(self.address)

        self.hbox.addWidget(QLabel('Size:'))
        self.size = QLineEdit()
        self.hbox.addWidget(self.size)

        self.hbox.addWidget(QLabel('Grouping:'))
        self.grouping = QComboBox()
        self.grouping.addItems(['1','2','4','8'])
        self.hbox.addWidget(self.grouping)

        self.search = QPushButton('Search')
        self.search.clicked.connect(lambda:self.find(self.address.text(), constants['block_size']))
        self.hbox.addWidget(self.search)

        self.refresh = QPushButton('Refresh')
        self.refresh.clicked.connect(lambda:self.grab_data(val=self.address.text(), size=self.size.text(), grouping=self.grouping.currentText(), refresh=True))
        self.hbox.addWidget(self.refresh)

        self.save = QPushButton('Save')
        self.save.clicked.connect(lambda: self.save_to_file())
        self.disable_save(self.qmp.running)
        self.qmp.stateChanged.connect(self.disable_save)
        self.hbox.addWidget(self.save)

        self.auto_refresh = QCheckBox('Auto Refresh')
        self.auto_refresh.setCheckState(Qt.CheckState.Checked)
        self.auto_refresh.stateChanged.connect(self.auto_refresh_check)
        self.hbox.addWidget(self.auto_refresh)

        self.vbox.addLayout(self.hbox)
        
        # textbox for addresses
        self.addresses = QTextEdit()
        self.addresses.setReadOnly(True)
        self.addresses.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.addresses.setLineWrapMode(QTextEdit.NoWrap)
        self.addresses.setCurrentFont(QFont('Courier New'))
        self.addresses.setGeometry(0,0,150,500)
        self.lower_hbox.addWidget(self.addresses)

        # textbox for hex display of memory
        self.mem_display = QTextEdit()
        self.mem_display.setReadOnly(True)
        self.mem_display.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.mem_display.setLineWrapMode(QTextEdit.NoWrap)
        self.mem_display.setCurrentFont(QFont('Courier New'))
        self.mem_display.setGeometry(0,0,600,500)
        self.lower_hbox.addWidget(self.mem_display)
        
        # textbox for char display of memory
        self.chr_display = QTextEdit()
        self.chr_display.setReadOnly(True)
        self.chr_display.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.chr_display.setLineWrapMode(QTextEdit.NoWrap)
        self.chr_display.setCurrentFont(QFont('Courier New'))
        self.mem_display.setGeometry(0,0,400,500)
        self.lower_hbox.addWidget(self.chr_display)

        self.lower_container.addWidget(self.lower_hbox)

        self.mem_display.verticalScrollBar().valueChanged.connect(self.addresses.verticalScrollBar().setValue) #synchronizes addresses's scroll bar to mem_display's 
        self.addresses.verticalScrollBar().valueChanged.connect(self.mem_display.verticalScrollBar().setValue) #synchronizes mem_display's scroll to addresses's, allowing for searching for addresses to scrol mem_display to the desired point 
        self.chr_display.verticalScrollBar().valueChanged.connect(self.mem_display.verticalScrollBar().setValue)  
        self.mem_display.verticalScrollBar().valueChanged.connect(self.chr_display.verticalScrollBar().setValue)  

        self.chr_display.verticalScrollBar().valueChanged.connect(self.handle_scroll) # allows us to do scrolling

        # setting up endiannes selection buttons
        self.little = QRadioButton("Little Endian")
        self.little.click()
        self.little.clicked.connect(lambda:self.change_endian(Endian.little))
        self.big = QRadioButton("Big Endian")
        self.big.clicked.connect(lambda:self.change_endian(Endian.big))
        self.endian_vbox.addWidget(self.little)
        self.endian_vbox.addWidget(self.big)
        self.endian_vbox.addSpacing(400)

        self.lower_container.addLayout(self.endian_vbox)

        self.vbox.addLayout(self.lower_container)
        self.vbox.setSpacing(10)
        self.setLayout(self.vbox)
        self.setWindowTitle("Memory Dump")
        self.setGeometry(100, 100, 1550, 500)
Пример #29
0
class MainWindow(QMainWindow):
    forum = Forum(id=-1, name="UNKNOWN")
    topics = []
    forum_model = ForumModel()
    day_model = DayModel()

    def __init__(self):
        QMainWindow.__init__(self)
        self.date_view = QListView()
        self.forum_view = QListView()
        self.contentMax = 0
        self.ioloop = asyncio.get_event_loop()
        icon = QIcon()
        icon.addPixmap(QPixmap(FAVICON_ICO), QIcon.Normal)
        self.setWindowIcon(icon)
        self.setWindowTitle(APP_TITLE)

        self.settings = QSettings('karoStudio', 'nnm-stats')
        self.resize(self.settings.value('main/size', QSize(640, 480)))
        self.move(self.settings.value('main/pos', QPoint(200, 200)))

        self.splitter = QSplitter()
        self.get_menu()

        self.content = QScrollArea()
        self.content.verticalScrollBar().valueChanged.connect(
            self.scrollBarChanged)
        self.content.verticalScrollBar().rangeChanged.connect(
            self.rangeChanged)
        self.torrents_list_view = QWidget()
        layout = QGridLayout()
        self.torrents_list_view.setLayout(layout)
        self.content.setWidget(self.torrents_list_view)
        self.splitter.addWidget(self.content)
        self.splitter.setSizes([160, 350])
        self.setCentralWidget(self.splitter)

        self.timer = QTimer()
        self.timer.singleShot(1600, self.load_task)

    def get_menu(self):
        scroll = QScrollArea(self)
        self.forum_view.setStyleSheet("QListView{font: bold 12px;}")
        self.forum_view.clicked.connect(self.listViewClick)
        self.forum_view.setModel(self.forum_model)

        self.date_view.setStyleSheet("QListView{font: bold 12px;}")
        self.date_view.clicked.connect(self.listViewClick)
        self.date_view.setModel(self.day_model)

        menu_splitter = QSplitter(self)
        menu_splitter.setOrientation(Qt.Vertical)
        menu_splitter.addWidget(self.forum_view)
        menu_splitter.addWidget(self.date_view)
        scroll.setWidget(menu_splitter)
        scroll.setWidgetResizable(True)
        self.splitter.addWidget(scroll)

    def load_task(self):
        self.ioloop.run_until_complete(self.load_forums())

    async def load_forums(self):
        tasks = [asyncio.ensure_future((get_forums("http://nnmclub.to/")))]
        done, pending = await asyncio.wait(tasks, return_when=FIRST_COMPLETED)
        forums = done.pop().result()
        for forum in forums:
            print(forum)
            self.forum_model.add(forum)

    def load_torrents_task(self, forum, start):
        self.ioloop.run_until_complete(self.load_torrents(forum, start))

    async def load_torrents(self, forum, start=0):
        tasks = [asyncio.ensure_future((get_topics(forum, start)))]
        done, pending = await asyncio.wait(tasks, return_when=FIRST_COMPLETED)
        if start == 0:
            self.topics = done.pop().result()
        else:
            self.topics = self.topics + done.pop().result()
        layout = QGridLayout()
        self.topics.sort(key=lambda x: x.likes, reverse=True)
        days = {}
        for i, topic in enumerate(self.topics):
            d = topic.published.date()
            if d in days.keys():
                days[d]['count'] += 1
                days[d]['likes'] += topic.likes
            else:
                days[d] = {'count': 1, 'likes': topic.likes}
            # layout.addWidget(TopicView(topic), i, 0)
        for day in days.keys():
            self.day_model.add(day, days[day])
        self.torrents_list_view = QWidget()
        self.torrents_list_view.setLayout(layout)
        self.content.setWidget(self.torrents_list_view)

    def rangeChanged(self, vert_min, vert_max):
        self.contentMax = vert_max

    def scrollBarChanged(self, value):
        if value == self.contentMax:
            print("LOADING {}".format(self.torrents_list_view.children()))
            self.timer.singleShot(
                1000,
                lambda: self.load_torrents_task(self.forum, len(self.topics)))

    def listViewClick(self, index):
        self.forum = self.forum_model.forums[index.row()]
        self.topics = []
        self.setWindowTitle("{} / {}".format(APP_TITLE, self.forum.name))
        self.day_model.clear()
        # self.timer.singleShot(1000, lambda: self.load_torrents_task(self.forum, 0))
        self.timer.timeout.connect(
            lambda: self.load_torrents_task(self.forum, len(self.topics)))
        self.timer.start(8000)

    def closeEvent(self, event):
        self.settings.setValue('main/size', self.size())
        self.settings.setValue('main/pos', self.pos())
        self.ioloop.close()
Пример #30
0
class MarkdownEditor(QDialog):
    """Markdown editor used to add comments on variants

    On PySide2 5.14+, comments can be edited in Markdown and previewed.

    """

    def __init__(self, parent=None, default_text=""):
        """Init Markdown editor

        :param default_text: Populate source edit with current comment in db.
        :type default_text: <str>
        """
        super().__init__(parent)

        self.setWindowTitle("Cutevariant - " + self.tr("Comment editor"))
        self.setWindowIcon(QIcon(cm.DIR_ICONS + "app.png"))

        main_vlayout = QVBoxLayout()
        # main_vlayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(main_vlayout)

        # Setup edit view
        self.rich_edit = QTextEdit()  # Rich text result
        self.source_edit = QPlainTextEdit()  # Markdown content
        self.source_edit.setPlainText(default_text)

        vlayout = QVBoxLayout()
        vlayout.setSpacing(1)
        vlayout.setContentsMargins(0, 0, 0, 0)
        main_vlayout.addLayout(vlayout)

        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.addWidget(self.source_edit)

        if parse_version(pyside_version) >= parse_version("5.14"):
            # RichText in Markdown is supported starting PySide 5.14
            self.splitter.addWidget(self.rich_edit)
            # self.rich_edit.setStyleSheet(
            #     "QWidget {background-color:'lightgray'}")
            self.rich_edit.setAcceptRichText(True)
            self.rich_edit.setAutoFormatting(QTextEdit.AutoAll)
            self.rich_edit.setReadOnly(True)

            # Update preview with Markdown content
            self.source_edit.textChanged.connect(self.update_rich_text)
            # Auto refresh rich text content now
            self.update_rich_text()

        # Setup toolbar
        self.toolbar = QToolBar()
        self.act_undo = self.toolbar.addAction(self.tr("undo"), self.source_edit.undo)
        self.act_undo.setIcon(FIcon(0xF054C))
        self.act_undo.setShortcut(QKeySequence.Undo)

        self.act_redo = self.toolbar.addAction(self.tr("redo"), self.source_edit.redo)
        self.act_redo.setIcon(FIcon(0xF044E))
        self.act_redo.setShortcut(QKeySequence.Redo)

        self.act_bold = self.toolbar.addAction(
            self.tr("bold"), lambda: self.infix("**")
        )
        self.act_bold.setIcon(FIcon(0xF0264))
        self.act_bold.setShortcut(QKeySequence("CTRL+B"))

        self.act_italic = self.toolbar.addAction(
            self.tr("italic"), lambda: self.infix("*")
        )
        self.act_italic.setIcon(FIcon(0xF0277))
        self.act_italic.setShortcut(QKeySequence("CTRL+I"))

        self.act_heading = self.toolbar.addAction(
            self.tr("insert title"), lambda: self.heading()
        )
        self.act_heading.setShortcut(QKeySequence("CTRL+H"))
        self.act_heading.setIcon(FIcon(0xF0274))

        self.act_unorder_list = self.toolbar.addAction(
            self.tr("insert list item"), self.unorder_list
        )
        self.act_unorder_list.setIcon(FIcon(0xF0279))

        self.act_quote = self.toolbar.addAction(self.tr("quote"), self.quote)
        self.act_quote.setIcon(FIcon(0xF027E))

        self.act_link = self.toolbar.addAction(self.tr("insert link"), self.link)
        self.act_link.setIcon(FIcon(0xF0339))

        vlayout.addWidget(self.toolbar)
        vlayout.addWidget(self.splitter)

        # Add buttons
        buttons = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        main_vlayout.addWidget(buttons)

    def update_rich_text(self):
        """Update preview with Markdown content

        .. warning:: Depends PySide Qt5.14
        """
        self.rich_edit.setMarkdown(self.source_edit.toPlainText())

    def infix(self, prefix: str, suffix=None):
        """Add tags before and after the selected text"""
        if suffix is None:
            suffix = prefix

        cursor = self.source_edit.textCursor()

        if not cursor.hasSelection():
            cursor.insertText(f"{prefix}Text{suffix}")
            return

        start = cursor.selectionStart()
        end = cursor.selectionEnd()

        # must be made in 1 step to avoid flooding the history (do/undo)
        text = self.source_edit.toPlainText()
        cursor.insertText(prefix + text[start:end] + suffix)

    def heading(self):
        """Add header tag before selected text"""
        cursor = self.source_edit.textCursor()

        if not cursor.hasSelection():
            cursor.insertText("# Title")
            return

        start = cursor.selectionStart()
        cursor.setPosition(start)
        cursor.insertText("# ")

    def unorder_list(self):
        """Add list tag before selected text"""
        cursor = self.source_edit.textCursor()

        if not cursor.hasSelection():
            cursor.insertText("- List item")
            return

        start = cursor.selectionStart()
        cursor.setPosition(start)
        cursor.insertText("- ")

    def quote(self):
        """Quote the selected text"""
        cursor = self.source_edit.textCursor()

        if not cursor.hasSelection():
            cursor.insertText("> text")
            return

        start = cursor.selectionStart()
        end = cursor.selectionEnd()

        # must be made in 1 step to avoid flooding the history (do/undo)
        text = self.source_edit.toPlainText()
        cursor.insertText("\n> %s\n" % text[start:end])

    def link(self):
        """Format selected text into URL link"""
        cursor = self.source_edit.textCursor()

        if not cursor.hasSelection():
            cursor.insertText("[text](url)")
            return

        start = cursor.selectionStart()
        end = cursor.selectionEnd()

        # must be made in 1 step to avoid flooding the history (do/undo)
        text = self.source_edit.toPlainText()
        cursor.insertText("[text](%s)" % text[start:end])

    def toPlainText(self):
        """Get source text from the current comment

        Used to save the comment in the database by variant_view plugin.
        """
        return self.source_edit.toPlainText()
Пример #31
0
 def testGetRange(self):
     splitter = QSplitter()
     _min, _max = splitter.getRange(0)
     self.assert_(isinstance(_min, int))
     self.assert_(isinstance(_max, int))