class QmyFormTable(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_QWFormTable() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.__dlgSetHeaders = None self.setAutoFillBackground(True) self.setCentralWidget(self.ui.tableView) self.ui.tableView.setAlternatingRowColors(True) ## self.ui.tableView.verticalHeader().setDefaultSectionSize(25) #缺省行高 ## self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) #单选 ## self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) #单元格选择 #构建Model/View self.itemModel = QStandardItemModel(10, 5, self) #数据模型,10行5列 self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型 self.ui.tableView.setModel(self.itemModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型 def __del__(self): ##析构函数 print("QmyFormTable 对象被删除了") ## ==============自定义功能函数============ ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##设置表格大小 def on_actSetSize_triggered(self): dlgTableSize = QmyDialogSize() #局部变量,构建时不能传递self dlgTableSize.setIniSize(self.itemModel.rowCount(), self.itemModel.columnCount()) ret = dlgTableSize.exec() if (ret == QDialog.Accepted): rows, cols = dlgTableSize.getTableSize() self.itemModel.setRowCount(rows) self.itemModel.setColumnCount(cols) @pyqtSlot() ##设置表头标题 def on_actSetHeader_triggered(self): if (self.__dlgSetHeaders == None): self.__dlgSetHeaders = QmyDialogHeaders(self) count = len(self.__dlgSetHeaders.headerList()) if (count != self.itemModel.columnCount()): strList = [] for i in range(self.itemModel.columnCount()): text = str( self.itemModel.headerData(i, Qt.Horizontal, Qt.DisplayRole)) strList.append(text) self.__dlgSetHeaders.setHeaderList(strList) ret = self.__dlgSetHeaders.exec() if (ret == QDialog.Accepted): strList2 = self.__dlgSetHeaders.headerList() self.itemModel.setHorizontalHeaderLabels(strList2)
def headerData(self, section, orientation, role=Qt.DisplayRole): fmt = "Chums ({}/{})" itms = len(self.app.gui.friendsItems.keys()) self.header_labels = [fmt.format(itms, itms)] if role == Qt.DisplayRole and orientation == Qt.Horizontal: return self.header_labels[section] return QStandardItemModel.headerData(self, section, orientation, role)
def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.DisplayRole and orientation == Qt.Horizontal: if section == Perso.infoName.value: return self.tr("Name") elif section == Perso.infoData.value: return self.tr("Value") else: return Perso(section).name else: return QStandardItemModel.headerData(self, section, orientation, role)
def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: if section == PlotStep.name: return self.tr("Name") elif section == PlotStep.meta: return self.tr("Meta") else: return "" else: return "" else: return QStandardItemModel.headerData(self, section, orientation, role)
def getIndexFromCaption(self, model: QStandardItemModel, caption: str): """ 获取指定列标题的索引. :param model: tableView模型 :param caption: 列标题 :return: 返回找到索引值,未找到返回-1 """ for index in range(model.columnCount()): text = model.headerData(index, Qt.Horizontal) if text == caption: return index return -1
class QmyFormTable(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_QWFormTable() self.ui.setupUi(self) self.__dlgSetHeaders = None self.setAutoFillBackground(True) self.setCentralWidget(self.ui.qTableView) self.itemModel = QStandardItemModel(10, 5, self) self.selectionModel = QItemSelectionModel(self.itemModel) self.ui.qTableView.setModel(self.itemModel) self.ui.qTableView.setSelectionModel(self.selectionModel) def __del__(self): print("QmyFormTable 对象被删除了") @pyqtSlot() def on_qAction1_triggered(self): dlgTableSize = QmyDialogSize() dlgTableSize.setIniSize(self.itemModel.rowCount(), self.itemModel.columnCount()) ret = dlgTableSize.exec() if (ret == QDialog.Accepted): rows, cols = dlgTableSize.getTableSize() self.itemModel.setRowCount(rows) self.itemModel.setColumnCount(cols) @pyqtSlot() def on_qAction2_triggered(self): if (self.__dlgSetHeaders == None): self.__dlgSetHeaders = QmyDialogHeaders(self) count = len(self.__dlgSetHeaders.headerList()) if (count != self.itemModel.columnCount()): strList = [] for i in range(self.itemModel.columnCount()): text = str( self.itemModel.headerData(i, Qt.Horizontal, Qt.DisplayRole)) strList.append(text) self.__dlgSetHeaders.setHeaderList(strList) ret = self.__dlgSetHeaders.exec() if (ret == QDialog.Accepted): strList2 = self.__dlgSetHeaders.headerList() self.itemModel.setHorizontalHeaderLabels(strList2)
class QmyMainWindow(QMainWindow): cellIndexChanged = pyqtSignal(int, int) def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.__dlgSetHeaders = None self.setCentralWidget(self.ui.tableView) ##构建状态栏 self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(200) self.ui.statusBar.addWidget(self.LabCellText) ##构建Item Model/View self.itemModel = QStandardItemModel(10, 5, self) #数据模型,10行5列 self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型 self.selectionModel.currentChanged.connect(self.do_currentChanged) ##为tableView设置数据模型 self.ui.tableView.setModel(self.itemModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型 ## self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) #单选 ## self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) #单元格选择 ## self.ui.tableView.setAlternatingRowColors(True) ## self.ui.tableView.verticalHeader().setDefaultSectionSize(25)#缺省行高 def __del__(self): ## super().__del__(self) print("QmyMainWindow 对象被删除了") ## ==============自定义功能函数============ ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##设置行数列数对话框 def on_actTab_SetSize_triggered(self): dlgTableSize = QmyDialogSize() #局部变量,构建时不能传递self dlgTableSize.setIniSize(self.itemModel.rowCount(), self.itemModel.columnCount()) ret = dlgTableSize.exec() #模态方式运行对话框 if (ret == QDialog.Accepted): rows, cols = dlgTableSize.getTableSize() self.itemModel.setRowCount(rows) self.itemModel.setColumnCount(cols) @pyqtSlot() ##设置表头标题 def on_actTab_SetHeader_triggered(self): if (self.__dlgSetHeaders == None): #未创建对话框 self.__dlgSetHeaders = QmyDialogHeaders(self) count = len(self.__dlgSetHeaders.headerList()) if (count != self.itemModel.columnCount()): #列数改变了 strList = [] for i in range(self.itemModel.columnCount()): text = str( self.itemModel.headerData(i, Qt.Horizontal, Qt.DisplayRole)) strList.append(text) #现有表格标题 self.__dlgSetHeaders.setHeaderList(strList) ret = self.__dlgSetHeaders.exec() #以模态方式运行对话框 if (ret == QDialog.Accepted): strList2 = self.__dlgSetHeaders.headerList() self.itemModel.setHorizontalHeaderLabels(strList2) @pyqtSlot() ##"定位单元格" def on_actTab_Locate_triggered(self): dlgLocate = QmyDialogLocate(self) dlgLocate.setSpinRange(self.itemModel.rowCount(), self.itemModel.columnCount()) dlgLocate.changeActionEnable.connect(self.do_setActLocateEnable) dlgLocate.changeCellText.connect(self.do_setACellText) self.cellIndexChanged.connect(dlgLocate.do_setSpinValue) dlgLocate.setAttribute(Qt.WA_DeleteOnClose) #对话框关闭时自动删除 dlgLocate.show() ## =============自定义槽函数=============================== def do_currentChanged(self, current, previous): if (current != None): #当前模型索引有效 self.LabCellPos.setText( "当前单元格:%d行,%d列" % (current.row(), current.column())) #显示模型索引的行和列号 item = self.itemModel.itemFromIndex(current) #从模型索引获得Item self.LabCellText.setText("单元格内容:" + item.text()) #显示item的文字内容 self.cellIndexChanged.emit(current.row(), current.column()) ## @pyqtSlot(bool) def do_setActLocateEnable(self, enable): self.ui.actTab_Locate.setEnabled(enable) ## @pyqtSlot(int,int,str) def do_setACellText(self, row, column, text): index = self.itemModel.index(row, column) #获取模型索引 self.selectionModel.clearSelection() #清除现有选择 self.selectionModel.setCurrentIndex( index, QItemSelectionModel.Select) #定位到单元格 self.itemModel.setData(index, text, Qt.DisplayRole) #设置单元格字符串
class GpsDataModel(object): """ :keyword Model QStandItemModel """ def __init__(self): self.__stand_item_model = QStandardItemModel() @property def Model(self): return self.__stand_item_model @property def HeadData(self): return [str(self.__stand_item_model.headerData(i, Qt.Horizontal)) for i in range(0, self.__stand_item_model.columnCount())] # 插入单行 def insertSingleRow(self,Data : tuple): item = [] count = len(Data) for i in range(0,count): item.append(QStandardItem(str(Data[i]))) self.__stand_item_model.appendRow(item) # 插入多行 def insertMultiRows(self, DataList : list): for i in range(0, len(DataList)): self.insertSingleRow(DataList[i]) def insertSingleColumn(self, headData, Data:tuple, column): """ :keyword 插入单列 :param HeadData : label Data : tuple column : to insert into column """ list_stand_item = [QStandardItem(str(data)) for data in Data] self.__stand_item_model.insertColumn(4, list_stand_item) self.__stand_item_model.setHorizontalHeaderItem(column, QStandardItem(headData)) # 插入多列 def insertHeadColumn(self, headData:list): self.__stand_item_model.setHorizontalHeaderLabels(headData) def appendColumnLast(self,headData:tuple, Data): """ 在最后插入一列 :param: headData: (label,Last_column_number) Data : iter """ list_stand_item = [QStandardItem(str(item)) for item in Data] self.__stand_item_model.appendColumn(list_stand_item) self.__stand_item_model.setHorizontalHeaderItem(headData[1], QStandardItem(headData[0])) def ergodic(self): """ 遍历所有单元格并将数据导出到本地 :return: tuple """ # 获得行列数 rows = self.__stand_item_model.rowCount() columns = self.__stand_item_model.columnCount() # 按行列获取单元格 data = [] for i in range(0, rows): row = [] for j in range(0, columns): try: row.append(self.__stand_item_model.item(i, j).text()) except: row.append('') data.append(row) return data def ergodic_column(self, column): data = [] rows = self.__stand_item_model.rowCount() for i in range(0, rows): data.append(self.__stand_item_model.item(i, column).text()) return data
class SetupDialog(QDialog, Ui_SetupDialog): """ Function and Event handling class for the Ui_SetupDialog. """ def __init__(self, parent, host_infos): QDialog.__init__(self, parent, get_modeless_dialog_flags()) self._gui_logger = GUILogger("GUILogger", logging.INFO) self._gui_job = None EventLogger.add_logger(self._gui_logger) self.data_logger_thread = None self.tab_debug_warning = False self.device_dialog = None self.last_host_index = -1 self.setupUi(self) self.model_data = QStandardItemModel(self) self.model_data.setHorizontalHeaderLabels( ['Time', 'Name', 'UID', 'Var', 'Raw', 'Unit']) self.table_data.setModel(self.model_data) self.table_data.setColumnWidth(0, 160) self.table_data.setColumnWidth(1, 170) self.table_data.setColumnWidth(2, 50) self.table_data.setColumnWidth(3, 110) self.table_data.setColumnWidth(4, 70) self.table_data.setColumnWidth(5, 100) self.model_devices = QStandardItemModel(self) self.model_devices.setHorizontalHeaderLabels(['Device', 'Value']) self.tree_devices.setModel(self.model_devices) self.tree_devices.setColumnWidth(0, 300) self.signal_initialization() self.check_authentication.stateChanged.connect( self.authentication_state_changed) self.label_secret.hide() self.edit_secret.hide() self.edit_secret.setEchoMode(QLineEdit.Password) self.check_secret_show.hide() self.check_secret_show.stateChanged.connect( self.secret_show_state_changed) self.btn_start_logging.setIcon( QIcon(load_pixmap('data_logger/start-icon.png'))) self.example_timestamp = time.time() self.edit_csv_file_name.setText( os.path.join( get_home_path(), 'logger_data_{0}.csv'.format(int(self.example_timestamp)))) self.edit_log_file_name.setText( os.path.join( get_home_path(), 'logger_debug_{0}.log'.format(int(self.example_timestamp)))) self.combo_data_time_format.addItem( utils.timestamp_to_de(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS)', 'de') self.combo_data_time_format.addItem( utils.timestamp_to_de_msec(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS,000)', 'de-msec') self.combo_data_time_format.insertSeparator( self.combo_data_time_format.count()) self.combo_data_time_format.addItem( utils.timestamp_to_us(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS)', 'us') self.combo_data_time_format.addItem( utils.timestamp_to_us_msec(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS.000)', 'us-msec') self.combo_data_time_format.insertSeparator( self.combo_data_time_format.count()) self.combo_data_time_format.addItem( utils.timestamp_to_iso(self.example_timestamp) + ' (ISO 8601)', 'iso') self.combo_data_time_format.addItem( utils.timestamp_to_iso_msec(self.example_timestamp) + ' (ISO 8601 + Milliseconds)', 'iso-msec') self.combo_data_time_format.insertSeparator( self.combo_data_time_format.count()) self.combo_data_time_format.addItem( utils.timestamp_to_unix(self.example_timestamp) + ' (Unix)', 'unix') self.combo_data_time_format.addItem( utils.timestamp_to_unix_msec(self.example_timestamp) + ' (Unix + Milliseconds)', 'unix-msec') self.combo_data_time_format.insertSeparator( self.combo_data_time_format.count()) t = utils.timestamp_to_strftime( self.example_timestamp, self.edit_data_time_format_strftime.text()) if len(t) == 0: t = '<empty>' self.combo_data_time_format.addItem((t + ' (strftime)'), 'strftime') self.combo_debug_time_format.addItem( utils.timestamp_to_de(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS)', 'de') self.combo_debug_time_format.addItem( utils.timestamp_to_us(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS)', 'us') self.combo_debug_time_format.addItem( utils.timestamp_to_iso(self.example_timestamp) + ' (ISO 8601)', 'iso') self.combo_debug_time_format.addItem( utils.timestamp_to_unix(self.example_timestamp) + ' (Unix)', 'unix') self.combo_log_level.addItem('Debug', 'debug') self.combo_log_level.addItem('Info', 'info') self.combo_log_level.addItem('Warning', 'warning') self.combo_log_level.addItem('Error', 'error') self.combo_log_level.addItem('Critical', 'critical') self.combo_log_level.setCurrentIndex(0) # debug self.combo_debug_level.addItem('Debug', logging.DEBUG) self.combo_debug_level.addItem('Info', logging.INFO) self.combo_debug_level.addItem('Warning', logging.WARNING) self.combo_debug_level.addItem('Error', logging.ERROR) self.combo_debug_level.addItem('Critical', logging.CRITICAL) self.combo_debug_level.setCurrentIndex(1) # info for host_info in host_infos: self.combo_host.addItem( host_info.host, (host_info.port, host_info.use_authentication, host_info.secret)) self._host_index_changed(0) self.update_ui_state() def update_ui_state(self): index = self.combo_data_time_format.currentIndex() if index > 0: time_format = self.combo_data_time_format.itemData(index) else: time_format = 'unknown' data_to_csv_file = self.check_data_to_csv_file.isChecked() debug_to_log_file = self.check_debug_to_log_file.isChecked() self.edit_data_time_format_strftime.setVisible( time_format == 'strftime') self.label_data_time_format_strftime_help.setVisible( time_format == 'strftime') self.label_csv_file_name.setVisible(data_to_csv_file) self.edit_csv_file_name.setVisible(data_to_csv_file) self.btn_browse_csv_file_name.setVisible(data_to_csv_file) self.label_log_file_name.setVisible(debug_to_log_file) self.edit_log_file_name.setVisible(debug_to_log_file) self.btn_browse_log_file_name.setVisible(debug_to_log_file) self.label_log_level.setVisible(debug_to_log_file) self.combo_log_level.setVisible(debug_to_log_file) def signal_initialization(self): """ Init of all important Signals and connections. """ # Buttons self.btn_start_logging.clicked.connect(self.btn_start_logging_clicked) self.btn_save_config.clicked.connect(self.btn_save_config_clicked) self.btn_load_config.clicked.connect(self.btn_load_config_clicked) self.combo_data_time_format.currentIndexChanged.connect( self.update_ui_state) self.edit_data_time_format_strftime.textChanged.connect( self.edit_data_time_format_strftime_changed) self.check_data_to_csv_file.stateChanged.connect(self.update_ui_state) self.check_debug_to_log_file.stateChanged.connect(self.update_ui_state) self.btn_browse_csv_file_name.clicked.connect( self.btn_browse_csv_file_name_clicked) self.btn_browse_log_file_name.clicked.connect( self.btn_browse_log_file_name_clicked) self.btn_clear_debug.clicked.connect(self.btn_clear_debug_clicked) self.combo_debug_level.currentIndexChanged.connect( self.combo_debug_level_changed) self.btn_add_device.clicked.connect(self.btn_add_device_clicked) self.btn_remove_device.clicked.connect(self.btn_remove_device_clicked) self.btn_remove_all_devices.clicked.connect( self.btn_remove_all_devices_clicked) self.tab_widget.currentChanged.connect(self.tab_reset_warning) self.btn_clear_data.clicked.connect(self.btn_clear_data_clicked) self._gui_logger.newEventMessage.connect(self.add_debug_message) self._gui_logger.newEventTabHighlight.connect(self.highlight_debug_tab) self.combo_host.currentIndexChanged.connect(self._host_index_changed) def btn_start_logging_clicked(self): """ Start/Stop of the logging process """ if (self.data_logger_thread is not None) and (not self.data_logger_thread.stopped): self.btn_start_logging.clicked.disconnect() self.data_logger_thread.stop() self._reset_stop() elif self.data_logger_thread is None: from brickv.data_logger import main self._gui_job = GuiDataJob(name="GuiData-Writer") self._gui_job.signalNewData.connect(self.table_add_row) self.data_logger_thread = main.main( None, GuiConfigHandler.create_config(self), self._gui_job, None, None, None) if self.data_logger_thread is not None: self.btn_start_logging.setText("Stop Logging") self.btn_start_logging.setIcon( QIcon(load_pixmap('data_logger/stop-icon.png'))) self.tab_devices.setEnabled(False) self.tab_setup.setEnabled(False) self.tab_widget.setCurrentIndex( self.tab_widget.indexOf(self.tab_data)) self.tab_reset_warning() def _reset_stop(self): self.tab_devices.setEnabled(True) self.tab_setup.setEnabled(True) self.btn_start_logging.setText("Start Logging") self.btn_start_logging.setIcon( QIcon(load_pixmap('data_logger/start-icon.png'))) self._gui_job.signalNewData.disconnect(self.table_add_row) self.data_logger_thread = None self._gui_job = None self.btn_start_logging.clicked.connect(self.btn_start_logging_clicked) def authentication_state_changed(self, state): visible = state == Qt.Checked self.label_secret.setVisible(visible) self.edit_secret.setVisible(visible) self.check_secret_show.setVisible(visible) def secret_show_state_changed(self, state): if state == Qt.Checked: self.edit_secret.setEchoMode(QLineEdit.Normal) else: self.edit_secret.setEchoMode(QLineEdit.Password) def edit_data_time_format_strftime_changed(self): index = self.combo_data_time_format.findData('strftime') if index < 0: return t = utils.timestamp_to_strftime( self.example_timestamp, self.edit_data_time_format_strftime.text()) if len(t) == 0: t = '<empty>' self.combo_data_time_format.setItemText(index, (t + ' (strftime)')) if self.edit_data_time_format_strftime.isVisible(): self.edit_data_time_format_strftime.setFocus() def btn_save_config_clicked(self): filename = get_save_file_name(self, 'Save Config', get_home_path(), 'JSON Files(*.json)') if len(filename) == 0: return if not filename.lower().endswith('.json'): filename += '.json' config = GuiConfigHandler.create_config(self) if not save_config(config, filename): QMessageBox.warning( self, 'Save Config', 'Could not save config to file! See Debug tab for details.', QMessageBox.Ok) def btn_load_config_clicked(self): filename = get_open_file_name(self, 'Load Config', get_home_path(), 'JSON Files(*.json);;All Files(*)') if len(filename) == 0: return config = load_and_validate_config(filename) if config == None: QMessageBox.warning( self, 'Load Config', 'Could not load config from file! See Debug tab for details.', QMessageBox.Ok) return self.update_setup_tab(config) self.update_devices_tab(config) def btn_browse_csv_file_name_clicked(self): if len(self.edit_csv_file_name.text()) > 0: last_dir = os.path.dirname( os.path.realpath(self.edit_csv_file_name.text())) else: last_dir = get_home_path() filename = get_save_file_name(self, 'Choose CSV File', last_dir, "CSV Files (*.csv)") if len(filename) > 0: if not filename.lower().endswith('.csv'): filename += '.csv' self.edit_csv_file_name.setText(filename) def btn_browse_log_file_name_clicked(self): if len(self.edit_log_file_name.text()) > 0: last_dir = os.path.dirname( os.path.realpath(self.edit_log_file_name.text())) else: last_dir = get_home_path() filename = get_save_file_name(self, 'Choose Log File', last_dir, "Log Files (*.log)") if len(filename) > 0: if not filename.lower().endswith('.log'): filename += '.log' self.edit_log_file_name.setText(filename) def btn_add_device_clicked(self): """ Opens the DeviceDialog in Add-Mode. """ if self.device_dialog is None: self.device_dialog = DeviceDialog(self) self.device_dialog.btn_refresh_clicked() self.device_dialog.show() def btn_remove_device_clicked(self): selection = self.tree_devices.selectionModel().selectedIndexes() while len(selection) > 0: index = selection[0] while index.parent() != self.model_devices.invisibleRootItem( ).index(): index = index.parent() self.model_devices.removeRows(index.row(), 1) # get new selection, because row removal might invalid indices selection = self.tree_devices.selectionModel().selectedIndexes() def btn_remove_all_devices_clicked(self): self.model_devices.removeRows(0, self.model_devices.rowCount()) def btn_clear_data_clicked(self): self.model_data.removeRows(0, self.model_data.rowCount()) def tab_reset_warning(self): """ Resets the Warning @ the debug tab. """ if not self.tab_debug_warning or self.tab_widget.currentIndex( ) != self.tab_widget.indexOf(self.tab_debug): return self.tab_debug_warning = False self.tab_set(self.tab_widget.indexOf(self.tab_debug), self.palette().color(QPalette.WindowText), None) def combo_debug_level_changed(self): """ Changes the log level dynamically. """ self._gui_logger.level = self.combo_debug_level.itemData( self.combo_debug_level.currentIndex()) def tab_set(self, tab_index, color, icon=None): """ Sets the font Color and an icon, if given, at a specific tab. """ self.tab_widget.tabBar().setTabTextColor(tab_index, color) if icon is not None: self.tab_widget.setTabIcon(tab_index, QIcon(icon)) else: self.tab_widget.setTabIcon(tab_index, QIcon()) def _host_index_changed(self, i): if self.last_host_index >= 0: self.combo_host.setItemData( self.last_host_index, (self.spin_port.value(), self.check_authentication.isChecked(), self.edit_secret.text())) self.last_host_index = i if i < 0: return host_info = self.combo_host.itemData(i) self.spin_port.setValue(host_info[0]) self.check_authentication.setChecked(host_info[1]) self.edit_secret.setText(host_info[2]) def update_setup_tab(self, config): EventLogger.debug('Updating setup tab from config') name = config['hosts']['default']['name'] port = config['hosts']['default']['port'] secret = config['hosts']['default']['secret'] i = self.combo_host.findText(name) if i >= 0: self.combo_host.setCurrentIndex(i) else: self.combo_host.insertItem(0, name, (port, secret != None, secret)) self.combo_host.setCurrentIndex(0) self.spin_port.setValue(port) self.check_authentication.setChecked(secret != None) self.edit_secret.setText(secret if secret != None else '') self.combo_data_time_format.setCurrentIndex( max( self.combo_data_time_format.findData( config['data']['time_format']), 0)) self.edit_data_time_format_strftime.setText( config['data']['time_format_strftime']) self.check_data_to_csv_file.setChecked( config['data']['csv']['enabled']) self.edit_csv_file_name.setText(config['data']['csv']['file_name']) self.combo_debug_time_format.setCurrentIndex( max( self.combo_debug_time_format.findData( config['debug']['time_format']), 0)) self.check_debug_to_log_file.setChecked( config['debug']['log']['enabled']) self.edit_log_file_name.setText(config['debug']['log']['file_name']) self.combo_log_level.setCurrentIndex( max( self.combo_debug_time_format.findData( config['debug']['log']['level']), 0)) def update_devices_tab(self, config): EventLogger.debug('Updating devices tab from config') self.model_devices.removeRows(0, self.model_data.rowCount()) for device in config['devices']: self.add_device_to_tree(device) def add_device_to_tree(self, device): # check if device is already added if len(device['uid']) > 0: for row in range(self.model_devices.rowCount()): existing_name = self.model_devices.item(row, 0).text() exisitng_uid = self.tree_devices.indexWidget( self.model_devices.item(row, 1).index()).text() if device['name'] == existing_name and device[ 'uid'] == exisitng_uid: EventLogger.info( 'Ignoring duplicate device "{0}" with UID "{1}"'. format(device['name'], device['uid'])) return # add device name_item = QStandardItem(device['name']) uid_item = QStandardItem('') self.model_devices.appendRow([name_item, uid_item]) edit_uid = QLineEdit() edit_uid.setPlaceholderText('Enter UID') edit_uid.setValidator( QRegExpValidator(QRegExp( '^[{0}]{{1,6}}$'.format(BASE58)))) # FIXME: use stricter logic edit_uid.setText(device['uid']) self.tree_devices.setIndexWidget(uid_item.index(), edit_uid) value_specs = device_specs[device['name']]['values'] parent_item = QStandardItem('Values') name_item.appendRow([parent_item, QStandardItem('')]) self.tree_devices.expand(parent_item.index()) # add values for value_spec in value_specs: value_name_item = QStandardItem(value_spec['name']) value_interval_item = QStandardItem('') parent_item.appendRow([value_name_item, value_interval_item]) spinbox_interval = IntervalWidget() spinbox_interval.set_interval( device['values'][value_spec['name']]['interval']) self.tree_devices.setIndexWidget(value_interval_item.index(), spinbox_interval) if value_spec['subvalues'] != None: for subvalue_name in value_spec['subvalues']: subvalue_name_item = QStandardItem(subvalue_name) subvalue_check_item = QStandardItem('') value_name_item.appendRow( [subvalue_name_item, subvalue_check_item]) check_subvalue = QCheckBox() check_subvalue.setChecked(device['values'][ value_spec['name']]['subvalues'][subvalue_name]) self.tree_devices.setIndexWidget( subvalue_check_item.index(), check_subvalue) self.tree_devices.expand(name_item.index()) # add options option_specs = device_specs[device['name']]['options'] if option_specs != None: parent_item = QStandardItem('Options') name_item.appendRow([parent_item, QStandardItem('')]) for option_spec in option_specs: option_name_item = QStandardItem(option_spec['name']) option_widget_item = QStandardItem('') parent_item.appendRow([option_name_item, option_widget_item]) if option_spec['type'] == 'choice': widget_option_value = QComboBox() for option_value_spec in option_spec['values']: widget_option_value.addItem(option_value_spec[0], option_value_spec[1]) widget_option_value.setCurrentIndex( widget_option_value.findText( device['options'][option_spec['name']]['value'])) elif option_spec['type'] == 'int': widget_option_value = QSpinBox() widget_option_value.setRange(option_spec['minimum'], option_spec['maximum']) widget_option_value.setSuffix(option_spec['suffix']) widget_option_value.setValue( device['options'][option_spec['name']]['value']) elif option_spec['type'] == 'bool': widget_option_value = QCheckBox() widget_option_value.setChecked( device['options'][option_spec['name']]['value']) self.tree_devices.setIndexWidget(option_widget_item.index(), widget_option_value) def add_debug_message(self, message): self.text_debug.append(message) while self.text_debug.document().blockCount() > 1000: cursor = QTextCursor(self.text_debug.document().begin()) cursor.select(QTextCursor.BlockUnderCursor) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) cursor.removeSelectedText() if self.checkbox_debug_auto_scroll.isChecked(): self.text_debug.verticalScrollBar().setValue( self.text_debug.verticalScrollBar().maximum()) def btn_clear_debug_clicked(self): self.text_debug.clear() def highlight_debug_tab(self): """ SIGNAL function: Highlight the debug tab when an error occurs. """ if not self.tab_debug_warning and self.tab_widget.currentIndex( ) != self.tab_widget.indexOf(self.tab_debug): self.tab_debug_warning = True self.tab_set(self.tab_widget.indexOf(self.tab_debug), QColor(255, 0, 0), get_resources_path("warning-icon-16.png")) def table_add_row(self, csv_data): """ SIGNAL function: Adds new CSV Data into the Table. """ rows = self.model_data.rowCount() while rows >= 1000: self.model_data.removeRow(0) rows = self.model_data.rowCount() row_number = None if rows > 0: try: row_number = int( self.model_data.headerData(rows - 1, Qt.Vertical)) except ValueError: pass self.model_data.appendRow([ QStandardItem(csv_data.timestamp), QStandardItem(csv_data.name), QStandardItem(csv_data.uid), QStandardItem(csv_data.var_name), QStandardItem(str(csv_data.raw_data)), QStandardItem(csv_data.var_unit) ]) if row_number != None: self.model_data.setHeaderData(rows, Qt.Vertical, str(row_number + 1)) if self.checkbox_data_auto_scroll.isChecked(): self.table_data.scrollToBottom()
class QmyMainWindow(QMainWindow): cellIndexChange = pyqtSignal(int, int) def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.__dlgSetHeaders = None self.setCentralWidget(self.ui.qTableView) self.qLabel1 = QLabel("当前单元格:", self) self.qLabel1.setMinimumWidth(180) self.ui.qStatusBar.addWidget(self.qLabel1) self.qLabel2 = QLabel("单元格内容:", self) self.qLabel2.setMinimumWidth(200) self.ui.qStatusBar.addWidget(self.qLabel2) self.itemModel = QStandardItemModel(10, 5, self) self.selectionModel = QItemSelectionModel(self.itemModel) self.selectionModel.currentChanged.connect( self.do_currentChanged) # 显示状态栏信息 self.ui.qTableView.setModel(self.itemModel) self.ui.qTableView.setSelectionModel(self.selectionModel) @pyqtSlot() def on_qAction1_triggered(self): # 设置行数列数 dlgTableSize = QmyDialogSize(self.itemModel.rowCount(), self.itemModel.columnCount()) ret = dlgTableSize.exec() if (ret == QDialog.Accepted): rows, cols = dlgTableSize.getTableSize() self.itemModel.setRowCount(rows) self.itemModel.setColumnCount(cols) @pyqtSlot() def on_qAction2_triggered(self): # 设置表头标题 if (self.__dlgSetHeaders == None): self.__dlgSetHeaders = QmyDialogHeaders(self) count = len(self.__dlgSetHeaders.headerList()) if (count != self.itemModel.columnCount() ): # 逻辑感人。首先表头标题不允许在表格内部直接改,因此如果发现标题与设置界面不一致,肯定是列数发生变化 strList = [] for i in range(self.itemModel.columnCount()): text = str( self.itemModel.headerData(i, Qt.Horizontal, Qt.DisplayRole)) strList.append(text) self.__dlgSetHeaders.setHeaderList(strList) ret = self.__dlgSetHeaders.exec() if (ret == QDialog.Accepted): strList2 = self.__dlgSetHeaders.headerList() self.itemModel.setHorizontalHeaderLabels(strList2) @pyqtSlot() def on_qAction3_triggered(self): # 定位单元格 dlgLocate = QmyDialogLocate(self) dlgLocate.setSpinRange(self.itemModel.rowCount(), self.itemModel.columnCount()) dlgLocate.changeActionEnable.connect(self.do_setActLocateEnable) dlgLocate.changeCellText.connect(self.do_setACellText) self.cellIndexChange.connect(dlgLocate.do_setSpinValue) dlgLocate.setAttribute(Qt.WA_DeleteOnClose) dlgLocate.show() def do_setActLocateEnable(self, enable): self.ui.qAction3.setEnabled(enable) def do_setACellText(self, row, column, text): index = self.itemModel.index(row, column) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(index, QItemSelectionModel.Select) self.itemModel.setData(index, text, Qt.DisplayRole) def do_currentChanged(self, current, previous): if (current != None): self.qLabel1.setText("当前单元格:%d行,%d列" % (current.row(), current.column())) item = self.itemModel.itemFromIndex(current) self.qLabel2.setText("单元格内容:" + item.text()) self.cellIndexChange.emit(current.row(), current.column())
class SetupDialog(QDialog, Ui_SetupDialog): """ Function and Event handling class for the Ui_SetupDialog. """ def __init__(self, parent, host_infos): QDialog.__init__(self, parent, get_modeless_dialog_flags()) self._gui_logger = GUILogger("GUILogger", logging.INFO) self._gui_job = None EventLogger.add_logger(self._gui_logger) self.data_logger_thread = None self.tab_debug_warning = False self.device_dialog = None self.last_host_index = -1 self.setupUi(self) self.model_data = QStandardItemModel(self) self.model_data.setHorizontalHeaderLabels(['Time', 'Name', 'UID', 'Var', 'Raw', 'Unit']) self.table_data.setModel(self.model_data) self.table_data.setColumnWidth(0, 160) self.table_data.setColumnWidth(1, 170) self.table_data.setColumnWidth(2, 50) self.table_data.setColumnWidth(3, 110) self.table_data.setColumnWidth(4, 70) self.table_data.setColumnWidth(5, 100) self.model_devices = QStandardItemModel(self) self.model_devices.setHorizontalHeaderLabels(['Device', 'Value']) self.tree_devices.setModel(self.model_devices) self.tree_devices.setColumnWidth(0, 300) self.signal_initialization() self.check_authentication.stateChanged.connect(self.authentication_state_changed) self.label_secret.hide() self.edit_secret.hide() self.edit_secret.setEchoMode(QLineEdit.Password) self.check_secret_show.hide() self.check_secret_show.stateChanged.connect(self.secret_show_state_changed) self.btn_start_logging.setIcon(QIcon(load_pixmap('data_logger/start-icon.png'))) self.example_timestamp = time.time() self.edit_csv_file_name.setText(os.path.join(get_home_path(), 'logger_data_{0}.csv'.format(int(self.example_timestamp)))) self.edit_log_file_name.setText(os.path.join(get_home_path(), 'logger_debug_{0}.log'.format(int(self.example_timestamp)))) self.combo_data_time_format.addItem(utils.timestamp_to_de(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS)', 'de') self.combo_data_time_format.addItem(utils.timestamp_to_de_msec(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS,000)', 'de-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) self.combo_data_time_format.addItem(utils.timestamp_to_us(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS)', 'us') self.combo_data_time_format.addItem(utils.timestamp_to_us_msec(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS.000)', 'us-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) self.combo_data_time_format.addItem(utils.timestamp_to_iso(self.example_timestamp) + ' (ISO 8601)', 'iso') self.combo_data_time_format.addItem(utils.timestamp_to_iso_msec(self.example_timestamp) + ' (ISO 8601 + Milliseconds)', 'iso-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) self.combo_data_time_format.addItem(utils.timestamp_to_unix(self.example_timestamp) + ' (Unix)', 'unix') self.combo_data_time_format.addItem(utils.timestamp_to_unix_msec(self.example_timestamp) + ' (Unix + Milliseconds)', 'unix-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) t = utils.timestamp_to_strftime(self.example_timestamp, self.edit_data_time_format_strftime.text()) if len(t) == 0: t = '<empty>' self.combo_data_time_format.addItem((t + ' (strftime)'), 'strftime') self.combo_debug_time_format.addItem(utils.timestamp_to_de(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS)', 'de') self.combo_debug_time_format.addItem(utils.timestamp_to_us(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS)', 'us') self.combo_debug_time_format.addItem(utils.timestamp_to_iso(self.example_timestamp) + ' (ISO 8601)', 'iso') self.combo_debug_time_format.addItem(utils.timestamp_to_unix(self.example_timestamp) + ' (Unix)', 'unix') self.combo_log_level.addItem('Debug', 'debug') self.combo_log_level.addItem('Info', 'info') self.combo_log_level.addItem('Warning', 'warning') self.combo_log_level.addItem('Error', 'error') self.combo_log_level.addItem('Critical', 'critical') self.combo_log_level.setCurrentIndex(0) # debug self.combo_debug_level.addItem('Debug', logging.DEBUG) self.combo_debug_level.addItem('Info', logging.INFO) self.combo_debug_level.addItem('Warning', logging.WARNING) self.combo_debug_level.addItem('Error', logging.ERROR) self.combo_debug_level.addItem('Critical', logging.CRITICAL) self.combo_debug_level.setCurrentIndex(1) # info for host_info in host_infos: self.combo_host.addItem(host_info.host, (host_info.port, host_info.use_authentication, host_info.secret)) self._host_index_changed(0) self.update_ui_state() def update_ui_state(self): index = self.combo_data_time_format.currentIndex() if index > 0: time_format = self.combo_data_time_format.itemData(index) else: time_format = 'unknown' data_to_csv_file = self.check_data_to_csv_file.isChecked() debug_to_log_file = self.check_debug_to_log_file.isChecked() self.edit_data_time_format_strftime.setVisible(time_format == 'strftime') self.label_data_time_format_strftime_help.setVisible(time_format == 'strftime') self.label_csv_file_name.setVisible(data_to_csv_file) self.edit_csv_file_name.setVisible(data_to_csv_file) self.btn_browse_csv_file_name.setVisible(data_to_csv_file) self.label_log_file_name.setVisible(debug_to_log_file) self.edit_log_file_name.setVisible(debug_to_log_file) self.btn_browse_log_file_name.setVisible(debug_to_log_file) self.label_log_level.setVisible(debug_to_log_file) self.combo_log_level.setVisible(debug_to_log_file) def signal_initialization(self): """ Init of all important Signals and connections. """ # Buttons self.btn_start_logging.clicked.connect(self.btn_start_logging_clicked) self.btn_save_config.clicked.connect(self.btn_save_config_clicked) self.btn_load_config.clicked.connect(self.btn_load_config_clicked) self.combo_data_time_format.currentIndexChanged.connect(self.update_ui_state) self.edit_data_time_format_strftime.textChanged.connect(self.edit_data_time_format_strftime_changed) self.check_data_to_csv_file.stateChanged.connect(self.update_ui_state) self.check_debug_to_log_file.stateChanged.connect(self.update_ui_state) self.btn_browse_csv_file_name.clicked.connect(self.btn_browse_csv_file_name_clicked) self.btn_browse_log_file_name.clicked.connect(self.btn_browse_log_file_name_clicked) self.btn_clear_debug.clicked.connect(self.btn_clear_debug_clicked) self.combo_debug_level.currentIndexChanged.connect(self.combo_debug_level_changed) self.btn_add_device.clicked.connect(self.btn_add_device_clicked) self.btn_remove_device.clicked.connect(self.btn_remove_device_clicked) self.btn_remove_all_devices.clicked.connect(self.btn_remove_all_devices_clicked) self.tab_widget.currentChanged.connect(self.tab_reset_warning) self.btn_clear_data.clicked.connect(self.btn_clear_data_clicked) self._gui_logger.newEventMessage.connect(self.add_debug_message) self._gui_logger.newEventTabHighlight.connect(self.highlight_debug_tab) self.combo_host.currentIndexChanged.connect(self._host_index_changed) def btn_start_logging_clicked(self): """ Start/Stop of the logging process """ if (self.data_logger_thread is not None) and (not self.data_logger_thread.stopped): self.btn_start_logging.clicked.disconnect() self.data_logger_thread.stop() self._reset_stop() elif self.data_logger_thread is None: from brickv.data_logger import main self._gui_job = GuiDataJob(name="GuiData-Writer") self._gui_job.signalNewData.connect(self.table_add_row) self.data_logger_thread = main.main(None, GuiConfigHandler.create_config(self), self._gui_job, None, None, None) if self.data_logger_thread is not None: self.btn_start_logging.setText("Stop Logging") self.btn_start_logging.setIcon(QIcon(load_pixmap('data_logger/stop-icon.png'))) self.tab_devices.setEnabled(False) self.tab_setup.setEnabled(False) self.tab_widget.setCurrentIndex(self.tab_widget.indexOf(self.tab_data)) self.tab_reset_warning() def _reset_stop(self): self.tab_devices.setEnabled(True) self.tab_setup.setEnabled(True) self.btn_start_logging.setText("Start Logging") self.btn_start_logging.setIcon(QIcon(load_pixmap('data_logger/start-icon.png'))) self._gui_job.signalNewData.disconnect(self.table_add_row) self.data_logger_thread = None self._gui_job = None self.btn_start_logging.clicked.connect(self.btn_start_logging_clicked) def authentication_state_changed(self, state): visible = state == Qt.Checked self.label_secret.setVisible(visible) self.edit_secret.setVisible(visible) self.check_secret_show.setVisible(visible) def secret_show_state_changed(self, state): if state == Qt.Checked: self.edit_secret.setEchoMode(QLineEdit.Normal) else: self.edit_secret.setEchoMode(QLineEdit.Password) def edit_data_time_format_strftime_changed(self): index = self.combo_data_time_format.findData('strftime') if index < 0: return t = utils.timestamp_to_strftime(self.example_timestamp, self.edit_data_time_format_strftime.text()) if len(t) == 0: t = '<empty>' self.combo_data_time_format.setItemText(index, (t + ' (strftime)')) if self.edit_data_time_format_strftime.isVisible(): self.edit_data_time_format_strftime.setFocus() def btn_save_config_clicked(self): filename = get_save_file_name(self, 'Save Config', get_home_path(), 'JSON Files (*.json)') if len(filename) == 0: return if not filename.lower().endswith('.json'): filename += '.json' config = GuiConfigHandler.create_config(self) if not save_config(config, filename): QMessageBox.warning(self, 'Save Config', 'Could not save config to file! See Debug tab for details.', QMessageBox.Ok) def btn_load_config_clicked(self): filename = get_open_file_name(self, 'Load Config', get_home_path(), 'JSON Files (*.json)') if len(filename) == 0: return config = load_and_validate_config(filename) if config == None: QMessageBox.warning(self, 'Load Config', 'Could not load config from file! See Debug tab for details.', QMessageBox.Ok) return self.update_setup_tab(config) self.update_devices_tab(config) def btn_browse_csv_file_name_clicked(self): if len(self.edit_csv_file_name.text()) > 0: last_dir = os.path.dirname(os.path.realpath(self.edit_csv_file_name.text())) else: last_dir = get_home_path() filename = get_save_file_name(self, 'Choose CSV File', last_dir, "CSV Files (*.csv)") if len(filename) > 0: if not filename.lower().endswith('.csv'): filename += '.csv' self.edit_csv_file_name.setText(filename) def btn_browse_log_file_name_clicked(self): if len(self.edit_log_file_name.text()) > 0: last_dir = os.path.dirname(os.path.realpath(self.edit_log_file_name.text())) else: last_dir = get_home_path() filename = get_save_file_name(self, 'Choose Log File', last_dir, "Log Files (*.log)") if len(filename) > 0: if not filename.lower().endswith('.log'): filename += '.log' self.edit_log_file_name.setText(filename) def btn_add_device_clicked(self): """ Opens the DeviceDialog in Add-Mode. """ if self.device_dialog is None: self.device_dialog = DeviceDialog(self) self.device_dialog.btn_refresh_clicked() self.device_dialog.show() def btn_remove_device_clicked(self): selection = self.tree_devices.selectionModel().selectedIndexes() while len(selection) > 0: index = selection[0] while index.parent() != self.model_devices.invisibleRootItem().index(): index = index.parent() self.model_devices.removeRows(index.row(), 1) # get new selection, because row removal might invalid indices selection = self.tree_devices.selectionModel().selectedIndexes() def btn_remove_all_devices_clicked(self): self.model_devices.removeRows(0, self.model_devices.rowCount()) def btn_clear_data_clicked(self): self.model_data.removeRows(0, self.model_data.rowCount()) def tab_reset_warning(self): """ Resets the Warning @ the debug tab. """ if not self.tab_debug_warning or self.tab_widget.currentIndex() != self.tab_widget.indexOf(self.tab_debug): return self.tab_debug_warning = False self.tab_set(self.tab_widget.indexOf(self.tab_debug), self.palette().color(QPalette.WindowText), None) def combo_debug_level_changed(self): """ Changes the log level dynamically. """ self._gui_logger.level = self.combo_debug_level.itemData(self.combo_debug_level.currentIndex()) def tab_set(self, tab_index, color, icon=None): """ Sets the font Color and an icon, if given, at a specific tab. """ self.tab_widget.tabBar().setTabTextColor(tab_index, color) if icon is not None: self.tab_widget.setTabIcon(tab_index, QIcon(icon)) else: self.tab_widget.setTabIcon(tab_index, QIcon()) def _host_index_changed(self, i): if self.last_host_index >= 0: self.combo_host.setItemData(self.last_host_index, (self.spin_port.value(), self.check_authentication.isChecked(), self.edit_secret.text())) self.last_host_index = i if i < 0: return host_info = self.combo_host.itemData(i) self.spin_port.setValue(host_info[0]) self.check_authentication.setChecked(host_info[1]) self.edit_secret.setText(host_info[2]) def update_setup_tab(self, config): EventLogger.debug('Updating setup tab from config') name = config['hosts']['default']['name'] port = config['hosts']['default']['port'] secret = config['hosts']['default']['secret'] i = self.combo_host.findText(name) if i >= 0: self.combo_host.setCurrentIndex(i) else: self.combo_host.insertItem(0, name, (port, secret != None, secret)) self.combo_host.setCurrentIndex(0) self.spin_port.setValue(port) self.check_authentication.setChecked(secret != None) self.edit_secret.setText(secret if secret != None else '') self.combo_data_time_format.setCurrentIndex(max(self.combo_data_time_format.findData(config['data']['time_format']), 0)) self.edit_data_time_format_strftime.setText(config['data']['time_format_strftime']) self.check_data_to_csv_file.setChecked(config['data']['csv']['enabled']) self.edit_csv_file_name.setText(config['data']['csv']['file_name']) self.combo_debug_time_format.setCurrentIndex(max(self.combo_debug_time_format.findData(config['debug']['time_format']), 0)) self.check_debug_to_log_file.setChecked(config['debug']['log']['enabled']) self.edit_log_file_name.setText(config['debug']['log']['file_name']) self.combo_log_level.setCurrentIndex(max(self.combo_debug_time_format.findData(config['debug']['log']['level']), 0)) def update_devices_tab(self, config): EventLogger.debug('Updating devices tab from config') self.model_devices.removeRows(0, self.model_data.rowCount()) for device in config['devices']: self.add_device_to_tree(device) def add_device_to_tree(self, device): # check if device is already added if len(device['uid']) > 0: for row in range(self.model_devices.rowCount()): existing_name = self.model_devices.item(row, 0).text() exisitng_uid = self.tree_devices.indexWidget(self.model_devices.item(row, 1).index()).text() if device['name'] == existing_name and device['uid'] == exisitng_uid: EventLogger.info('Ignoring duplicate device "{0}" with UID "{1}"' .format(device['name'], device['uid'])) return # add device name_item = QStandardItem(device['name']) uid_item = QStandardItem('') self.model_devices.appendRow([name_item, uid_item]) edit_uid = QLineEdit() edit_uid.setPlaceholderText('Enter UID') edit_uid.setValidator(QRegExpValidator(QRegExp('^[{0}]{{1,6}}$'.format(BASE58)))) # FIXME: use stricter logic edit_uid.setText(device['uid']) self.tree_devices.setIndexWidget(uid_item.index(), edit_uid) value_specs = device_specs[device['name']]['values'] parent_item = QStandardItem('Values') name_item.appendRow([parent_item, QStandardItem('')]) self.tree_devices.expand(parent_item.index()) # add values for value_spec in value_specs: value_name_item = QStandardItem(value_spec['name']) value_interval_item = QStandardItem('') parent_item.appendRow([value_name_item, value_interval_item]) spinbox_interval = IntervalWidget() spinbox_interval.set_interval(device['values'][value_spec['name']]['interval']) self.tree_devices.setIndexWidget(value_interval_item.index(), spinbox_interval) if value_spec['subvalues'] != None: for subvalue_name in value_spec['subvalues']: subvalue_name_item = QStandardItem(subvalue_name) subvalue_check_item = QStandardItem('') value_name_item.appendRow([subvalue_name_item, subvalue_check_item]) check_subvalue = QCheckBox() check_subvalue.setChecked(device['values'][value_spec['name']]['subvalues'][subvalue_name]) self.tree_devices.setIndexWidget(subvalue_check_item.index(), check_subvalue) self.tree_devices.expand(name_item.index()) # add options option_specs = device_specs[device['name']]['options'] if option_specs != None: parent_item = QStandardItem('Options') name_item.appendRow([parent_item, QStandardItem('')]) for option_spec in option_specs: option_name_item = QStandardItem(option_spec['name']) option_widget_item = QStandardItem('') parent_item.appendRow([option_name_item, option_widget_item]) if option_spec['type'] == 'choice': widget_option_value = QComboBox() for option_value_spec in option_spec['values']: widget_option_value.addItem(option_value_spec[0], option_value_spec[1]) widget_option_value.setCurrentIndex(widget_option_value.findText(device['options'][option_spec['name']]['value'])) elif option_spec['type'] == 'int': widget_option_value = QSpinBox() widget_option_value.setRange(option_spec['minimum'], option_spec['maximum']) widget_option_value.setSuffix(option_spec['suffix']) widget_option_value.setValue(device['options'][option_spec['name']]['value']) elif option_spec['type'] == 'bool': widget_option_value = QCheckBox() widget_option_value.setChecked(device['options'][option_spec['name']]['value']) self.tree_devices.setIndexWidget(option_widget_item.index(), widget_option_value) def add_debug_message(self, message): self.text_debug.append(message) while self.text_debug.document().blockCount() > 1000: cursor = QTextCursor(self.text_debug.document().begin()) cursor.select(QTextCursor.BlockUnderCursor) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) cursor.removeSelectedText() if self.checkbox_debug_auto_scroll.isChecked(): self.text_debug.verticalScrollBar().setValue(self.text_debug.verticalScrollBar().maximum()) def btn_clear_debug_clicked(self): self.text_debug.clear() def highlight_debug_tab(self): """ SIGNAL function: Highlight the debug tab when an error occurs. """ if not self.tab_debug_warning and self.tab_widget.currentIndex() != self.tab_widget.indexOf(self.tab_debug): self.tab_debug_warning = True self.tab_set(self.tab_widget.indexOf(self.tab_debug), QColor(255, 0, 0), get_resources_path("warning-icon-16.png")) def table_add_row(self, csv_data): """ SIGNAL function: Adds new CSV Data into the Table. """ rows = self.model_data.rowCount() while rows >= 1000: self.model_data.removeRow(0) rows = self.model_data.rowCount() row_number = None if rows > 0: try: row_number = int(self.model_data.headerData(rows - 1, Qt.Vertical)) except ValueError: pass self.model_data.appendRow([QStandardItem(csv_data.timestamp), QStandardItem(csv_data.name), QStandardItem(csv_data.uid), QStandardItem(csv_data.var_name), QStandardItem(str(csv_data.raw_data)), QStandardItem(csv_data.var_unit)]) if row_number != None: self.model_data.setHeaderData(rows, Qt.Vertical, str(row_number + 1)) if self.checkbox_data_auto_scroll.isChecked(): self.table_data.scrollToBottom()