def updateObjectFilter(self): # -- Add message filter rows -- # font = self.getFont() self.filterTabs.clear() self.filterTabs.addTab(self.filterTable, 'Information ID') # For each object in objectList, create a new Table and add it to the tabs. for keys, values in Globals.objectDict.items(): objectFilterTable = QTableWidget() objectFilterTable.setRowCount(0) objectFilterTable.setColumnCount(2) strSplit = keys + ';Enable' objectFilterTable.setHorizontalHeaderLabels(strSplit.split(';')) objectFilterTable.resizeColumnsToContents() filterTableHeader = objectFilterTable.horizontalHeader() filterTableHeader.setSectionResizeMode(0, QHeaderView.Stretch) filterTableHeader.setSectionResizeMode( 1, QHeaderView.ResizeToContents) checkBoxFilter = self.createCheckBox() objectFilterTable.itemChanged.connect( lambda *a, table=objectFilterTable: self.filterAllObjectIDs( *a, table=table)) objectTableIndex = 0 objectFilterTable.insertRow(objectTableIndex) objCat = QTableWidgetItem('FILTER ALL') objCat.setFont(font) objectFilterTable.setItem(objectTableIndex, 0, objCat) objectFilterTable.setItem(objectTableIndex, 1, checkBoxFilter) objectTableIndex = objectTableIndex + 1 for keys2, values2 in values.items(): objectFilterTable.insertRow(objectTableIndex) checkBoxFilter = self.createCheckBox() objectFilterTable.setItem( objectTableIndex, 0, QTableWidgetItem('ID: ' + str(keys2) + ' Name: ' + str(values2))) objectFilterTable.setItem(objectTableIndex, 1, checkBoxFilter) objectTableIndex = objectTableIndex + 1 # Add the newly create table to the tabs. self.filterTabs.addTab(objectFilterTable, keys) self.objectTabDict[keys] = objectFilterTable
class SyncWidget(QWidget): def __init__(self, gui, do_user_config, selected_book_ids, is_sync_selected): QWidget.__init__(self, gui) api.build_request('/limits') self.logger = Logger( path.join(gui.current_db.library_path, 'bookfusion_sync.log')) self.logger.info( 'Open sync dialog: selected_book_ids={}; is_sync_selected={}'. format(selected_book_ids, is_sync_selected)) if len(selected_book_ids) == 0: is_sync_selected = False self.worker_thread = None self.do_user_config = do_user_config self.db = gui.current_db.new_api self.selected_book_ids = selected_book_ids self.l = QVBoxLayout() self.l.setContentsMargins(0, 0, 0, 0) self.setLayout(self.l) self.radio_layout = QVBoxLayout() self.l.addLayout(self.radio_layout) self.sync_all_radio = QRadioButton('Sync all books') self.sync_all_radio.setChecked(not is_sync_selected) self.radio_layout.addWidget(self.sync_all_radio) sync_selected_radio_label = 'Sync selected books' if len(selected_book_ids) > 0: sync_selected_radio_label = 'Sync {} selected {}'.format( len(selected_book_ids), 'book' if len(selected_book_ids) == 1 else 'books') self.sync_selected_radio = QRadioButton(sync_selected_radio_label) self.sync_selected_radio.toggled.connect(self.toggle_sync_selected) self.sync_selected_radio.setChecked(is_sync_selected) self.sync_selected_radio.setEnabled(len(selected_book_ids) > 0) self.radio_layout.addWidget(self.sync_selected_radio) self.reupload_possible = len(selected_book_ids) > 0 and len( selected_book_ids) <= 100 if self.reupload_possible: for book_id in selected_book_ids: identifiers = self.db.get_proxy_metadata(book_id).identifiers if not identifiers.get('bookfusion'): self.reupload_possible = False self.reupload_checkbox = QCheckBox('Re-upload book files', self) self.reupload_checkbox.setVisible(is_sync_selected and self.reupload_possible) self.radio_layout.addWidget(self.reupload_checkbox) self.btn_layout = QHBoxLayout() self.l.addLayout(self.btn_layout) self.config_btn = QPushButton('Configure') self.config_btn.clicked.connect(self.config) self.btn_layout.addWidget(self.config_btn) self.btn_layout.addStretch() self.start_btn = QPushButton('Start') self.start_btn.clicked.connect(self.start) self.btn_layout.addWidget(self.start_btn) self.cancel_btn = QPushButton('Cancel') self.cancel_btn.clicked.connect(self.cancel) self.cancel_btn.hide() self.btn_layout.addWidget(self.cancel_btn) self.info = QHBoxLayout() self.info.setContentsMargins(0, 0, 0, 0) self.l.addLayout(self.info) self.msg = QLabel() self.info.addWidget(self.msg) self.info.addStretch() self.log_btn = QLabel('<a href="#">Log</a>') self.log_btn.linkActivated.connect(self.toggle_log) self.log_btn.hide() self.info.addWidget(self.log_btn) self.log = QTableWidget(0, 2) self.log.setHorizontalHeaderLabels(['Book', 'Message']) self.log.horizontalHeader().setStretchLastSection(True) self.log.hide() self.l.addWidget(self.log) self.apply_config() def __del__(self): if self.worker_thread: self.worker_thread.quit() self.worker_thread.terminate() def config(self): self.do_user_config(parent=self) self.apply_config() def apply_config(self): configured = bool(prefs['api_key']) self.start_btn.setEnabled(configured) def toggle_sync_selected(self, is_sync_selected): if hasattr(self, 'reupload_checkbox'): self.reupload_checkbox.setVisible(is_sync_selected and self.reupload_possible) def start(self): if self.sync_selected_radio.isChecked( ) and self.reupload_checkbox.isChecked(): reply = QMessageBox.question( self, 'BookFusion Sync', 'Re-uploading book files can potentially result in previous highlights or bookmarks no longer working.\n\nPreviously uploaded files will be overwritten. Are you sure you want to re-upload?', QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) if reply != QMessageBox.Yes: return self.worker = None self.valid_book_ids = None self.book_log_map = {} self.book_progress_map = {} if self.sync_selected_radio.isChecked(): book_ids = list(self.selected_book_ids) else: book_ids = list(self.db.all_book_ids()) self.logger.info('Start sync: sync_selected={}; book_ids={}'.format( self.sync_selected_radio.isChecked(), book_ids)) self.in_progress = True self.total = len(book_ids) self.update_progress(None) self.start_btn.hide() self.cancel_btn.show() self.config_btn.setEnabled(False) self.sync_all_radio.setEnabled(False) self.sync_selected_radio.setEnabled(False) self.worker_thread = QThread(self) self.worker = CheckWorker(self.db, self.logger, book_ids) self.worker.finished.connect(self.finish_check) self.worker.finished.connect(self.worker_thread.quit) self.worker.progress.connect(self.update_progress) self.worker.limitsAvailable.connect(self.apply_limits) self.worker.resultsAvailable.connect(self.apply_results) self.worker.aborted.connect(self.abort) self.worker.moveToThread(self.worker_thread) self.worker_thread.started.connect(self.worker.start) self.worker_thread.start() def apply_limits(self, limits): self.logger.info('Limits: {}'.format(limits)) self.limits = limits def apply_results(self, books_count, valid_ids): self.logger.info('Check results: books_count={}; valid_ids={}'.format( books_count, valid_ids)) self.valid_book_ids = valid_ids self.books_count = books_count def finish_check(self): if self.valid_book_ids: is_filesize_exceeded = len(self.valid_book_ids) < self.books_count is_total_books_exceeded = self.limits[ 'total_books'] and self.books_count > self.limits['total_books'] if is_filesize_exceeded or is_total_books_exceeded: if self.limits['message']: msg_box = QMessageBox(self) msg_box.setWindowTitle('BookFusion Sync') msg_box.addButton(QMessageBox.No) msg_box.addButton(QMessageBox.Yes) msg_box.setText(self.limits['message']) msg_box.setDefaultButton(QMessageBox.Yes) reply = msg_box.exec_() if reply == QMessageBox.Yes: self.start_sync() else: self.in_progress = False self.msg.setText('Canceled.') self.finish_sync() else: self.start_sync() else: self.start_sync() else: if self.in_progress: self.in_progress = False self.msg.setText('No supported books selected.') self.finish_sync() def start_sync(self): self.log_btn.show() self.log.setRowCount(0) self.log.show() self.worker_thread = QThread(self) book_ids = self.valid_book_ids if self.limits['total_books']: book_ids = book_ids[:self.limits['total_books']] self.total = len(book_ids) self.worker = UploadManager( self.db, self.logger, book_ids, self.sync_selected_radio.isChecked() and self.reupload_checkbox.isChecked()) self.worker.finished.connect(self.finish_sync) self.worker.finished.connect(self.worker_thread.quit) self.worker.progress.connect(self.update_progress) self.worker.uploadProgress.connect(self.update_upload_progress) self.worker.started.connect(self.log_start) self.worker.skipped.connect(self.log_skip) self.worker.failed.connect(self.log_fail) self.worker.uploaded.connect(self.log_upload) self.worker.updated.connect(self.log_update) self.worker.aborted.connect(self.abort) self.worker.moveToThread(self.worker_thread) self.worker_thread.started.connect(self.worker.start) self.worker_thread.start() def finish_sync(self): if self.in_progress: self.msg.setText('Done.') self.cancel_btn.hide() self.cancel_btn.setEnabled(True) self.start_btn.show() self.config_btn.setEnabled(True) self.sync_all_radio.setEnabled(True) self.sync_selected_radio.setEnabled(len(self.selected_book_ids) > 0) def abort(self, error): self.in_progress = False self.msg.setText(error) def cancel(self): self.in_progress = False self.msg.setText('Canceled.') self.cancel_btn.setEnabled(False) self.worker.cancel() def update_progress(self, progress): if self.in_progress: if isinstance(self.worker, UploadManager): msg = 'Synchronizing...' else: msg = 'Preparing...' if progress: msg += ' {} of {}'.format(progress + 1, self.total) self.msg.setText(msg) def update_upload_progress(self, book_id, sent, total): if not book_id in self.book_progress_map: return progress = self.book_progress_map[book_id] if sent < total: progress.setValue(sent) progress.setMaximum(total) else: progress.setMaximum(0) def log_start(self, book_id): self.update_log(book_id, None) def log_fail(self, book_id, msg): self.update_log(book_id, msg) def log_skip(self, book_id): self.update_log(book_id, 'skipped') def log_upload(self, book_id): self.update_log(book_id, 'uploaded') def log_update(self, book_id): self.update_log(book_id, 'updated') def toggle_log(self, _): self.log.setVisible(not self.log.isVisible()) def update_log(self, book_id, msg): if book_id in self.book_log_map: index = self.book_log_map[book_id] else: index = self.log.rowCount() self.book_log_map[book_id] = index self.log.insertRow(index) title = self.db.get_proxy_metadata(book_id).title title_item = QTableWidgetItem(title) title_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) self.log.setItem(index, 0, title_item) progress = QProgressBar() progress.setMaximum(0) self.log.setCellWidget(index, 1, progress) self.book_progress_map[book_id] = progress if not msg is None: del (self.book_progress_map[book_id]) self.log.setCellWidget(index, 1, None) msg_item = QTableWidgetItem(msg) msg_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) self.log.setItem(index, 1, msg_item) def maybe_cancel(self): if self.worker_thread and self.worker_thread.isRunning(): reply = QMessageBox.question( self, 'BookFusion Sync', 'Are you sure you want to cancel the currently running process?', QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) if reply == QMessageBox.Yes: self.cancel() else: return False return True
class TableTab(TraceDocks): ## The constructor # initialize the super-class, assign a name and first configItems def __init__(self,parent): super(TableTab, self).__init__(parent,'TableTab') self.tabName = 'TableTab' self.parent = parent self.logger.logEvent('Creating Tab now: '+ self.tabName) # Set a couple of default-values, in case the configParser does not work self.snifferConfig.configAutoClearCheck = 1 self.snifferConfig.configFilterState = 'Local' self.snifferConfig.configFilterList = self.snifferFilter.filteredIdList # By parsing the config now, we assure that we re-load everything # the way we left it self.snifferConfig.parseConfigFromFile() self.lastSearchedText = 'nullthiswillneverbefound' self.lastMatch = 'purge' self.lastIndex = 0 ## Create the visible UI def setTableTabLayout(self): # Create Table Tab --------------------### # Create Layouts self.Vlayout = QVBoxLayout() self.H1layout = QHBoxLayout() self.H11layout = QHBoxLayout() self.H12layout = QHBoxLayout() self.H21layout = QHBoxLayout() self.V11layout = QVBoxLayout() self.V21layout = QVBoxLayout() # Create Widgets for H1layout # First buttons self.clearTableButt = QPushButton('Clear Table') self.clearTableButt.clicked.connect(self.clearTable) self.autoClearCheck = QCheckBox('Auto Clear') self.autoClearCheck.stateChanged.connect(self.checkAutoClearChanged) self.searchInputField = QLineEdit() self.searchInputField.setPlaceholderText('Enter search term, then click search') self.searchButt = QPushButton('Search Table') self.searchButt.clicked.connect(lambda: self.searchInTable(self.searchInputField.text(),2)) self.showSummaryButt = QPushButton('Show Summary') self.showSummaryButt.clicked.connect(self.showSummary) self.filterGroup = QButtonGroup() self.localFilterRadio = QRadioButton('Local',self) self.globalFilterRadio = QRadioButton('Global', self) self.configureFilterButt = QPushButton('Configure Filter') self.configureFilterButt.clicked.connect(self.configureFilter) self.localFilterRadio.clicked.connect(self.localRadioSelected) self.globalFilterRadio.clicked.connect(self.globalRadioSelected) self.H21layout.addWidget(self.localFilterRadio) self.H21layout.addWidget(self.globalFilterRadio) self.H21layout.addWidget(self.showSummaryButt) self.V21layout.addLayout(self.H21layout) self.V21layout.addWidget(self.configureFilterButt) # Add Widgets to H11layout self.H11layout.addWidget(self.clearTableButt) self.H11layout.addWidget(self.autoClearCheck) # Add Widgets to H12layout self.H12layout.addWidget(self.searchInputField) self.H12layout.addWidget(self.searchButt) self.H12layout.addStretch() self.V11layout.addLayout(self.H11layout) self.V11layout.addLayout(self.H12layout) self.H1layout.addLayout(self.V11layout) self.H1layout.addLayout(self.V21layout) self.syncUiToConfig() #------------------------------------ # Create Table self.detailTableIndex = 0 self.detailTable = QTableWidget() self.detailTableItem = QTableWidgetItem() self.detailTable.setRowCount(0) self.detailTable.setColumnCount(6) self.detailTable.setHorizontalHeaderLabels('packID;Tick;Timer;Type;Message;Length'.split(';')) self.detailTable.resizeColumnsToContents() self.detailTableHeader = self.detailTable.horizontalHeader() self.detailTableHeader.setSectionResizeMode(0, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(1, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(3, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(4, QHeaderView.Stretch) self.detailTableHeader.setSectionResizeMode(5, QHeaderView.ResizeToContents) #------------------------------------ self.Vlayout.addLayout(self.H1layout) self.Vlayout.addWidget(self.detailTable) self.dockContents.setLayout(self.Vlayout) ## Fill the main table with the entries from the given list # @param fillTableList the list which is to be parsed into the table def fillTable(self,fillTableList): print('Filling Table with all items in PayloadList') self.detailTable.scrollToTop() # Scrolls to the top of the table self.configAutoClearCheck = True if self.configAutoClearCheck == True: self.detailTable.setRowCount(0) self.detailTableIndex = 0 Globals.dockDict['dockStart'].progressShotBar.setMaximum(len(Globals.payloadList)) for self.tablePayload in fillTableList: self.detailTable.insertRow(self.detailTableIndex) self.detailTable.setItem(self.detailTableIndex,0,QTableWidgetItem(str(self.tablePayload.payloadHead.packetID))) self.detailTable.setItem(self.detailTableIndex,1,QTableWidgetItem(str(self.tablePayload.payloadHead.tickCountHigh << 8 | self.tablePayload.payloadHead.tickCountLow))) self.detailTable.setItem(self.detailTableIndex,2,QTableWidgetItem(str(self.tablePayload.payloadHead.timerByteHigh << 8 | self.tablePayload.payloadHead.timerByteLow))) self.detailTable.setItem(self.detailTableIndex,3,QTableWidgetItem(Globals.tspDict[self.tablePayload.payloadHead.informationID][0])) testPayload = self.tablePayload.toDict() # This a little messy: We check whether an objectDict is available, and if it is # we check for the different DataTypes there might be, since the message section is unified. # Basically, we need to handle all the different cases there can be, payload0-payload3 and the actual objectType if Globals.objectDict: if getDataType(self.tablePayload) == 1: if getObjectType(self.tablePayload) == 5: try: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+': '+Globals.objectDict['TASK'][str(self.tablePayload.data1)])) except: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem('FAILED WITH KEYERROR - see printOutput')) print('KEYERROR DETAILS:') print(testPayload) elif getDataType(self.tablePayload) == 2: try: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+': '+Globals.objectDict[Globals.objectTypeDict[self.tablePayload.data1]][str(self.tablePayload.data2)])) except: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem('FAILED WITH KEYERROR - see printOutput')) print('KEYERROR DETAILS:') print(testPayload) # If the objectDict does not exist, we can just dump the raw information into the message-section -> no lookup will be happening # and it is up to the user to interpret the data else: if getDataType(self.tablePayload) == 1: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1))) elif getDataType(self.tablePayload) == 2: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+';'+str(self.tablePayload.data2))) elif getDataType(self.tablePayload) == 3: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+';'+str(self.tablePayload.data2)+';'+str(self.tablePayload.data3))) self.detailTable.setItem(self.detailTableIndex,5,QTableWidgetItem('payload'+str(getDataType(self.tablePayload)))) self.detailTableIndex+=1 Globals.dockDict['dockStart'].progressShotBar.setValue(self.detailTableIndex) Globals.dockDict['dockStart'].progressShotBar.setValue(len(Globals.payloadList)) Globals.dockDict['dockStart'].displayStatusMessage('Table filled completely, check the tab') self.lastSearchedText = 'thisisAWorkAround' # No idea why this is there... # --- DOCK-SPECIFIC UI FUNCTIONS --- # # -----------------------------------# ## Disable all UI-buttons belonging to the tab. This is implementation specific def disableButtons(self): self.clearTableButt.setEnabled(False) self.autoClearCheck.setEnabled(False) print('Disable TabTable Buttons') ## CB: clearTableButton // Clear the main table def clearTable(self): self.logger.logEvent('clear Table clicked') self.detailTable.setRowCount(0) self.detailTableIndex = 0 ## CB: autoClearCheckbox // set the configAutoClearCheck state according to the checkbox def checkAutoClearChanged(self): self.snifferConfig.configAutoClearCheck ^= 1 self.logger.logEvent('changed Auto Clear Checkbox to - '+ str(self.snifferConfig.configAutoClearCheck)) ## CB: localRadioButt // set the configFilterState to 'Local' def localRadioSelected(self): self.snifferConfig.configFilterState = 'Local' self.logger.logEvent('changed Filter Radio to - '+ str(self.snifferConfig.configFilterState)) ## CB: globalRadioButt // set the configFilterState to 'Global' def globalRadioSelected(self): self.snifferConfig.configFilterState = 'Global' self.logger.logEvent('changed Filter Radio to - '+ str(self.snifferConfig.configFilterState)) ## CB: Show Stats // opens the Stat-Dialog to show misc. information about the measurement def showSummary(self): self.snifferStats.show() self.snifferStats.showStats() ## Searches the table for a string and scrolls to the found item. # @param textToSearch the string that needs to be found in the table # @param column the column where the search needs to take place def searchInTable(self, textToSearch, column): if textToSearch not in Globals.IDList: QMessageBox.about(self,'Not Found','SearchText not Found!') return if self.lastSearchedText == textToSearch: self.detailTable.setCurrentItem(self.lastFound[self.lastIndex]) self.detailTable.scrollToItem(self.lastFound[self.lastIndex]) self.lastIndex = self.lastIndex + 1 if self.lastIndex == len(self.lastFound): self.lastIndex = 0 else: foundItems = self.detailTable.findItems(textToSearch, Qt.MatchExactly) self.lastSearchedText = textToSearch if len(foundItems) != 0: self.lastFound = foundItems self.lastIndex = 1 self.detailTable.setCurrentItem(foundItems[0]) self.detailTable.scrollToItem(self.lastFound[self.lastIndex]) # --- MANDATORY UI FUNCTIONS --- # # -------------------------------# ## Read out all components of snifferConfig and set the UI elements according to # the saved values. def syncUiToConfig(self): self.autoClearCheck.setChecked(self.snifferConfig.configAutoClearCheck) if self.snifferConfig.configFilterState == 'Local': self.localFilterRadio.click() elif self.snifferConfig.configFilterState == 'Global': self.globalFilterRadio.click() else: print('Error, neither local nor global in config') ## Open the correct filter based on the radioButton. If the Global-filter is checked # we assign its calledby variable in order to execute the right callback after the filter is saved. def configureFilter(self): if self.localFilterRadio.isChecked(): self.snifferFilter.show() elif self.globalFilterRadio.isChecked(): Globals.globalFilter.show() Globals.globalFilter.calledBy = 'dockTable' else: print('neither radios checked. Error!') ## CB: // Updates the UI-contents with after filtering has taken place. # This function should not be called by the tab itself, but by the filter # @param filteredIDs the IDs that are to be kept in the payloadList (obsolete) # @param filteredPayloads the new payloadList, which only contains the payloads filtered by the SnifferFilter def filterUpdated(self, filteredIDs, filteredPayloads): print('we arrive from SnifferFilter') self.clearTable() self.fillTable(filteredPayloads)
class SnifferFilter(QDialog): ## The constructor. # Initialize lists to be filled with filtered payloads and # create the dialog. def __init__(self, parent): super(SnifferFilter, self).__init__() self.parent = parent self.setWindowTitle('SnifferFilter') self.filteredPayloadList = [] self.filteredIdList = [] self.layoutingComplete = False self.calledBy = 'NONE' # We store here which tab the filter belongs to, if there is no parent.(Like when the filter is global!) self.objectTabDict = {} self.filteredObjectDict = {} self.resize(670, 700) ## Filters the global payloadList by ID by iterating through it and # appending to a new list by filter-criteria # @param filterList a list of IDs that are to be transferred to the new list def filterPayloadsByID(self, filterList): for payload in Globals.payloadList: if hasattr(payload, 'payloadHead'): if Globals.tspDict[ payload.payloadHead.informationID][0] in filterList: self.filteredPayloadList.append(payload) else: #print('informationID is in filteredIdList, skipping packet') pass ## Filters the global payloadList by Message by iterating through it and # appending to a new list by filter-criteria # @param filterDict a dictionary of Key: ObjectType and Value: ObjectsToKeep as a template # of which items are to be kept in the filteredList def filterPayloadsByMessage(self, filterDict): localFilteredList = [] for payload in self.filteredPayloadList: print('payloadID:' + str(payload.payloadHead.informationID)) # If the ID has nothing to do with the object, we can safely add it. if payload.payloadHead.informationID is 23: x = 0 pass if isRelatedToObject(payload): for objType, messageID in filterDict.items(): print(Globals.objectTypeDict[getObjectType(payload)]) if Globals.objectTypeDict[getObjectType( payload )] == objType: # If the objectType matches the one in the dictionary if objType == 0: x = 0 pass if getDataType(payload) == 2: if payload.data2 not in messageID: # and the message does not match the dictionary print( 'Passing data with msgid: ' + str(payload.data2) ) # don't append, but print that we skipped this one else: localFilteredList.append( payload ) # the message does match the dictionary -> we want to keep it, so we add it to the list elif getDataType(payload) == 1: if payload.data1 not in messageID: print('Passing data with msgid: ' + str(payload.data1)) else: localFilteredList.append(payload) else: localFilteredList.append(payload) else: # If the ID has nothing to do with the object, we can safely add it. # Also, is the object is not even in the filterDict, we can add it too (this should not happen, but # it's there for safety purposes if getDataType(payload) == 0 or Globals.objectTypeDict[ getObjectType(payload)] not in filterDict: localFilteredList.append(payload) else: localFilteredList.append(payload) # In every other case, append it to the list, since we only want to filter out specific objects self.filteredPayloadList = list(localFilteredList) ## Create the visible UI # like the different tables, the searchbar etc. def setSnifferFilterUi(self): self.filterTabs = QTabWidget() self.H1layout = QHBoxLayout() self.Vlayout = QVBoxLayout() self.searchInputField = QLineEdit() self.searchInputField.setPlaceholderText( 'Enter search term, then click search') self.searchButt = QPushButton('Search Table') self.saveFilterButt = QPushButton('Save Filter') self.filterTable = QTableWidget() self.saveFilterButt.clicked.connect(self.saveFilterList) self.searchButt.clicked.connect( lambda: self.searchInTable(self.searchInputField.text(), 0)) # Create Table self.filterTableIndex = 0 self.filterTable = QTableWidget() self.filterTableItem = QTableWidgetItem() self.filterTable.setRowCount(0) self.filterTable.setColumnCount(2) self.filterTable.setHorizontalHeaderLabels( 'informationID;Enable'.split(';')) self.filterTable.resizeColumnsToContents() self.filterTableHeader = self.filterTable.horizontalHeader() self.filterTableHeader.setSectionResizeMode(0, QHeaderView.Stretch) self.filterTableHeader.setSectionResizeMode( 1, QHeaderView.ResizeToContents) font = self.getFont() self.checkBoxAllIds = self.createCheckBox() self.filterTable.itemChanged.connect(self.filterAllIDs) self.checkBoxAllMessages = self.createCheckBox() # -- Add first Row for all -- # self.filterTable.insertRow(self.filterTableIndex) idFilterItem = QTableWidgetItem('FILTER ALL IDs') idFilterItem.setFont(font) messageFilterItem = QTableWidgetItem('FILTER ALL Messages') messageFilterItem.setFont(font) self.filterTable.setItem(self.filterTableIndex, 0, idFilterItem) self.filterTable.setItem(self.filterTableIndex, 1, self.checkBoxAllIds) self.filterTableIndex = self.filterTableIndex + 1 # -- Add informationID filter rows -- # for keys, values in Globals.tspDict.items(): if values[0].startswith('ID'): checkBoxFilter = self.createCheckBox() self.filterTable.insertRow(self.filterTableIndex) self.filterTable.setItem(self.filterTableIndex, 0, QTableWidgetItem(values[0])) self.filterTable.setItem(self.filterTableIndex, 1, checkBoxFilter) self.filterTableIndex = self.filterTableIndex + 1 self.filterTabs.addTab(self.filterTable, 'Information ID') self.H1layout.addWidget(self.searchInputField) self.H1layout.addWidget(self.searchButt) self.Vlayout.addLayout(self.H1layout) self.Vlayout.addWidget(self.filterTabs) self.Vlayout.addWidget(self.saveFilterButt) #------------------------------------ self.setLayout(self.Vlayout) self.layoutingComplete = True ## Updates the visible filter UI to the new objectList # This function is called, when either a new measurement was executed or an old measurement was loaded. # Since the objects shown in the Filter need to be updated. def updateObjectFilter(self): # -- Add message filter rows -- # font = self.getFont() self.filterTabs.clear() self.filterTabs.addTab(self.filterTable, 'Information ID') # For each object in objectList, create a new Table and add it to the tabs. for keys, values in Globals.objectDict.items(): objectFilterTable = QTableWidget() objectFilterTable.setRowCount(0) objectFilterTable.setColumnCount(2) strSplit = keys + ';Enable' objectFilterTable.setHorizontalHeaderLabels(strSplit.split(';')) objectFilterTable.resizeColumnsToContents() filterTableHeader = objectFilterTable.horizontalHeader() filterTableHeader.setSectionResizeMode(0, QHeaderView.Stretch) filterTableHeader.setSectionResizeMode( 1, QHeaderView.ResizeToContents) checkBoxFilter = self.createCheckBox() objectFilterTable.itemChanged.connect( lambda *a, table=objectFilterTable: self.filterAllObjectIDs( *a, table=table)) objectTableIndex = 0 objectFilterTable.insertRow(objectTableIndex) objCat = QTableWidgetItem('FILTER ALL') objCat.setFont(font) objectFilterTable.setItem(objectTableIndex, 0, objCat) objectFilterTable.setItem(objectTableIndex, 1, checkBoxFilter) objectTableIndex = objectTableIndex + 1 for keys2, values2 in values.items(): objectFilterTable.insertRow(objectTableIndex) checkBoxFilter = self.createCheckBox() objectFilterTable.setItem( objectTableIndex, 0, QTableWidgetItem('ID: ' + str(keys2) + ' Name: ' + str(values2))) objectFilterTable.setItem(objectTableIndex, 1, checkBoxFilter) objectTableIndex = objectTableIndex + 1 # Add the newly create table to the tabs. self.filterTabs.addTab(objectFilterTable, keys) self.objectTabDict[keys] = objectFilterTable ## Searches the table for a string and scrolls to the found item. # @param textToSearch the string that needs to be found in the table # @param column the column where the search needs to take place def searchInTable(self, textToSearch, column): # Create a model of the table, so we can match a text tableModel = self.filterTable.model() start = tableModel.index(0, column) matches = tableModel.match(start, Qt.DisplayRole, textToSearch, 1, Qt.MatchContains) # If there are multiple matches, we take the first one, select it and scroll to it if matches: index = matches[0] self.filterTable.selectionModel().select( index, QItemSelectionModel.Select) self.filterTable.scrollToItem( self.filterTable.itemFromIndex(index)) ## CB: SaveFilterButton // Call the filterFunctions -> Filter the unfiltered list by ID and Object and call the update # function of the executing tab in order to update their UI. def saveFilterList(self): self.filteredPayloadList.clear() #--Save by ID--# rowCnt = self.filterTable.rowCount() self.filteredIdList.clear() for rows in range(0, rowCnt): if self.filterTable.item(rows, 1).checkState() == Qt.Checked: #print(self.filterTable.item(rows,0).text()) self.filteredIdList.append( self.filterTable.item(rows, 0).text()) self.filterPayloadsByID(self.filteredIdList) print(len(self.filteredPayloadList)) print(len(Globals.payloadList)) #--Save By Objects--# self.filteredObjectDict.clear() for objType, objectTable in self.objectTabDict.items(): rowCnt = objectTable.rowCount() objectsToFilter = [] for rows in range(0, rowCnt): if objectTable.item(rows, 1).checkState() == Qt.Checked: #print(objectTable.item(rows,0).text()) try: objectsToFilter.append( int( re.search('ID: (.*) Name:.*', objectTable.item( rows, 0).text()).group(1))) self.filteredObjectDict[objType] = objectsToFilter #print('Found Regex: '+re.search('ID: (.*) Name:.*',objectTable.item(rows,0).text()).group(1)) except: print('Error when parsing TableRegex...this is okay') self.filterPayloadsByMessage(self.filteredObjectDict) # We filtered the list, now we hide the windows and call the update-function # If the maintainer of the tab did not follow the implementation guide, there is no update-function to call. # We still save the filtered list, so we print a message to show where to find it. self.hide() try: self.parent.filterUpdated(self.filteredIdList, self.filteredPayloadList) except AttributeError: print( 'No corresponding callable function filterUpdated was found, you can access the most recent filteredList via self.snifferFilter.filteredIdList' ) try: Globals.dockDict[self.calledBy].filterUpdated( self.filteredIdList, self.filteredPayloadList) except: print('not global!') ## Check whether the first checkbox was checked and then update the entire ID table to either checked or unchecked state # @param checkBox a checkBox we perform the query on def filterAllIDs(self, checkBox): if self.layoutingComplete == True: if checkBox.column() == 1 and checkBox.row( ) == 0: # We clicked the toggle ID checkbox if checkBox.checkState() == Qt.Checked: rowCnt = self.filterTable.rowCount() for rows in range(0, rowCnt): try: self.filterTable.item(rows, 1).setCheckState(Qt.Checked) except AttributeError: print( 'no items after' + str(rows) + 'found...Maybe this column has less items than' + str(rowCnt) + '?') elif checkBox.checkState() == Qt.Unchecked: rowCnt = self.filterTable.rowCount() for rows in range(0, rowCnt): try: self.filterTable.item(rows, 1).setCheckState( Qt.Unchecked) except AttributeError: print( 'no items after' + str(rows) + 'found...Maybe this column has less items than' + str(rowCnt) + '?') else: print( 'neither checked nor unchecked...should never be here..' ) ## Check whether the first checkbox was checked and then update the entire ObjectIDtable to either checked or unchecked state # @param checkBox a checkBox we perform the query on # @param table the table that is to be updated def filterAllObjectIDs(self, checkBox, table): if (self.objectTabDict): if checkBox.column() == 1 and checkBox.row( ) == 0: # We clicked the toggle ID checkbox if checkBox.checkState() == Qt.Checked: rowCnt = table.rowCount() for rows in range(0, rowCnt): try: table.item(rows, 1).setCheckState(Qt.Checked) except AttributeError: print( 'no items after' + str(rows) + 'found...Maybe this column has less items than' + str(rowCnt) + '?') elif checkBox.checkState() == Qt.Unchecked: rowCnt = table.rowCount() for rows in range(0, rowCnt): try: table.item(rows, 1).setCheckState(Qt.Unchecked) except AttributeError: print( 'no items after' + str(rows) + 'found...Maybe this column has less items than' + str(rowCnt) + '?') else: print( 'neither checked nor unchecked...should never be here..' ) # --- HELPER FUNCTIONS --- # ## Create a defined checkbox within a tableWidgetItem to facilitate filling the table # @return the created checkbox def createCheckBox(self): myCheck = QTableWidgetItem() myCheck.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) myCheck.setCheckState(Qt.Checked) return myCheck ## Create a defined font (bold,underlined) to facilitate filling the table # @return the created font def getFont(self): font = QFont() font.setBold(True) font.setUnderline(True) return font