class SummarySection(QTabWidget): """ Represents the summary section. Takes reply data from main ui and updates it's tabs Keeps track of cumulative summary data. """ def __init__(self, parent=None): super(SummarySection, self).__init__(parent) self.text_edit_summary = QTextEdit() self.text_edit_summary.setReadOnly(True) self.addTab(self.text_edit_summary, "Output") self.ball_grid = BallGrid(30, 30, 2) self.addTab(self.ball_grid, "Led View") self.tab_summary = SummaryTab() self.addTab(self.tab_summary, "Summary") #some private fields, keep track of accumulated summary data self.current_ip = 0 #we take reply data for ips in order self.sent_packets = 0 self.received_packets = 0 self.average_delay = 0 def setIPs(self, ips): #this indicates the start of a new ping, could be treated #as a pingStarted signal self.current_ip = 0 self.sent_packets = 0 self.received_packets = 0 self.average_delay = 0 self.text_edit_summary.clear() self.ball_grid.layoutForIps(ips) self.tab_summary.zeroOut() def takeReplyData(self, replyData, sentPackets): #sent packets is passed in as extra """ Update the output text area with the replyData string representation Set proper widget states on the BallGrid Calculate summaries cumulatively and set them for display on the summary tab """ self.text_edit_summary.append(str(replyData)) packets_lost = replyData.packets_lost if packets_lost: self.ball_grid.setStateAt(self.current_ip, BallWidget.UNREACHABLE) else: self.ball_grid.setStateAt(self.current_ip, BallWidget.REACHABLE) self.current_ip += 1 self.sent_packets += sentPackets self.received_packets = self.sent_packets - replyData.packets_lost self.average_delay += (replyData.rtt / self.current_ip) #the current_ip reflects the overall number of replies summary_data = SummaryData(self.sent_packets, self.received_packets, self.average_delay) self.tab_summary.setSummaryData(summary_data) def pingingStoppedHandler(self): self.ball_grid.pingingCancelledHandler()
class Console(): def __init__(self, targetLayoutContainer): self.textarea = QTextEdit() self.commits = QListWidget() self.commits.addAction(QAction('Rollback to this revision', self.commits, triggered=self.rollback)) self.commits.setContextMenuPolicy(Qt.ActionsContextMenu) self.widget = QTabWidget() self.widget.addTab(self.textarea, 'Log') self.widget.addTab(self.commits, 'Commits') targetLayoutContainer.addWidget(self.widget) def color(self, module, function, color, *args): print module, function, args prettyString = '<font color="' + color + '"><b>', module, '</b><i>::', function, '</i> --> ', ''.join(args), '</font>' self.textarea.append(''.join(prettyString)) def info(self, module, function, *args): print module, function, args prettyString = '<font color="black"><b>', module, '</b><i>::', function, '</i> --> ', ''.join(args), '</font>' self.textarea.append(''.join(prettyString)) def error(self, module, function, *args): print module, function, args prettyString = '<font color="red"><b>', module, '</b><i>::', function, '</i> --> ', ''.join(args), '</font>' self.textarea.append(''.join(prettyString)) def warn(self, module, function, *args): print module, function, args prettyString = '<font color="#BE9900"><b>', module, '</b><i>::', function, '</i> --> ', ''.join(args), '</font>' self.textarea.append(''.join(prettyString)) def set_commits(self, commits): self.commits.clear() for commit in commits: self.commits.addItem(commit) def clear(self): self.textarea.clear() def rollback(self): targetCommit = self.commits.currentItem().text().split(' ')[0] if QMessageBox.warning(None, 'Rollback to commit?', 'All commits after ' + targetCommit + ' will be lost, proceed?', QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes: rollback(targetCommit)
class CardBordAIApp(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("CardBord AI Demo ver0.10") self.operator = Operator() self.setupUi() self.operator.speach.connect(self.speak_ai) self.init() def setupUi(self): self.mainlayout = QVBoxLayout(self) self.title = QLabel("CardBord Operator", self) self.mainlayout.addWidget(self.title) self.user_txtarea = QTextEdit(self) self.ai_txtarea = QTextEdit(self) self.users_edit_title = QLabel("質問に答えてください") self.users_edit = QLineEdit("", self) self.txtarea_layout = QHBoxLayout() self.txtarea_layout.addWidget(self.user_txtarea) self.txtarea_layout.addWidget(self.ai_txtarea) self.mainlayout.addLayout(self.txtarea_layout) self.mainlayout.addWidget(self.users_edit_title) self.mainlayout.addWidget(self.users_edit) self.send_btn = QPushButton("send", self) self.send_btn.setObjectName("send_btn") self.mainlayout.addWidget(self.send_btn) QMetaObject.connectSlotsByName(self) def init(self): # Talkエリアの初期化 self.user_txtarea.clear() self.ai_txtarea.clear() # オペレータ初期化、初期ワード取得 self.operator.init() @Slot() def on_send_btn_clicked(self): # UI処理 user_word = self.users_edit.text() if user_word == "": return None self.users_edit.clear() self.user_txtarea.append(user_word) # サーバー(エンジン)へユーザワードを送信 self.operator.send(user_word) @Slot() def speak_ai(self, word): # AIのトークを表示 # ディレイを使ってテンポを整える def wrapper(): self.ai_txtarea.append(word) QTimer.singleShot(TALK_DELAY, wrapper)
class CustomWidget(QtGui.QMainWindow): def __init__(self): """ Constructor """ QtGui.QMainWindow.__init__(self) self.name = "Custom widget" self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) # TODO: This is ugly, improve it self.iconp = JConfig().icons_path self._createLayout() def _createGui(self): """ Subclasses must override this depending on the elements they want to add self._createOutputTree(), self._createOutputTable(), self._createOutputWindow() and add them to the corresponding layouts. """ raise NotImplementedError def _createToolBar(self, name): """ Subclasses need to define the specific Actions """ self.toolbar = self.addToolBar(name) self.toolbar.setMovable(False) def _createLayout(self): """ This creates the basic layout: Buttons & Outputs """ # Layouts (This is a common disposition) main_layout = QtGui.QVBoxLayout() self.button_layout = QtGui.QHBoxLayout() output_layout = QtGui.QVBoxLayout() # You will need to create your buttons # and add them to your layout like this: # self.button_layout.addWidget(button_1) # Output Layout Inner (QSplitter) # Add as many widgets as you please # They will be ordered vertically and # be resizable by the user # self.splitter.addWidget(self.table_label) # self.splitter.addWidget(...) self.splitter = QSplitter(QtCore.Qt.Vertical) # Nested layouts main_layout.addLayout(self.button_layout) output_layout.addWidget(self.splitter) main_layout.addLayout(output_layout) self.central_widget.setLayout(main_layout) def _createOutputWindow(self): """ Some binary analysis commands will output to this. """ self.output_label = QtGui.QLabel('Output') self.output_window = QTextEdit() self.output_window.setFontPointSize(10) self.output_window.setReadOnly(True) # Save it for later use self.output_window.original_textcolor = self.output_window.textColor() def _createOutputTable(self): """ A vanilla QTableWidget. Callbacks modify its properties, like number of columns, etc. """ self.table_label = QtGui.QLabel('Table Output') self.table = QTableWidget() self.table.setColumnCount(3) self.table.setColumnWidth(0, 100) self.table.setColumnWidth(1, 300) self.table.setColumnWidth(2, 300) self.table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) # Connect signals to slots self.table.customContextMenuRequested.connect(self._tablePopup) self.table.horizontalHeader().sectionDoubleClicked.connect( self._tableHeaderDoubleClicked) self.table.cellDoubleClicked.connect(self._tableCellDoubleClicked) def _createOutputTree(self): """ A QtreeWidget. Initially used to display those pesky dword comparison to immediate values. """ self.tree_label = QtGui.QLabel('Tree Output') self.tree = QTreeWidget() self.tree.setColumnCount(3) self.tree.setColumnWidth(0, 150) self.tree.setColumnWidth(1, 150) self.tree.setColumnWidth(2, 50) # Connect signals to slots self.tree.itemDoubleClicked.connect(self._treeElementDoubleClicked) ################################################################# # GUI Callbacks ################################################################# def _console_output(self, s="", err=False): """ Convenience wrapper """ if err: # Error message err_color = QColor('red') self.output_window.setTextColor(err_color) self.output_window.append(s) # restore original color self.output_window.setTextColor( self.output_window.original_textcolor) else: self.output_window.append(s) def _tableCellDoubleClicked(self, row, col): """ Most of the info displayed in QTableWidgets represent addresses. Default action: try to jump to it. """ it = self.table.item(row, col).text() try: addr = int(it, 16) jump_to_address(addr) except ValueError: self._console_output("[!] That does not look like an address...", err=True) return def _tablePopup(self, pos): """ Popup menu activated clicking the secondary button on the table """ menu = QtGui.QMenu() # Add menu entries delRow = menu.addAction(QIcon(self.iconp + "close.png"), "Delete Row") selItem = menu.addAction(QIcon(self.iconp + "bookmark.png"), "Mark entry") menu.addSeparator() origFunc = menu.addAction(QIcon(self.iconp + "lightning.png"), "Select origin function") destFunc = menu.addAction(QIcon(self.iconp + "flag.png"), "Select destination function") # Get entry clicked action = menu.exec_(self.mapToGlobal(pos)) # Dispatch :) if action == delRow: self.table.removeRow(self.table.currentRow()) elif action == selItem: self.table.currentItem().setBackground(QtGui.QColor('red')) elif action == origFunc: try: addr = self.table.currentItem().text() InfoUI.function_orig_ea = int(addr, 16) except: self._console_output("This does not look like an address...", err=True) elif action == destFunc: try: addr = self.table.currentItem().text() InfoUI.function_dest_ea = int(addr, 16) except: self._console_output("This does not look like an address...", err=True) def _tableHeaderDoubleClicked(self, index): """ Used to sort the contents """ self.table.sortItems(index, order=QtCore.Qt.AscendingOrder) def _treeElementDoubleClicked(self, item, column): """ QTreeWidgetElement callback. Basically it jumps to the selected address in IDA disassembly window. """ try: # Only interested in addresses addr_int = int(item.text(column), 16) jump_to_address(addr_int) # Paint some color item.setBackground(column, QtGui.QColor('green')) except: self._console_output("[!] That does not look like an address...", err=True)
class Dialog(QWidget): def __init__(self, parent=None): super(Dialog, self).__init__(parent) self.setupUI() self.setupConnection() self.threadPool = [] self.totalUids = 0 self.finishedThreadNum = 0 self.realThreadNum = 0 self.excel = Excel() #self.initSearchDb() def setupUI(self): self.pushButton = QPushButton(u"Search", self) #self.testButton = QPushButton(u"Test", self) self.lineEdit = QLineEdit(self) self.textEdit = QTextEdit(self) self.comboBox = QComboBox(self) self.label = QLabel(u"DB:", self) self.progressBar = QProgressBar(self) self.progressBar.setRange(0,1) self.textEdit.setReadOnly(True) self.layout = QVBoxLayout() self.topLayout = QHBoxLayout() self.topLayout.addWidget(self.label) self.topLayout.addWidget(self.comboBox) self.topLayout.addWidget(self.lineEdit) self.topLayout.addWidget(self.pushButton) #self.topLayout.addWidget(self.testButton) #self.testButton.clicked.connect(self.onTestButtonClicked) self.layout.addLayout(self.topLayout) self.layout.addWidget(self.textEdit) self.layout.addWidget(self.progressBar) self.setLayout(self.layout) self.resize(600, 700) self.setWindowTitle(u"Search Data for NCBI") def setupConnection(self): self.pushButton.clicked.connect(self.onButtonClicked) def onButtonClicked(self): if not self.lineEdit.text() or not self.comboBox.count(): QtGui.QMessageBox.information(self, u"Warning", u"Please Set the Search Field") return # disable button self.pushButton.setDisabled(True) dbName = self.comboBox.currentText() fieldName = self.lineEdit.text() self.log("Start searching db: %s and field: %s" % (dbName, fieldName)) # add use history to add all uids to the history server handle = self.entrez.esearch(db=dbName, term=fieldName, usehistory='y') record = self.entrez.read(handle) self.log("All result count %s" % record['Count']) self.totalUids = int(record['Count']) # to get onnly data less than the MAX_COUNT if self.totalUids > MAX_COUNT: ret = QtGui.QMessageBox.question( self, u'Warning', u'result count %s is too large, will only get the %s result \ continue?' % (self.totalUids, MAX_COUNT), QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel ) if ret == QtGui.QMessageBox.Ok: self.totalUids = MAX_COUNT else: return #handle = self.entrez.efetch(db=dbName, id=record['IdList'], rettype='gb') self.finishedThreadNum = 0 WebEnv = record['WebEnv'] QueryKey = record['QueryKey'] global FINISHED_COUNT FINISHED_COUNT = 0 self.progressBar.setValue(0) self.progressBar.setMaximum(self.totalUids) if self.totalUids / RET_MAX_SUMMARY >= MAX_THREAD: self.realThreadNum = MAX_THREAD each_count = self.totalUids/MAX_THREAD startIndex = 0 for i in range(MAX_THREAD - 1): thread = MyThread(startIndex, each_count, dbName, fieldName, WebEnv, QueryKey) thread.finished.connect(self.onThreadFinished) thread.finishedCountChanged.connect(self.onFinishedCountChange) thread.start() self.threadPool.append(thread) startIndex = startIndex + each_count thread = MyThread(startIndex, (self.totalUids-startIndex+1), dbName, fieldName, WebEnv, QueryKey) thread.finished.connect(self.onThreadFinished) thread.finishedCountChanged.connect(self.onFinishedCountChange) self.threadPool.append(thread) thread.start() else: if self.totalUids == RET_MAX_SUMMARY: self.realThreadNum = 1 else: self.realThreadNum = self.totalUids/RET_MAX_SUMMARY + 1 startIndex = 0 for i in range(self.realThreadNum): thread = MyThread(startIndex, RET_MAX_SUMMARY, dbName, fieldName, WebEnv, QueryKey) thread.finished.connect(self.onThreadFinished) thread.finishedCountChanged.connect(self.onFinishedCountChange) self.threadPool.append(thread) thread.start() startIndex = startIndex + RET_MAX_SUMMARY self.log("thread num: %s" % self.realThreadNum) self.log('reading data') self.filename = '%s_%s_output.xls' % (dbName, fieldName) self.excel.setFilename(self.filename) def log(self, context): self.textEdit.append(context) def initSearchDb(self): self.entrez = Entrez self.entrez.email = email self.log("Connect to NCBI") try: handle = self.entrez.einfo() except: QtGui.QMessageBox.warning(self, u"Error", u"Error Connect the WebSite") self.close() record = self.entrez.read(handle) self.log("Get NCBI DataBases:") for name in record['DbList']: self.log('DBName:\t%s' % name) self.comboBox.addItems(record['DbList']) def onThreadFinished(self): self.finishedThreadNum = self.finishedThreadNum + 1 self.log('finished thread %s ' % self.finishedThreadNum) if(self.finishedThreadNum == self.realThreadNum): global all_output heads = all_output[0][0].keys() self.excel.setHead(heads) for values in all_output: for value in values: self.excel.addValues(value) self.excel.save() self.progressBar.setValue(int(self.totalUids)) # clear all thread self.threadPool = [] all_output = [] self.excel.clearValues() self.pushButton.setDisabled(False) self.log("output to file: %s" % self.filename) def onFinishedCountChange(self, num): self.progressBar.setValue(num) def onTestButtonClicked(self): self.finishedThreadNum = 0 self.realThreadNum = 1 self.thread = MyThread(0, 10,"abd","123") self.thread.finished.connect(self.onThreadFinished) self.thread.startIndex()
class TransferPanel(QWidget): ''' Transfer Panel This Panel is the main dialog box for the Dive Computer Transfer GUI ''' def __init__(self, parent=None): super(TransferPanel, self).__init__(parent) self._logbook = None self._logbookName = 'None' self._logbookPath = None self._createLayout() self._readSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _createLayout(self): 'Create the Widget Layout' self._txtLogbook = QLineEdit() self._txtLogbook.setReadOnly(True) self._lblLogbook = QLabel(self.tr('&Logbook File:')) self._lblLogbook.setBuddy(self._txtLogbook) self._btnBrowse = QPushButton('...') self._btnBrowse.clicked.connect(self._btnBrowseClicked) self._btnBrowse.setStyleSheet('QPushButton { min-width: 24px; max-width: 24px; }') self._btnBrowse.setToolTip(self.tr('Browse for a Logbook')) self._cbxComputer = QComboBox() self._lblComputer = QLabel(self.tr('Dive &Computer:')) self._lblComputer.setBuddy(self._cbxComputer) self._btnAddComputer = QPushButton(QPixmap(':/icons/list-add.png'), self.tr('')) self._btnAddComputer.setStyleSheet('QPushButton { min-width: 24px; min-height: 24; max-width: 24px; max-height: 24; }') self._btnAddComputer.clicked.connect(self._btnAddComputerClicked) self._btnRemoveComputer = QPushButton(QPixmap(':/icons/list-remove.png'), self.tr('')) self._btnRemoveComputer.setStyleSheet('QPushButton { min-width: 24px; min-height: 24; max-width: 24px; max-height: 24; }') self._btnRemoveComputer.clicked.connect(self._btnRemoveComputerClicked) hbox = QHBoxLayout() hbox.addWidget(self._btnAddComputer) hbox.addWidget(self._btnRemoveComputer) gbox = QGridLayout() gbox.addWidget(self._lblLogbook, 0, 0) gbox.addWidget(self._txtLogbook, 0, 1) gbox.addWidget(self._btnBrowse, 0, 2) gbox.addWidget(self._lblComputer, 1, 0) gbox.addWidget(self._cbxComputer, 1, 1) gbox.addLayout(hbox, 1, 2) gbox.setColumnStretch(1, 1) self._pbTransfer = QProgressBar() self._pbTransfer.reset() self._txtStatus = QTextEdit() self._txtStatus.setReadOnly(True) self._btnTransfer = QPushButton(self.tr('&Transfer Dives')) self._btnTransfer.clicked.connect(self._btnTransferClicked) self._btnExit = QPushButton(self.tr('E&xit')) self._btnExit.clicked.connect(self.close) hbox = QHBoxLayout() hbox.addWidget(self._btnTransfer) hbox.addStretch() hbox.addWidget(self._btnExit) vbox = QVBoxLayout() vbox.addLayout(gbox) vbox.addWidget(self._pbTransfer) vbox.addWidget(self._txtStatus) vbox.addLayout(hbox) self.setLayout(vbox) def _closeLogbook(self): 'Close the current Logbook' if self._logbook is None: return self._logbook = None self._logbookName = 'None' self._logbookPath = None self._txtLogbook.clear() self._cbxComputer.setModel(None) self._writeSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _openLogbook(self, path): 'Open an existing Logbook' if self._logbook is not None: self._closeLogbook() if not os.path.exists(path): QMessageBox.critical(self, self.tr('Missing Logbook'), self.tr('Logbook File "%s" does not exist.') % path) return #TODO: Handle a Schema Upgrade in a user-friendly manner self._logbook = Logbook(path, auto_update=False) self._logbookName = os.path.basename(path) self._logbookPath = path self._txtLogbook.setText(self._logbookPath) self._cbxComputer.setModel(DiveComputersModel(self._logbook)) self._writeSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _readSettings(self): 'Read main window settings from the configuration' settings = QSettings() settings.beginGroup('MainWindow') max = settings.value('max') size = settings.value('size') pos = settings.value('pos') file = settings.value('file') settings.endGroup() # Size and Position the Main Window if size is not None: self.resize(size) if pos is not None: self.move(pos) # HAX because QVariant is not exposed in PySide and the default # coercion to string is just stupid if max is not None and (max == 'true'): self.showMaximized() # Open the Logbook if file is not None: self._openLogbook(file) def _writeSettings(self): 'Write settings to the configuration' settings = QSettings() settings.beginGroup('MainWindow') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) settings.setValue('max', self.isMaximized()) settings.setValue('file', self._logbookPath) settings.endGroup() def closeEvent(self, e): 'Intercept an OnClose event' self._writeSettings() e.accept() #-------------------------------------------------------------------------- # Slots @QtCore.Slot() def _btnAddComputerClicked(self): 'Add a Dive Computer' dc = AddDiveComputerWizard.RunWizard(self) if dc is not None: self._logbook.session.add(dc) self._logbook.session.commit() self._cbxComputer.model().reload() self._cbxComputer.setCurrentIndex(self._cbxComputer.findText(dc.name)) @QtCore.Slot() def _btnRemoveComputerClicked(self): 'Remove a Dive Computer' idx = self._cbxComputer.currentIndex() dc = self._cbxComputer.itemData(idx, Qt.UserRole+0) if QMessageBox.question(self, self.tr('Delete Dive Computer?'), self.tr('Are you sure you want to delete "%s"?') % dc.name, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) == QMessageBox.Yes: self._logbook.session.delete(dc) self._logbook.session.commit() self._cbxComputer.model().reload() @QtCore.Slot() def _btnBrowseClicked(self): 'Browse for a Logbook File' if self._logbook is not None: dir = os.path.dirname(self._logbookPath) else: dir = os.path.expanduser('~') fn = QFileDialog.getOpenFileName(self, caption=self.tr('Select a Logbook file'), dir=dir, filter='Logbook Files (*.lbk);;All Files(*.*)')[0] if fn == '': return if not os.path.exists(fn): if QMessageBox.question(self, self.tr('Create new Logbook?'), self.tr('Logbook "%s" does not exist. Would you like to create it?') % os.path.basename(fn), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) != QMessageBox.Yes: return Logbook.Create(fn) self._openLogbook(fn) @QtCore.Slot() def _btnTransferClicked(self): 'Transfer Dives' idx = self._cbxComputer.currentIndex() dc = self._cbxComputer.itemData(idx, Qt.UserRole+0) if self._logbook.session.dirty: print "Flushing dirty session" self._logbook.rollback() self._txtLogbook.setEnabled(False) self._btnBrowse.setEnabled(False) self._cbxComputer.setEnabled(False) self._btnAddComputer.setEnabled(False) self._btnRemoveComputer.setEnabled(False) self._btnTransfer.setEnabled(False) self._btnExit.setEnabled(False) self._txtStatus.clear() thread = QThread(self) #FIXME: ZOMG HAX: Garbage Collector will eat TransferWorker when moveToThread is called #NOTE: Qt.QueuedConnection is important... self.worker = None self.worker = TransferWorker(dc) thread.started.connect(self.worker.start, Qt.QueuedConnection) self.worker.moveToThread(thread) self.worker.finished.connect(self._transferFinished, Qt.QueuedConnection) self.worker.finished.connect(self.worker.deleteLater, Qt.QueuedConnection) self.worker.finished.connect(thread.deleteLater, Qt.QueuedConnection) self.worker.progress.connect(self._transferProgress, Qt.QueuedConnection) self.worker.started.connect(self._transferStart, Qt.QueuedConnection) self.worker.status.connect(self._transferStatus, Qt.QueuedConnection) thread.start() @QtCore.Slot(str) def _transferStatus(self, msg): 'Transfer Status Message' self._txtStatus.append(msg) @QtCore.Slot(int) def _transferStart(self, nBytes): 'Transfer Thread Stated' if nBytes > 0: self._pbTransfer.setMaximum(nBytes) else: self._pbTransfer.setMaximum(100) self._pbTransfer.reset() @QtCore.Slot(int) def _transferProgress(self, nTransferred): 'Transfer Thread Progress Event' self._pbTransfer.setValue(nTransferred) @QtCore.Slot(models.Dive) def _transferParsed(self, dive): 'Transfer Thread Parsed Dive' self._logbook.session.add(dive) @QtCore.Slot() def _transferFinished(self): 'Transfer Thread Finished' self._logbook.session.commit() self._txtLogbook.setEnabled(True) self._btnBrowse.setEnabled(True) self._cbxComputer.setEnabled(True) self._btnAddComputer.setEnabled(True) self._btnRemoveComputer.setEnabled(True) self._btnTransfer.setEnabled(True) self._btnExit.setEnabled(True)
class QLogger(logging.Handler): '''Code from: https://stackoverflow.com/questions/28655198/best-way-to-display-logs-in-pyqt ''' def __init__(self, parent=None, format=settings.log_fmt, level=logging.INFO): logging.Handler.__init__(self) # Initialize a log handler as the super class self.setFormatter(logging.Formatter(format)) # Set the formatter for the logger self.setLevel(level) # Set the logging level self.frame = QFrame(parent) # Initialize a QFrame to place other widgets in self.frame2 = QFrame(parent) # Initialize frame2 for the label and checkbox self.label = QLabel('Logs') # Define a label for the frame self.check = QCheckBox('Debugging') # Checkbox to enable debugging logging self.check.clicked.connect(self.__changeLevel) # Connect checkbox clicked to the __changeLevel method self.log_widget = QTextEdit() # Initialize a QPlainTextWidget to write logs to self.log_widget.verticalScrollBar().minimum() # Set a vertical scroll bar on the log widget self.log_widget.horizontalScrollBar().minimum() # Set a horizontal scroll bar on the log widget self.log_widget.setLineWrapMode(self.log_widget.NoWrap) # Set line wrap mode to no wrapping self.log_widget.setFont(QFont("Courier", 12)) # Set the font to a monospaced font self.log_widget.setReadOnly(True) # Set log widget to read only layout = QHBoxLayout() # Initialize a horizontal layout scheme for the label and checkbox frame layout.addWidget(self.label) # Add the label to the layout scheme layout.addWidget(self.check) # Add the checkbox to the layout scheme self.frame2.setLayout(layout) # Set the layout for frame to the horizontal layout layout = QVBoxLayout() # Initialize a layout scheme for the widgets layout.addWidget(self.frame2) # Add the label/checkbox frame to the layout scheme layout.addWidget(self.log_widget) # Add the text widget to the layout scheme self.frame.setLayout(layout) # Set the layout of the fram to the layout scheme defined ############################################################################## def emit(self, record): ''' Overload the emit method so that it prints to the text widget ''' msg = self.format(record) # Format the message for logging if record.levelno >= logging.CRITICAL: # If the log level is critical self.log_widget.setTextColor(Qt.red) # Set text color to red elif record.levelno >= logging.ERROR: # Elif level is error self.log_widget.setTextColor(Qt.darkMagenta) # Set text color to darkMagenta elif record.levelno >= logging.WARNING: # Elif level is warning self.log_widget.setTextColor(Qt.darkCyan) # Set text color to darkCyan else: # Else self.log_widget.setTextColor(Qt.black) # Set text color to black self.log_widget.append(msg) # Add the log to the text widget ############################################################################## def write(self, m): ''' Overload the write method so that it does nothing ''' pass ############################################################################## def __changeLevel(self, *args): ''' Private method to change logging level ''' if self.check.isChecked(): self.setLevel(logging.DEBUG) # Get the Meso1819 root logger and add the handler to it else: self.setLevel(logging.INFO)
class qNotebook(QVBoxLayout): def __init__(self): QVBoxLayout.__init__(self) self._teditor = QTextEdit() self._teditor.setMinimumWidth(500) self._teditor.setStyleSheet("font: 12pt \"Courier\";") button_layout = QHBoxLayout() self.addLayout(button_layout) self.clear_but = qmy_button(button_layout, self.clear_all, "clear") self.copy_but = qmy_button(button_layout, self._teditor.copy, "copy") qmy_button(button_layout, self._teditor.selectAll, "select all") qmy_button(button_layout, self._teditor.undo, "undo") qmy_button(button_layout, self._teditor.redo, "redo") search_button = qButtonWithArgumentsClass("search", self.search_for_text, {"search_text": ""}) button_layout.addWidget(search_button) qmy_button(button_layout, self.save_as_html, "save notebook") self.addWidget(self._teditor) self._teditor.document().setUndoRedoEnabled(True) self.image_counter = 0 self.image_dict = {} self.image_data_dict = {} def append_text(self, text): self._teditor.append(str(text)) def search_for_text(self, search_text = " "): self._teditor.find(search_text) def clear_all(self): self._teditor.clear() self.image_dict = {} self.image_counter = 0 # newdoc = QTextDocument() # self._teditor.setDocument(newdoc) def append_image(self, fig=None): #This assumes that an image is there waiting to be saved from matplotlib self.imgdata = StringIO.StringIO() if fig is None: pyplot.savefig(self.imgdata, transparent = False, format = img_format) else: fig.savefig(self.imgdata, transparent = False, format = img_format) self.abuffer = QBuffer() self.abuffer.open(QBuffer.ReadWrite) self.abuffer.write(self.imgdata.getvalue()) self.abuffer.close() self.abuffer.open(QBuffer.ReadOnly) iReader = QImageReader(self.abuffer, img_format ) the_image = iReader.read() # the_image = the_image0.scaledToWidth(figure_width) # save the image in a file imageFileName = "image" + str(self.image_counter) + "." + img_format self.image_data_dict[imageFileName] = self.imgdata self.image_counter +=1 imageFormat = QTextImageFormat() imageFormat.setName(imageFileName) imageFormat.setWidth(image_width) self.image_dict[imageFileName] = the_image #insert the image in the text document text_doc = self._teditor.document() text_doc.addResource(QTextDocument.ImageResource, QUrl(imageFileName), the_image) cursor = self._teditor.textCursor() cursor.movePosition(QTextCursor.End) cursor.insertImage(imageFormat) def add_image_data_resource(self, imgdata, imageFileName): self.abuffer = QBuffer() self.abuffer.open(QBuffer.ReadWrite) self.abuffer.write(imgdata.getvalue()) self.abuffer.close() self.abuffer.open(QBuffer.ReadOnly) iReader = QImageReader(self.abuffer, img_format ) the_image = iReader.read() # the_image = the_image0.scaledToWidth(figure_width) # save the image in a file # imageFileName = "image" + str(self.image_counter) + "." + img_format self.image_data_dict[imageFileName] = imgdata # self.image_counter +=1 imageFormat = QTextImageFormat() imageFormat.setName(imageFileName) imageFormat.setWidth(image_width) self.image_dict[imageFileName] = the_image #insert the image in the text document text_doc = self._teditor.document() text_doc.addResource(QTextDocument.ImageResource, QUrl(imageFileName), the_image) def append_html_table_from_array(self, a, header_rows=0, precision = 3, caption = None, cmap = None): nrows = len(a) ncols = len(a[0]) result_string = "<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\">\n" if caption != None: result_string += "<caption>%s</caption>\n" % caption r = 0 while r < header_rows: result_string += "<tr>" for c in range(ncols): if a[r][c] != "": # count following blank columns count = 1 while ((c+count) < len(a[r])) and (a[r][c+count] == "") : count += 1 val = a[r][c] if (type(val) == numpy.float64) or (type(val) == float): # @UndefinedVariable if precision != 999: val = round(val, precision) if count > 1: result_string +="<th colspan=%s>%s</th>" % (count, val) else: result_string += "<th>%s</th>" % val result_string +="</tr>\n" r += 1 while r < nrows: result_string += "<tr>" for c in range(ncols): val = a[r][c] if (cmap == None): fcolor = "#ffffff" elif (type(val) == int) or (type(val) == float) or (type(val) == numpy.float64): # @UndefinedVariable fcolor = cmap.color_from_val(val) else: fcolor = "#ffffff" if (val != "") or (c == 0): if (type(val) == numpy.float64) or (type(val) == float): # @UndefinedVariable if precision != 999: val = round(val, precision) count = 1 while ((c+count) < len(a[r])) and (a[r][c+count] == "") : count += 1 if count > 1: result_string +="<td colspan=%s bgcolor=%s>%s</td>" % (count, fcolor, val) else: result_string += "<td bgcolor=%s>%s</td>" % (fcolor, val) result_string +="</tr>\n" r += 1 result_string += "</table>\n" self.append_text(result_string) def create_empty_string_array(self, rows, cols): table_array = [] for r in range(rows): #@UnusedVariable the_row = [] for c in range(cols): #@UnusedVariable the_row.append("") table_array.append(the_row) return table_array def recurse_on_dict_headers(self, sdict, r, c, sorted_headers = None): if ((type(sdict) != dict) and (type(sdict) != OrderedDict)): return c + 1 else: if sorted_headers != None: sheaders = sorted_headers else: sheaders = sorted(sdict.keys()) for k in sheaders: self.table_array[r][c] = k c = self.recurse_on_dict_headers(sdict[k], r + 1, c) return c def recurse_to_find_size(self, sdict, r, c): if ((type(sdict) != dict) and (type(sdict) != OrderedDict)): return r, c + 1 else: rbiggest = r for k in sorted(sdict.keys()): rnew, c = self.recurse_to_find_size(sdict[k], r + 1, c) if rnew > rbiggest: rbiggest = rnew return rbiggest, c def recurse_on_dict(self, sdict, r, c, sorted_headers = None): if ((type(sdict) != dict) and (type(sdict) != OrderedDict)): self.table_array[r][c] = sdict return c + 1 else: if sorted_headers != None: sheaders = sorted_headers else: sheaders = sorted(sdict.keys()) for k in sheaders: c = self.recurse_on_dict(sdict[k], r, c) return c def convert_structured_dicts_to_array(self, sdict, sorted_keys = None, sorted_headers = None): header_levels, ncols = self.recurse_to_find_size(sdict[sdict.keys()[0]], 0, 0) nrows = header_levels + len(sdict.keys()) self.table_array = self.create_empty_string_array(nrows, ncols) self.recurse_on_dict_headers(sdict[sdict.keys()[0]], 0, 0, sorted_headers) if sorted_keys != None: key_list = sorted_keys else: key_list = sdict.keys() r = header_levels for entry in key_list: c = 0 self.table_array[r][0] = entry self.recurse_on_dict(sdict[entry], r, c, sorted_headers = sorted_headers) r += 1 return self.table_array def append_html_table_from_dicts(self, sdict, header_rows = 1, title = None, sorted_keys = None, precision = 3, cmap = None, sorted_headers = None): the_array = self.convert_structured_dicts_to_array(sdict, sorted_keys, sorted_headers = sorted_headers) self.append_html_table_from_array(the_array, header_rows, caption = title, precision = precision, cmap = cmap) def append_table(self, rows, cols, border_style = QTextFrameFormat.BorderStyle_Solid): tformat = QTextTableFormat() tformat.setBorderStyle(border_style) cursor= self._teditor.textCursor() cursor.movePosition(QTextCursor.End) table = cursor.insertTable(rows, cols, tformat) return table def fill_table_cell(self, row, col, table, text): cptr = table.cellAt(row, col).firstCursorPosition() cptr.insertText(text) def save_as_html(self): fname = QFileDialog.getSaveFileName()[0] fdirectoryname = os.path.dirname(fname) # fdirectoryname = QFileDialog.getExistingDirectory() # print fdirectoryname # fname = fdirectoryname + "/report.html" text_doc = self._teditor.document() f = open(fname, 'w') f.write(text_doc.toHtml()) f.close() for imageFileName in self.image_dict.keys(): full_image_path = fdirectoryname + "/" + imageFileName iWriter = QImageWriter(full_image_path, img_format) iWriter.write(self.image_dict[imageFileName])
class CustomWidget(QtGui.QMainWindow): def __init__(self): """ Constructor """ QtGui.QMainWindow.__init__(self) self.name = "Custom widget" self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) # TODO: This is ugly, improve it self.iconp = JConfig().icons_path self._createLayout() def _createGui(self): """ Subclasses must override this depending on the elements they want to add self._createOutputTree(), self._createOutputTable(), self._createOutputWindow() and add them to the corresponding layouts. """ raise NotImplementedError def _createToolBar(self, name): """ Subclasses need to define the specific Actions """ self.toolbar = self.addToolBar(name) self.toolbar.setMovable(False) def _createLayout(self): """ This creates the basic layout: Buttons & Outputs """ # Layouts (This is a common disposition) main_layout = QtGui.QVBoxLayout() self.button_layout = QtGui.QHBoxLayout() output_layout = QtGui.QVBoxLayout() # You will need to create your buttons # and add them to your layout like this: # self.button_layout.addWidget(button_1) # Output Layout Inner (QSplitter) # Add as many widgets as you please # They will be ordered vertically and # be resizable by the user # self.splitter.addWidget(self.table_label) # self.splitter.addWidget(...) self.splitter = QSplitter(QtCore.Qt.Vertical) # Nested layouts main_layout.addLayout(self.button_layout) output_layout.addWidget(self.splitter) main_layout.addLayout(output_layout) self.central_widget.setLayout(main_layout) def _createOutputWindow(self): """ Some binary analysis commands will output to this. """ self.output_label = QtGui.QLabel('Output') self.output_window = QTextEdit() self.output_window.setFontPointSize(10) self.output_window.setReadOnly(True) # Save it for later use self.output_window.original_textcolor = self.output_window.textColor() def _createOutputTable(self): """ A vanilla QTableWidget. Callbacks modify its properties, like number of columns, etc. """ self.table_label = QtGui.QLabel('Table Output') self.table = QTableWidget() self.table.setColumnCount(3) self.table.setColumnWidth(0, 100) self.table.setColumnWidth(1, 300) self.table.setColumnWidth(2, 300) self.table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) # Connect signals to slots self.table.customContextMenuRequested.connect(self._tablePopup) self.table.horizontalHeader().sectionDoubleClicked.connect(self._tableHeaderDoubleClicked) self.table.cellDoubleClicked.connect(self._tableCellDoubleClicked) def _createOutputTree(self): """ A QtreeWidget. Initially used to display those pesky dword comparison to immediate values. """ self.tree_label = QtGui.QLabel('Tree Output') self.tree = QTreeWidget() self.tree.setColumnCount(3) self.tree.setColumnWidth(0, 150) self.tree.setColumnWidth(1, 150) self.tree.setColumnWidth(2, 50) # Connect signals to slots self.tree.itemDoubleClicked.connect(self._treeElementDoubleClicked) ################################################################# # GUI Callbacks ################################################################# def _console_output(self, s = "", err = False): """ Convenience wrapper """ if err: # Error message err_color = QColor('red') self.output_window.setTextColor(err_color) self.output_window.append(s) # restore original color self.output_window.setTextColor(self.output_window.original_textcolor) else: self.output_window.append(s) def _tableCellDoubleClicked(self, row, col): """ Most of the info displayed in QTableWidgets represent addresses. Default action: try to jump to it. """ it = self.table.item(row, col).text() try: addr = int(it, 16) jump_to_address(addr) except ValueError: self._console_output("[!] That does not look like an address...", err = True) return def _tablePopup(self, pos): """ Popup menu activated clicking the secondary button on the table """ menu = QtGui.QMenu() # Add menu entries delRow = menu.addAction(QIcon(self.iconp + "close.png"), "Delete Row") selItem = menu.addAction(QIcon(self.iconp + "bookmark.png"), "Mark entry") menu.addSeparator() origFunc = menu.addAction(QIcon(self.iconp + "lightning.png"), "Select origin function") destFunc = menu.addAction(QIcon(self.iconp + "flag.png"), "Select destination function") # Get entry clicked action = menu.exec_(self.mapToGlobal(pos)) # Dispatch :) if action == delRow: self.table.removeRow(self.table.currentRow()) elif action == selItem: self.table.currentItem().setBackground(QtGui.QColor('red')) elif action == origFunc: try: addr = self.table.currentItem().text() InfoUI.function_orig_ea = int(addr, 16) except: self._console_output("This does not look like an address...", err = True) elif action == destFunc: try: addr = self.table.currentItem().text() InfoUI.function_dest_ea = int(addr, 16) except: self._console_output("This does not look like an address...", err = True) def _tableHeaderDoubleClicked(self, index): """ Used to sort the contents """ self.table.sortItems(index, order = QtCore.Qt.AscendingOrder) def _treeElementDoubleClicked(self, item, column): """ QTreeWidgetElement callback. Basically it jumps to the selected address in IDA disassembly window. """ try: # Only interested in addresses addr_int = int(item.text(column), 16) jump_to_address(addr_int) # Paint some color item.setBackground(column, QtGui.QColor('green')) except: self._console_output("[!] That does not look like an address...", err = True)