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()
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()
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_()
def testGetRange(self): splitter = QSplitter() _min, _max = splitter.getRange(0) self.assert_(isinstance(_min, int)) self.assert_(isinstance(_max, int))
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()
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)
def getSplitter(self): splitter = QSplitter() splitter.addWidget(self.editBox) return splitter
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)
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()
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)
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()
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() '''
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()
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)
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)
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())
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)
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)
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)
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_())
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)
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)
# --------------------------- # 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_())
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)
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)
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)
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)
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()
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()