class QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.qSplitter) self.__ColCount = 6 self.itemModel = QStandardItemModel(5, self.__ColCount, self) self.selectionModel = QItemSelectionModel(self.itemModel) self.selectionModel.currentChanged.connect(self.do_curChanged) self.__lastColumnTitle = "测井取样" self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) self.ui.qTableView.setModel(self.itemModel) self.ui.qTableView.setSelectionModel(self.selectionModel) oneOrMore = QAbstractItemView.ExtendedSelection self.ui.qTableView.setSelectionMode(oneOrMore) itemOrRow = QAbstractItemView.SelectItems self.ui.qTableView.setSelectionBehavior(itemOrRow) self.ui.qTableView.verticalHeader().setDefaultSectionSize(22) self.ui.qTableView.setAlternatingRowColors(True) self.ui.qTableView.setEnabled(False) self.qLabel1 = QLabel("当前单元格:", self) self.qLabel1.setMinimumWidth(180) self.qLabel2 = QLabel("单元格内容:", self) self.qLabel2.setMinimumWidth(150) self.qLabel3 = QLabel("当前文件:", self) self.ui.qStatusBar.addWidget(self.qLabel1) self.ui.qStatusBar.addWidget(self.qLabel2) self.ui.qStatusBar.addPermanentWidget(self.qLabel3) def __iniModelFromStringList(self, allLines): rowCnt = len(allLines) self.itemModel.setRowCount(rowCnt - 1) headerText = allLines[0].strip() headerList = headerText.split("\t") self.itemModel.setHorizontalHeaderLabels(headerList) self.__lastColumnTitle = headerList[len(headerList) - 1] lastColNo = self.__ColCount - 1 for i in range(rowCnt - 1): lineText = allLines[i + 1].strip() strList = lineText.split("\t") for j in range(self.__ColCount - 1): item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(True) if (strList[lastColNo] == "0"): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) def __setCellAlignment(self, align=Qt.AlignHCenter): if (not self.selectionModel.hasSelection()): return selectedIndex = self.selectionModel.selectedIndexes() count = len(selectedIndex) for i in range(count): index = selectedIndex[i] item = self.itemModel.itemFromIndex(index) item.setTextAlignment(align) @pyqtSlot() def on_qAction1_triggered(self): # 打开文件 curPath = os.getcwd() filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.qLabel3.setText("当前文件:" + filename) self.ui.qPlainTextEdit.clear() aFile = open(filename, 'r') allLines = aFile.readlines() aFile.close() for strLine in allLines: self.ui.qPlainTextEdit.appendPlainText(strLine.strip()) self.__iniModelFromStringList(allLines) self.ui.qTableView.setEnabled(True) self.ui.qAction2.setEnabled(True) self.ui.qAction3.setEnabled(True) self.ui.qAction4.setEnabled(True) self.ui.qAction5.setEnabled(True) self.ui.qAction6.setEnabled(True) @pyqtSlot() def on_qAction2_triggered(self): # 另存文件 curPath = os.getcwd() filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.on_qAction3_triggered() aFile = open(filename, "w") aFile.write(self.ui.qPlainTextEdit.toPlainText()) aFile.close() @pyqtSlot() def on_qAction4_triggered(self): # 添加行 itemList = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemList.append(item) item = QStandardItem(self.__lastColumnTitle) item.setCheckable(True) item.setFlags(self.__lastColumnFlags) itemList.append(item) self.itemModel.appendRow(itemList) curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_qAction5_triggered(self): # 插入行 itemlist = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(True) item.setCheckState(Qt.Checked) itemlist.append(item) curIndex = self.selectionModel.currentIndex() self.itemModel.insertRow(curIndex.row(), itemlist) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_qAction6_triggered(self): # 删除行 curIndex = self.selectionModel.currentIndex() self.itemModel.removeRow(curIndex.row()) @pyqtSlot() def on_qAction7_triggered(self): # 居左 self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() def on_qAction8_triggered(self): # 居中 self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() def on_qAction9_triggered(self): # 居右 self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) def on_qAction10_triggered(self, checked): # 粗体 if (not self.selectionModel.hasSelection()): return selectedIndex = self.selectionModel.selectedIndexes() count = len(selectedIndex) for i in range(count): index = selectedIndex[i] item = self.itemModel.itemFromIndex(index) font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() def on_qAction3_triggered(self): # 模型数据 self.ui.qPlainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): item = self.itemModel.horizontalHeaderItem(i) lineStr = lineStr + item.text() + "\t" item = self.itemModel.horizontalHeaderItem(self.__ColCount - 1) lineStr = lineStr + item.text() self.ui.qPlainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): lineStr = "" for j in range(self.itemModel.columnCount() - 1): item = self.itemModel.item(i, j) lineStr = lineStr + item.text() + "\t" item = self.itemModel.item(i, self.__ColCount - 1) if (item.checkState() == Qt.Checked): lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.qPlainTextEdit.appendPlainText(lineStr) def do_curChanged(self, current, previous): if (current != None): text = "当前单元格:%d行,%d列" % (current.row(), current.column()) self.qLabel1.setText(text) item = self.itemModel.itemFromIndex(current) self.qLabel2.setText("单元格内容:" + item.text()) font = item.font() self.ui.qAction10.setChecked(font.bold())
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) self.__buildStatusBar() self.COL_COUNT = 6 #常数,列数=6 self.itemModel = QStandardItemModel(5, self.COL_COUNT, self) # 数据模型,10行6列 ## headerList=["测深(m)","垂深(m)","方位(°)","总位移(m)","固井质量","测井取样"] ## self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头文字 self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型 self.selectionModel.currentChanged.connect(self.do_currentChanged) self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) self.__lastColumnTitle = "测井取样" #为tableView设置数据模型 self.ui.tableView.setModel(self.itemModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型 oneOrMore = QAbstractItemView.ExtendedSelection self.ui.tableView.setSelectionMode(oneOrMore) #可多选 itemOrRow = QAbstractItemView.SelectItems self.ui.tableView.setSelectionBehavior(itemOrRow) #单元格选择 self.ui.tableView.verticalHeader().setDefaultSectionSize(22) #缺省行高 self.ui.tableView.setEnabled(False) #先禁用tableView ## self.__resetTable(5) #创建自定义代理组件并设置 self.spinCeShen = QmyFloatSpinDelegate(0, 10000, 0, self) #用于 测深 self.spinLength = QmyFloatSpinDelegate(0, 6000, 2, self) #垂深,总位移 self.spinDegree = QmyFloatSpinDelegate(0, 360, 1, self) #用于 方位 self.ui.tableView.setItemDelegateForColumn(0, self.spinCeShen) #测深 self.ui.tableView.setItemDelegateForColumn(1, self.spinLength) #垂深 self.ui.tableView.setItemDelegateForColumn(3, self.spinLength) #总位移 self.ui.tableView.setItemDelegateForColumn(2, self.spinDegree) #方位角 qualities = ["优", "良", "合格", "不合格"] self.comboDelegate = QmyComboBoxDelegate(self) self.comboDelegate.setItems(qualities, False) #不可编辑 self.ui.tableView.setItemDelegateForColumn(4, self.comboDelegate) #固井质量 ## ==============自定义功能函数============ def __buildStatusBar(self): ##构建状态栏 self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(150) self.ui.statusBar.addWidget(self.LabCellText) self.LabCurFile = QLabel("当前文件:", self) self.ui.statusBar.addPermanentWidget(self.LabCurFile) ## def __resetTable(self,tableRowCount): #复位数据表,必须为数值设置0,否则代理组件出错 ## self.itemModel.removeRows(0,self.itemModel.rowCount()) #删除所有行 ## self.itemModel.setRowCount(tableRowCount) #设置新的行数 ## ## for i in range(tableRowCount): #设置最后一列 ## for j in range(self.COL_COUNT-1): ## index=self.itemModel.index(i,j) #获取模型索引 ## item=self.itemModel.itemFromIndex(index) #获取item ## item.setData(0,Qt.DisplayRole) #数值必须初始化为0 ## item.setTextAlignment(Qt.AlignHCenter) ## ## index=self.itemModel.index(i,self.COL_COUNT-1) #获取模型索引 ## item=self.itemModel.itemFromIndex(index) #获取item ## item.setCheckable(True) ## item.setData(self.__lastColumnTitle,Qt.DisplayRole) ## item.setEditable(False) #不可编辑 def __iniModelFromStringList(self, allLines): ##从字符串列表构建模型 rowCnt = len(allLines) #文本行数,第1行是标题 self.itemModel.setRowCount(rowCnt - 1) #实际数据行数 headerText = allLines[0].strip() #第1行是表头,去掉末尾的换行符 "\n" headerList = headerText.split("\t") self.itemModel.setHorizontalHeaderLabels(headerList) self.__lastColumnTitle = headerList[len(headerList) - 1] # 最后一列表头的标题,即“测井取样” lastColNo = self.COL_COUNT - 1 #最后一列的列号 for i in range(rowCnt - 1): lineText = allLines[i + 1].strip() #一行的文字,\t分隔 strList = lineText.split("\t") for j in range(self.COL_COUNT - 1): #不含最后一列 item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) if (strList[lastColNo] == "0"): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) #设置最后一列的item def __setCellAlignment(self, align=Qt.AlignHCenter): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #列表类型 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 item.setTextAlignment(align) #设置文字对齐方式 ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##“打开文件” def on_actOpen_triggered(self): curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.LabCurFile.setText("当前文件:" + filename) self.ui.plainTextEdit.clear() aFile = open(filename, 'r') allLines = aFile.readlines() #读取所有行,list类型,每行末尾带有 \n ## for eachLine in aFile: #每次读取一行 ## self.ui.plainTextEdit.appendPlainText(eachLine) aFile.close() for strLine in allLines: self.ui.plainTextEdit.appendPlainText(strLine.strip()) self.__iniModelFromStringList(allLines) self.ui.actAppend.setEnabled(True) #更新Actions的enable属性 self.ui.actInsert.setEnabled(True) self.ui.actDelete.setEnabled(True) self.ui.actSave.setEnabled(True) self.ui.actModelData.setEnabled(True) self.ui.tableView.setEnabled(True) #启用tableView @pyqtSlot() ##保存文件 def on_actSave_triggered(self): curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.on_actModelData_triggered() #更新数据到plainTextEdit aFile = open(filename, 'w') #以写方式打开 aFile.write(self.ui.plainTextEdit.toPlainText()) aFile.close() @pyqtSlot() def on_actAppend_triggered(self): #在最后添加一行 itemlist = [] # 列表 for i in range(self.COL_COUNT - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setCheckable(True) item.setFlags(self.__lastColumnFlags) itemlist.append(item) self.itemModel.appendRow(itemlist) curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##插入一行 def on_actInsert_triggered(self): itemlist = [] # 列表 for i in range(self.COL_COUNT - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) item.setCheckState(Qt.Checked) itemlist.append(item) curIndex = self.selectionModel.currentIndex() #获取当前选中项的模型索引 self.itemModel.insertRow(curIndex.row(), itemlist) #在当前行的前面插入一行 self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除当前行 def on_actDelete_triggered(self): curIndex = self.selectionModel.currentIndex() #获取当前选择单元格的模型索引 self.itemModel.removeRow(curIndex.row()) # //删除当前行 @pyqtSlot() ##左对齐 def on_actAlignLeft_triggered(self): self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() ##中间对齐 def on_actAlignCenter_triggered(self): self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() ##右对齐 def on_actAlignRight_triggered(self): self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) ##字体Bold def on_actFontBold_triggered(self, checked): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #列表类型 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() ##模型数据显示到plainTextEdit里 def on_actModelData_triggered(self): self.ui.plainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): #表头 item = self.itemModel.horizontalHeaderItem(i) lineStr = lineStr + item.text() + "\t" item = self.itemModel.horizontalHeaderItem(self.COL_COUNT - 1) #最后一列 lineStr = lineStr + item.text() self.ui.plainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): lineStr = "" for j in range(self.itemModel.columnCount() - 1): #不包括最后一列 item = self.itemModel.item(i, j) lineStr = lineStr + item.text() + "\t" item = self.itemModel.item(i, self.COL_COUNT - 1) #最后一列 if (item.checkState() == Qt.Checked): lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.plainTextEdit.appendPlainText(lineStr) ## =============自定义槽函数=============================== 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的文字内容 font = item.font() #获取item的字体 self.ui.actFontBold.setChecked(font.bold()) #更新actFontBold的check状态
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建Ui对象 self.ui.setupUi(self) #构造UI self.__ColCount = 6 #列数 self.itemModel = QStandardItemModel( 10, self.__ColCount, self) #创建QStandardItemModel类型的数据模型,指定行列值 ''' setSelectionBehavior() 此属性保存视图使用的选择行为。 此属性保存选择是根据单个项目,行还是列进行的 #QItemSelectionModel() 此属性保存视图在哪种选择模式下运行。 #此属性控制用户是否可以选择一个或多个项目,并且在多个项目选择中控制选择是否必须是连续范围的项目 ''' self.selectionModel = QItemSelectionModel(self.itemModel) self.selectionModel.currentChanged.connect( self.do_curChanged) #单元格选择发生变化时会发射此信号 self.__lastColumnTitle = "" #设置最后一列的标题,可以是空 self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) #设置tableView属性 self.ui.tableView.setModel(self.itemModel) #数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #选择模型 oneOrMore = QAbstractItemView.ExtendedSelection #选择模式->多选模式 self.ui.tableView.setSelectionMode(oneOrMore) #多选模式 itemOrRow = QAbstractItemView.SelectItems #项选择模式->单元格选择 self.ui.tableView.setSelectionBehavior(itemOrRow) #单元格选择 self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.setAlternatingRowColors(True) #交替行颜色 self.ui.tableView.setEnabled(False) #设置默认禁用tabelView self.ui.actFontBold.setCheckable(False) self.setCentralWidget(self.ui.splitter) #设置中心组件 # self.setCentralWidget(self.ui.tableView) self.__buildStatusBar() self.spinCeshen = QmyFloatSpinDelegate(0, 10000, 0, self) self.spinLength = QmyFloatSpinDelegate(0, 6000, 2, self) self.spinDegree = QmyFloatSpinDelegate(0, 360, 1, self) self.ui.tableView.setItemDelegateForColumn(0, self.spinCeshen) self.ui.tableView.setItemDelegateForColumn(1, self.spinLength) self.ui.tableView.setItemDelegateForColumn(3, self.spinLength) self.ui.tableView.setItemDelegateForColumn(2, self.spinDegree) qualities = ["优", "良", "合格", "不合格"] self.comboDelegate = QmyComboBoxDelegate(self) self.comboDelegate.setItems(qualities, False) self.ui.tableView.setItemDelegateForColumn(4, self.comboDelegate) ##==========自定义功能函数========== def __buildStatusBar(self): ''' 设置状态栏ui组件 :return: ''' self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(150) self.ui.statusBar.addWidget(self.LabCellText) self.LabCurFile = QLabel("当前文件:", self) self.ui.statusBar.addWidget(self.LabCurFile) def __iniModelFromStringList(self, allLines): rowCnt = len(allLines) #获取总行数 self.itemModel.setRowCount(rowCnt - 1) #除去表头的数据行数 headerText = allLines[0].strip() #表头去除换行符,文件呢的空格使用Tab代替 headerList = headerText.split("\t") #按照制表符转化为列表 self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头标题 # print(headerList) self.__lastColumnTitle = headerList[len(headerList) - 1] #最后一列的标题 lastColNo = self.__ColCount - 1 #最后一列的列号 for i in range(rowCnt - 1): #除去表头的数据行数 # print(i) lineText = allLines[i + 1].strip() #去除换行符,不包括表头 strList = lineText.split("\t") #按制表符生成列表 for j in range(self.__ColCount - 1): #不包括最后一列 ''' QStandardItem是一个数据结构,他可以存储一个cell的各种信息,比如文本、图标、是否可选、字体、别景色、前景色等等。 并且QStandardItem可以有孩子和兄弟,他是为model提供数据存储的节点。 QTableView:作为表格cell时,有一个作为根节点的QStandardItem,其他节点都是QStandardItem节点的孩子节点,并且都是兄弟节点(这里暂时不考虑多列的情况)。 QTreeView:作为树节点cell时,有一个作为根节点的QStandardItem,其他节点都是他的孩子节点,但是其他节点也可以作为父节点存在(这里暂时不考虑多列的情况)。 ''' item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) #设置最后一行 # print(self.__lastColumnTitle) item = QStandardItem(self.__lastColumnTitle) #将最后一行的表头 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) # print(strList[lastColNo]) if strList[lastColNo] == '0': #对比文本内的数值进行设定,类型是字符串 item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) def __setCellAlignment(self, align=Qt.AlignHCenter): if not self.selectionModel.hasSelection(): return ''' selectedIndexes()返回一个元素为QModelIndex类型的列表,包括所有被选中的单元格的模型索引 ''' selectdIndex = self.selectionModel.selectedIndexes() # print(selectdIndex) count = len(selectdIndex) for i in range(count): index = selectdIndex[i] ''' itemFromIndex(index)返回的是模型索引为index的QStandardItem对象 ''' item = self.itemModel.itemFromIndex(index) item.setTextAlignment(align) ##==========事件处理函数=========== ##==========由connectSlotsByName()自动关联的槽函数==== @pyqtSlot() def on_actOpenFile_triggered(self): curPath = os.getcwd() #获取当前路径 # print(curPath)? #flt是文件过滤器 filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if filename == "": return self.LabCurFile.setText(("当前文件: " + filename)) #设置状态栏文本 self.ui.plainTextEdit.clear() aFile = open(filename, "r") allLines = aFile.readlines() aFile.close() for strLine in allLines: self.ui.plainTextEdit.appendPlainText( strLine.strip()) #按照行添加到plainTextEdit中 self.__iniModelFromStringList(allLines) #设置激活状态 self.ui.tableView.setEnabled(True) self.ui.actAppend.setEnabled(True) self.ui.actInsert.setEnabled(True) self.ui.actDel.setEnabled(True) self.ui.actSaveFile.setEnabled(True) self.ui.actModelData.setEnabled(True) self.ui.actFontBold.setCheckable(True) #设置加粗可用 @pyqtSlot() def on_actAppend_triggered(self): itemlist = [] for i in range(self.__ColCount - 1): #循环一行中的各个列,不包括最后一列 item = QStandardItem("0") #添加0到数据结构中 itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #将最后一行的表头添加入数据结构 item.setCheckable(True) #可选 item.setFlags(self.__lastColumnFlags) itemlist.append(item) #添加到itemlist内(添加到最后一个) self.itemModel.appendRow(itemlist) curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) #获取最后一行第一个单元格的模型索引 self.selectionModel.clearSelection() #清除选择 self.selectionModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) #设置在添加后自动选择添加行的第一个单元格(可从状态栏确认) @pyqtSlot() def on_actInsert_triggered(self): ''' 插入行 :return: ''' itemlist = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(False) #是否可以修改选择 item.setCheckState(Qt.Checked) #是否选中 itemlist.append(item) curIndex = self.selectionModel.currentIndex() #选中项的模型索引,包括行列等其他信息 self.itemModel.insertRow(curIndex.row(), itemlist) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_actDel_triggered(self): ''' 删除行 :return: ''' curIndex = self.selectionModel.currentIndex() self.itemModel.removeRow(curIndex.row()) @pyqtSlot() def on_actAlignLeft_triggered(self): self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() def on_actAlignCenter_triggered(self): self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() def on_actAlignRight_triggered(self): self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) def on_actFontBold_triggered(self, checked): print("1,checked", checked) if not self.selectionModel.hasSelection(): return selectIndex = self.selectionModel.selectedIndexes() for i in range(len(selectIndex)): index = selectIndex[i] item = self.itemModel.itemFromIndex(index) font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() def on_actModelData_triggered(self): self.ui.plainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): #不包括最后一列的表头遍历 item = self.itemModel.horizontalHeaderItem(i) #返回行标题的一个数据项对象,i是列号 lineStr = lineStr + item.text() + "\t" #给每个项对象的文本添加Tab制表符 item = self.itemModel.horizontalHeaderItem(self.__ColCount - 1) #最后一列的表头 lineStr = lineStr + item.text() self.ui.plainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): #获取行总数,不包括表头行 lineStr = "" for j in range(self.itemModel.columnCount() - 1): #不包括最后一列的遍历 item = self.itemModel.item(i, j) #按照行列号获取工作区内项对象 lineStr = lineStr + item.text() + "\t" #项对象文本添加制表符 item = self.itemModel.item(i, self.__ColCount - 1) #按照行号获取最后一列的项对象 if item.checkState() == Qt.Checked: #匹配状态是否为选中 lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.plainTextEdit.appendPlainText(lineStr) @pyqtSlot() def on_actSaveFile_triggered(self): ''' 保存文件 :return: ''' curPath = os.getcwd() filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if filename == "": return self.on_actModelData_triggered() aFile = open(filename, "w") aFile.write(self.ui.plainTextEdit.toPlainText()) aFile.close() ##=========自定义槽函数============ def do_curChanged(self, current, previous): ''' 设置状态栏组件显示内容 :param current: :param previous: :return: ''' if current != None: text = " 当前单元格: %d行,%d列" % (current.row() + 1, current.column() + 1) self.LabCellPos.setText(text) item = self.itemModel.itemFromIndex(current) self.LabCellText.setText("单元格内容:" + item.text()) font = item.font() self.ui.actFontBold.setChecked( font.bold()) #设置按钮按下,当font.Bold的值大于50式font.blod()会为True
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) self.__ColCount = 6 #列数=6 self.itemModel = QStandardItemModel(5, self.__ColCount, self) # 数据模型,10行6列 self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型 self.selectionModel.currentChanged.connect(self.do_curChanged) self.__lastColumnTitle = "测井取样" self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) ##tableView设置 self.ui.tableView.setModel(self.itemModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型 oneOrMore = QAbstractItemView.ExtendedSelection self.ui.tableView.setSelectionMode(oneOrMore) #可多选 itemOrRow = QAbstractItemView.SelectItems self.ui.tableView.setSelectionBehavior(itemOrRow) #单元格选择 self.ui.tableView.verticalHeader().setDefaultSectionSize(22) #缺省行高 self.ui.tableView.setAlternatingRowColors(True) #交替行颜色 self.ui.tableView.setEnabled(False) #先禁用tableView self.__buildStatusBar() ## ==============自定义功能函数============ def __buildStatusBar(self): ##构建状态栏 self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(150) self.ui.statusBar.addWidget(self.LabCellText) self.LabCurFile = QLabel("当前文件:", self) self.ui.statusBar.addPermanentWidget(self.LabCurFile) def __iniModelFromStringList(self, allLines): ##从字符串列表构建模型 rowCnt = len(allLines) #文本行数,第1行是标题 self.itemModel.setRowCount(rowCnt - 1) #实际数据行数 headerText = allLines[0].strip() #第1行是表头,去掉末尾的换行符 "\n" headerList = headerText.split("\t") #转换为字符串列表 self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头标题 self.__lastColumnTitle = headerList[len(headerList) - 1] # 最后一列表头的标题,即“测井取样” lastColNo = self.__ColCount - 1 #最后一列的列号 for i in range(rowCnt - 1): lineText = allLines[i + 1].strip() #一行的文字,\t分隔 strList = lineText.split("\t") #分割为字符串列表 for j in range(self.__ColCount - 1): #不含最后一列 item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) #设置模型的item item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) if (strList[lastColNo] == "0"): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) #设置最后一列的item def __setCellAlignment(self, align=Qt.AlignHCenter): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #模型索引列表 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 item.setTextAlignment(align) #设置文字对齐方式 ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##“打开文件” def on_actOpen_triggered(self): ## curPath=QDir.currentPath() #获取当前路径 curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.LabCurFile.setText("当前文件:" + filename) self.ui.plainTextEdit.clear() aFile = open(filename, 'r') allLines = aFile.readlines() #读取所有行,list类型,每行末尾带有 \n aFile.close() for strLine in allLines: self.ui.plainTextEdit.appendPlainText(strLine.strip()) self.__iniModelFromStringList(allLines) self.ui.tableView.setEnabled(True) #tableView可用 self.ui.actAppend.setEnabled(True) #更新Actions的enable属性 self.ui.actInsert.setEnabled(True) self.ui.actDelete.setEnabled(True) self.ui.actSave.setEnabled(True) self.ui.actModelData.setEnabled(True) @pyqtSlot() ##保存文件 def on_actSave_triggered(self): ## curPath=QDir.currentPath() #获取当前路径 curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.on_actModelData_triggered() #更新数据到plainTextEdit aFile = open(filename, 'w') #以写方式打开 aFile.write(self.ui.plainTextEdit.toPlainText()) aFile.close() @pyqtSlot() ##在最后添加一行 def on_actAppend_triggered(self): itemlist = [] # QStandardItem 对象列表 for i in range(self.__ColCount - 1): #不包括最后一列 item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setCheckable(True) item.setFlags(self.__lastColumnFlags) itemlist.append(item) self.itemModel.appendRow(itemlist) #添加一行 curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##插入一行 def on_actInsert_triggered(self): itemlist = [] # QStandardItem 对象列表 for i in range(self.__ColCount - 1): #不包括最后一列 item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) item.setCheckState(Qt.Checked) itemlist.append(item) curIndex = self.selectionModel.currentIndex() #获取当前选中项的模型索引 self.itemModel.insertRow(curIndex.row(), itemlist) #在当前行的前面插入一行 self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除当前行 def on_actDelete_triggered(self): curIndex = self.selectionModel.currentIndex() #获取当前选择单元格的模型索引 self.itemModel.removeRow(curIndex.row()) #删除当前行 @pyqtSlot() ##左对齐 def on_actAlignLeft_triggered(self): self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() ##中间对齐 def on_actAlignCenter_triggered(self): self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() ##右对齐 def on_actAlignRight_triggered(self): self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) ##字体Bold def on_actFontBold_triggered(self, checked): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #模型索引列表 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() ##模型数据显示到plainTextEdit里 def on_actModelData_triggered(self): self.ui.plainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): #表头,不含最后一列 item = self.itemModel.horizontalHeaderItem(i) lineStr = lineStr + item.text() + "\t" item = self.itemModel.horizontalHeaderItem(self.__ColCount - 1) #最后一列 lineStr = lineStr + item.text() #表头文字字符串 self.ui.plainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): lineStr = "" for j in range(self.itemModel.columnCount() - 1): #不包括最后一列 item = self.itemModel.item(i, j) lineStr = lineStr + item.text() + "\t" item = self.itemModel.item(i, self.__ColCount - 1) #最后一列 if (item.checkState() == Qt.Checked): lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.plainTextEdit.appendPlainText(lineStr) ## =============自定义槽函数=============================== def do_curChanged(self, current, previous): if (current != None): #当前模型索引有效 text = "当前单元格:%d行,%d列" % (current.row(), current.column()) self.LabCellPos.setText(text) item = self.itemModel.itemFromIndex(current) #从模型索引获得Item self.LabCellText.setText("单元格内容:" + item.text()) #显示item的文字内容 font = item.font() #获取item的字体 self.ui.actFontBold.setChecked(font.bold()) #更新actFontBold的check状态
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, ): super(MainWindow, self).__init__() self.setupUi(self) self.fig_dict = {} self.tabWidget.setCurrentIndex(0) self.plotAreaVerticalLayout = QtWidgets.QVBoxLayout() self.plotsFrame.setLayout(self.plotAreaVerticalLayout) #initialize Table self.headerV = self.tableWidget.verticalHeader() self.headerV.show() # add a widget for previewing plots, they can then be added to the actual plot self.plotPreview = PlotWidget() self.plotAreaVerticalLayout.addWidget(self.plotPreview) # Create tree model to store sim data items and connect it to views self.simDataItemTreeModel = SimDataItemTreeModel() self.bdTableModel = BdTableModel() self.bdUserGeneratedTableModel = BdUserGeneratedCurvesTableModel() self.simDataItemTreeView.setModel(self.simDataItemTreeModel) self.plotPreview.tableView.setModel(self.bdTableModel) # connect a double clicked section of the bd table to a change of the anchor self.plotPreview.tableView.horizontalHeader( ).sectionDoubleClicked.connect(self.update_bd_table) self.plotPreview.tableView.verticalHeader( ).sectionDoubleClicked.connect( self.update_bd_user_generated_curves_table) # Set custom selection model, so that sub items are automatically # selected if parent is selected self._selection_model = QRecursiveSelectionModel( self.simDataItemTreeView.model()) self.simDataItemTreeView.setSelectionModel(self._selection_model) # Connect list view with model for the selected values of tree view self.selectedSimulationDataItemListModel = OrderedDictModel() self.simDataItemListView.setModel( self.selectedSimulationDataItemListModel) self._selection_model.selectionChanged.connect(self.change_list) # set up signals and slots self.selectedSimulationDataItemListModel.items_changed.connect( self.update_variable_tree) # Connect signals of menus self.actionOpen_File.triggered.connect( self.simDataItemTreeView.add_file) self.actionOpen_Directory.triggered.connect( self.simDataItemTreeView.add_folder) self.actionOpen_Directory_List.triggered.connect( self.simDataItemTreeView.add_folder_list) self.actionHide_PlotSettings.triggered.connect( self.set_plot_settings_visibility) self.actionHide_Sequence.triggered.connect( self.set_sequence_widget_visibility) self.actionHide_Status.triggered.connect( self.set_status_widget_visibility) self.actionSave_Table.triggered.connect(self.save_bd_table) self.actionExport_Figure_as_Tikzpicture.triggered.connect( self.plotPreview.export_plot_tikz) self.actionExport_TableWidget.triggered.connect( self.export_table_to_csv) self.actionSave_Data.triggered.connect(self.save_current_selection) self.action_About.triggered.connect(self.open_about_page) self.variableTreeModel = VariableTreeModel() self.variableTreeView.setModel(self.variableTreeModel) self.plotsettings.visibilityChanged.connect( self.plot_settings_visibility_changed) self.sequenceWidget.visibilityChanged.connect( self.sequence_widget_visibility_changed) self.statusWidget.visibilityChanged.connect( self.status_widget_visibility_changed) # Set recursive selection model for variable view self._variable_tree_selection_model = QRecursiveSelectionModel( self.variableTreeView.model()) self.variableTreeView.setSelectionModel( self._variable_tree_selection_model) # set up combo boxes for rate/psnr and interpolation options self.combo_interp.addItems(["pchip", "pol"]) self.combo_rate_psnr.addItems(["drate", "dsnr"]) self.combo_interp.currentIndexChanged.connect(self.on_combo_box) self.combo_rate_psnr.currentIndexChanged.connect(self.on_combo_box) # set up bd plot checkbox self.checkBox_bdplot.stateChanged.connect(self.update_bd_plot) self.curveWidget.hide() self.curveListModel = OrderedDictModel() self.curveListView.setModel(self.curveListModel) self.curveListSelectionModel = QItemSelectionModel(self.curveListModel) self.curveListView.setSelectionModel(self.curveListSelectionModel) self.curveListView.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection) self.curveWidget.visibilityChanged.connect( self.curve_widget_visibility_changed) self.actionGenerate_curve.triggered.connect(self.generate_new_curve) self.actionRemove_items.triggered.connect(self.remove) self.actionReload_files.triggered.connect(self.reload_files) self.settings = QSettings() self.get_recent_files() self.simDataItemTreeView.itemsOpened.connect(self.add_recent_files) self.watcher = QFileSystemWatcher(self) self.watcher.fileChanged.connect(self.warning_file_change) self.watcher.directoryChanged.connect(self.warning_file_change) self.simDataItemTreeView.parserThread.newParsedData.connect( self.add_files_to_watcher) self.show_file_changed_message = True self.reset_timer = QTimer(self) self.reset_timer.setSingleShot(True) self.reset_timer.setInterval(15000) self.reset_timer.timeout.connect(self._reset_file_changed_message) self.simDataItemTreeView.customContextMenuRequested.connect( self.show_sequences_context_menu) # self.curveListView.actionCalculateBD.triggered.connect(self.bd_user_generated_curves) # sets Visibility for the Plotsettings Widget def set_plot_settings_visibility(self): self.plotsettings.visibilityChanged.disconnect( self.plot_settings_visibility_changed) if self.plotsettings.isHidden(): self.plotsettings.setVisible(True) else: self.plotsettings.setHidden(True) self.plotsettings.visibilityChanged.connect( self.plot_settings_visibility_changed) # updates the QAction if Visibility is changed def plot_settings_visibility_changed(self): if self.plotsettings.isHidden(): self.actionHide_PlotSettings.setChecked(True) else: self.actionHide_PlotSettings.setChecked(False) self._variable_tree_selection_model.selectionChanged.connect( self.update_plot) self.curveListSelectionModel.selectionChanged.connect(self.update_plot) self.simDataItemTreeView.deleteKey.connect(self.remove) # sets Visibility for the Sequence Widget def set_sequence_widget_visibility(self): self.sequenceWidget.visibilityChanged.disconnect( self.sequence_widget_visibility_changed) if self.sequenceWidget.isHidden(): self.sequenceWidget.setVisible(True) else: self.sequenceWidget.setHidden(True) self.sequenceWidget.visibilityChanged.connect( self.sequence_widget_visibility_changed) def sequence_widget_visibility_changed(self): if self.sequenceWidget.isHidden(): self.actionHide_Sequence.setChecked(True) else: self.actionHide_Sequence.setChecked(False) # Sets Visibility for the Status Widget def set_status_widget_visibility(self): self.statusWidget.visibilityChanged.disconnect( self.status_widget_visibility_changed) if self.statusWidget.isHidden(): self.statusWidget.setVisible(True) else: self.statusWidget.setHidden(True) self.statusWidget.visibilityChanged.connect( self.status_widget_visibility_changed) def status_widget_visibility_changed(self): if self.statusWidget.isHidden(): self.actionHide_Status.setChecked(True) else: self.actionHide_Status.setChecked(False) def curve_widget_visibility_changed(self): if self.curveWidget.isHidden(): self.curveListView.delete_key.disconnect() else: self.curveListView.delete_key.connect(self.remove_curves) def remove(self): values = self.selectedSimulationDataItemListModel.values() for value in values: self.watcher.removePath(value.path) # List call necessary to avoid runtime error because of elements changing # during iteration self._variable_tree_selection_model.selectionChanged.disconnect() # disconnect slot to avoid multiple function triggers by selectionChanged signal # not disconnecting slows program down significantly self._selection_model.selectionChanged.disconnect(self.change_list) self.simDataItemTreeModel.remove(list(values)) self.change_list(QItemSelection(), QItemSelection()) self._selection_model.selectionChanged.connect(self.change_list) self._variable_tree_selection_model.selectionChanged.connect( self.update_plot) if len(self.selectedSimulationDataItemListModel.values()) == 0: self.update_plot() def change_list(self, q_selected, q_deselected): """Extend superclass behavior by automatically adding the values of all selected items in :param: `q_selected` to value list model. """ selected_q_indexes = q_deselected.indexes() q_reselect_indexes = [] for q_index in self.simDataItemTreeView.selectedIndexes(): if q_index not in selected_q_indexes: q_reselect_indexes.append(q_index) # Find all all values that are contained by selected tree items tuples = [] for q_index in q_selected.indexes() + q_reselect_indexes: # Add values, ie. sim data items stored at the item, to the list # model. sim_data_items = q_index.internalPointer().values tuples.extend((e.path, e) for e in sim_data_items) # Overwrite all elements in dictionary by selected values # Note, that overwriting only issues one `updated` signal, and thus, # only rerenders the plots one time. Therefore, simply overwriting # is much more efficient, despite it would seem, that selectively # overwriting keys is. self.selectedSimulationDataItemListModel.clear_and_update_from_tuples( tuples) def get_selected_simulation_data_items(self): return [ self.selectedSimulationDataItemListModel[key] for key in self.selectedSimulationDataItemListModel ] def get_plot_data_collection_from_selected_variables(self): """Get a :class: `dict` with y-variable as key and values as items from the current selection of the variable tree. :rtype: :class: `dict` of :class: `string` and :class: `list` """ plot_data_collection = [] for q_index in self.variableTreeView.selectedIndexes(): item = q_index.internalPointer() if len(item.values) > 0: plot_data_collection.extend(item.values) return plot_data_collection def update_variable_tree(self): """Collect all SimDataItems currently selected, and create variable tree and corresponding data from it. Additionaly reselect all previously selected variables. """ # Create a list of all currently selected paths # Note, that *q_index* can not be used directly, because it might # change if the variable tree is recreated selected_path_collection = [] for q_index in self.variableTreeView.selectedIndexes(): selected_path_collection.append( # Create list of identifiers from path # Note, that the root node is excluded from the path [ item.identifier for item in q_index.internalPointer().path[1:] ]) # Join the data of all currently selected items to a dictionary # tree sim_data_items = self.get_selected_simulation_data_items() dict_tree = dict_tree_from_sim_data_items(sim_data_items) # check if qp values are the same # self.check_qp(sim_data_items) # Reset variable tree and update it with *dict_tree* self.variableTreeModel.clear_and_update_from_dict_tree(dict_tree) # Auto expand variable tree self.variableTreeView.expandToDepth(1) # Reselect all variables, which also exist on the new tree for path in selected_path_collection: # Try to reselect, and do nothing, if path does not exist anymore try: # Reselect new item corresponding to the previously selected # path item = self.variableTreeModel.get_item_from_path(*path) self.variableTreeView.selectionModel().select( self.variableTreeModel._get_index_from_item(item), QItemSelectionModel.Select, ) except KeyError: pass # TODO: it might be that some log files do not have a QP value, therefore the check_qp method must be # implemented in a way that these files are not affected # def check_qp(self, sim_data_items): # check if qp values are the same for each sequence # if len(sim_data_items) < 2: return # sim_data_items.sort(key=lambda item: (item.sequence)) # # qp_list,list = [],[] # seq = sim_data_items[0].sequence # config = sim_data_items[0].config # # for item in sim_data_items: # if ((seq == item.sequence) & (config == item.config)): # list.append(item.qp) # elif (seq == item.sequence): #same sequence different config # config = item.config # qp_list.append(list) # list = [] # list.append(item.qp) # else: # different sequence # seq = item.sequence # config = item.config # qp_list.append(list) # if not(all(list == qp_list[0] for list in qp_list)): # QtWidgets.QMessageBox.warning(self, "Warning", # "Be careful! You chose a sequence with different QP.") # return # list, qp_list = [], [] # list.append(item.qp) # qp_list.append(list) # # if not(all(list == qp_list[0] for list in qp_list )): # QtWidgets.QMessageBox.warning(self, "Warning", # "Be careful! You chose a sequence with different QP.") def check_labels(self): selectionmodel = self.variableTreeView.selectionModel() selected = self.variableTreeView.selectedIndexes() # return if no comparison needed if len(selected) < 2: return labelx = [] labely = [] for index in selected: x = index.internalPointer() if len(x.values) > 0: labelx.append(x.values[0].label[0]) labely.append(x.values[0].label[1]) if all(x == labelx[0] for x in labelx) and all(x == labely[0] for x in labely): return else: QtWidgets.QMessageBox.information( self, "Error!", "You should not choose curves with different units.") selectionmodel.clearSelection() # updates the plot if the plot variable is changed def update_plot(self): # user-generated curves and curves loaded from files are not supposed to be mixed user_generated_curves = False if self.sender() == self._variable_tree_selection_model or self.sender( ) == self.curveListSelectionModel: self.check_labels() data_collection = self.get_plot_data_collection_from_selected_variables( ) data_collection_user_generated = [] for index in self.curveListView.selectedIndexes(): data_collection_user_generated.append( self.curveListModel[index.data()]) else: return plot_data_collection = data_collection + data_collection_user_generated if len(data_collection_user_generated): self.plotPreview.tableView.setModel(self.bdUserGeneratedTableModel) self.plotPreview.change_plot(plot_data_collection, True) else: self.plotPreview.tableView.setModel(self.bdTableModel) self.update_table(data_collection) self.plotPreview.change_plot(plot_data_collection, False) if len(data_collection) and len(data_collection_user_generated): # don't mix user-generated and normal curves self.plotPreview.tableView.hide() self.plotPreview.label_warning.show() return self.plotPreview.tableView.show() self.plotPreview.label_warning.hide() self.plotPreview.tableView.model().update( plot_data_collection, self.combo_rate_psnr.currentText(), self.combo_interp.currentText(), not (self.checkBox_bdplot.isChecked())) def get_table_header(self, plot_data_collection): tmp_legend = [] tmp_config = [] # make legend for plot_data in plot_data_collection: tmp = [] for identifiers in plot_data.identifiers[1:]: tmp += identifiers.split(sep) tmp2 = tmp + plot_data.path tmp_legend.append(tmp2) tmp_config.append(tmp) legend = [] config = [] for c in tmp_legend: result = list( filter(lambda x: all(x in l for l in tmp_legend) == False, c)) if result == []: result = [plot_data.path[-1]] legend.append(" ".join(result)) if len(tmp_legend) == 1: legend = [plot_data.path[-1]] #make config for c in tmp_config: result = list( filter(lambda x: all(x in l for l in tmp_config) == False, c)) if ((set([" ".join(result)]) - set(config) != set()) & (result != [])): config.append(" ".join(result)) result = (legend, config) return result #updates the table def update_table(self, plot_data_collection): self.tableWidget.clear() self.tableWidget.setColumnCount(0) self.tableWidget.setRowCount(0) if plot_data_collection != []: if 'Temporal' in plot_data_collection[0].path: self.change_table_temporal(plot_data_collection) else: self.change_table_summary(plot_data_collection) self.tableWidget.resizeColumnsToContents() def change_table_temporal(self, plot_data_collect): plot_data_collection = plot_data_collect self.tableWidget.setRowCount(len(plot_data_collection)) plot_count = data_count = 0 data_names = [] plot_data_collection.sort( key=lambda plot_data: (plot_data.identifiers)) legend = self.get_table_header(plot_data_collection) header = legend[0] for plot_data in plot_data_collection: values = ((float(x), float(y)) for (x, y) in plot_data.values) sorted_value_pairs = sorted(values, key=lambda pair: pair[0]) [xs, ys] = list(zip(*sorted_value_pairs)) # make header if plot_data.identifiers[0] not in data_names: self.tableWidget.insertRow(plot_count) v_item = QtWidgets.QTableWidgetItem( str(plot_data.identifiers[0])) font = self.tableWidget.font() v_item.setData( 6, QtGui.QFont(self.tableWidget.font().setBold(True))) v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold)) self.tableWidget.setVerticalHeaderItem(plot_count, v_item) header_count = plot_count data_names.append(plot_data.identifiers[0]) plot_count += 1 # round data if plot_data.label[1] == 'dB': ys = tuple(map(lambda i: round(i, 1), ys)) self.tableWidget.horizontalHeader().setVisible(False) # fill up column per column for column_count in range(0, len(xs)): self.tableWidget.setCurrentCell(plot_count, column_count) if column_count > self.tableWidget.currentColumn(): self.tableWidget.insertColumn(column_count) self.tableWidget.setItem( plot_count, column_count, QtWidgets.QTableWidgetItem(str(ys[column_count]))) self.tableWidget.setVerticalHeaderItem( plot_count, QtWidgets.QTableWidgetItem( str(header[data_count]) + " [" + str(plot_data.label[1]) + "] ")) new_item = QtWidgets.QTableWidgetItem(str(xs[column_count])) new_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold)) self.tableWidget.setItem(header_count, column_count, new_item) column_count += 1 plot_count += 1 data_count += 1 def change_table_summary(self, plot_data_collect): plot_data_collection = plot_data_collect header_count = plot_count = data_count = config_count = column_saver = 0 data_names = [] plot_data_collection.sort(key=lambda plot_data: plot_data.path[-1]) plot_data_collection.sort( key=lambda plot_data: plot_data.identifiers[0]) legend = self.get_table_header(plot_data_collection) header = legend[0] config = legend[1] if ((config == []) | (len(config) == 1)): self.change_table_temporal(plot_data_collection) return self.tableWidget.setRowCount(len(plot_data_collection) / len(config)) for plot_data in plot_data_collection: values = ((float(x), float(y)) for (x, y) in plot_data.values) sorted_value_pairs = sorted(values, key=lambda pair: pair[0]) [xs, ys] = list(zip(*sorted_value_pairs)) # make header, important if more than one plot if plot_data.identifiers[0] not in data_names: self.tableWidget.insertRow(plot_count) v_item = QtWidgets.QTableWidgetItem( str(plot_data.identifiers[0])) v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold)) self.tableWidget.setVerticalHeaderItem(plot_count, v_item) header_count = plot_count data_names.append(plot_data.identifiers[0]) plot_count += 1 # round data if plot_data.label[1] == 'dB': ys = tuple(map(lambda i: round(i, 1), ys)) #horizontal header if more than one config if len(config) > 1: self.tableWidget.horizontalHeader().setVisible(True) else: self.tableWidget.horizontalHeader().setVisible(False) for column_count in range(0, len(xs)): columns = column_saver + column_count if (((column_saver + column_count) >= self.tableWidget.columnCount()) | (self.tableWidget.columnCount() == 0)): self.tableWidget.insertColumn(column_saver + column_count) if plot_count >= self.tableWidget.rowCount(): self.tableWidget.insertRow(plot_count) # units in first row of table new_item = QtWidgets.QTableWidgetItem(plot_data.label[0] + ' | ' + plot_data.label[1]) new_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold)) self.tableWidget.setItem(header_count, column_saver + column_count, new_item) #self.tableWidget.setItem(header_count, column_saver + column_count, QtWidgets.QTableWidgetItem(plot_data.label[0] + ' | ' + plot_data.label[1])) # x and y-value in one cell self.tableWidget.setItem( plot_count, columns, QtWidgets.QTableWidgetItem( str(xs[column_count]) + ' | ' + str(ys[column_count]))) # header self.tableWidget.setHorizontalHeaderItem( column_saver + column_count, QtWidgets.QTableWidgetItem(str(config[config_count]))) column_count += 1 if config[config_count] == header[data_count]: header[data_count] = header[data_count].replace( config[config_count], plot_data.path[-1]) elif config[config_count] in header[data_count]: header[data_count] = header[data_count].replace( config[config_count], '') self.tableWidget.setVerticalHeaderItem( plot_count, QtWidgets.QTableWidgetItem(str(header[data_count]))) column_saver = column_saver + column_count config_count += 1 if config_count == len(config): plot_count += 1 column_saver = config_count = 0 data_count += 1 def update_bd_table(self, index): # update bd table, the index determines the anchor, # if it is non integer per default the first config is regarded as # anchor self.bdTableModel.update_table(self.combo_rate_psnr.currentText(), self.combo_interp.currentText(), index, not (self.checkBox_bdplot.isChecked())) def update_bd_user_generated_curves_table(self, index): clicked_text = self.bdUserGeneratedTableModel.headerData( index, Qt.Vertical, Qt.DisplayRole) self.bdUserGeneratedTableModel.update( None, self.combo_rate_psnr.currentText(), self.combo_interp.currentText(), not (self.checkBox_bdplot.isChecked()), clicked_text) def update_bd_plot(self): data_collection = self.get_plot_data_collection_from_selected_variables( ) data_collection_user_generated = [] for index in self.curveListSelectionModel.selectedIndexes(): data_collection_user_generated.append( self.curveListModel[index.data()]) if len(data_collection): self.bdTableModel.update(data_collection, self.combo_rate_psnr.currentText(), self.combo_interp.currentText(), not (self.checkBox_bdplot.isChecked())) elif len(data_collection_user_generated): self.bdTableModel.update(data_collection_user_generated, self.combo_rate_psnr.currentText(), self.combo_interp.currentText(), not (self.checkBox_bdplot.isChecked())) def export_table_to_csv(self): # remember that the decimal mark is '.' if self.tableWidget.rowCount() > 0: path, extension = QtWidgets.QFileDialog.getSaveFileName( self, 'Save Table View as', '.', 'CSV (*.csv)') if path != '': if '.csv' not in path: path += '.csv' with open(str(path), 'w', newline='') as stream: writer = csv.writer(stream) for row in range(self.tableWidget.rowCount()): rowdata = [] rowdata.append( str( self.tableWidget.verticalHeaderItem(row).data( 0))) #data(0) = data(Qt.displayRole) for column in range(self.tableWidget.columnCount()): item = self.tableWidget.item(row, column) if item is not None: rowdata.append(str(item.text())) else: rowdata.append('') writer.writerow(rowdata) def save_bd_table(self): if self.bdTableModel.rowCount(self) == 0: return filename, extension = QtWidgets.QFileDialog.getSaveFileName( self, 'Save Table as', '.', 'Latex (*.tex)') if filename != '': if '.tex' not in filename: filename += '.tex' self.bdTableModel.export_to_latex(filename) def on_combo_box(self): # just update the bd table but do not change the anchor self.update_bd_table(-1) def save_current_selection(self): """Saves the current selected sim data item collection""" if not self.get_selected_simulation_data_items(): msg = QtWidgets.QMessageBox(self) # use self as parent here msg.setIcon(QtWidgets.QMessageBox.Information) msg.setText( "You did not select any simulation data item to store\n" "Please make a selection and try again.") msg.setWindowTitle("Info") msg.show() return filename, extension = QtWidgets.QFileDialog.getSaveFileName( self, 'Save RD data as', '.', 'RDPlot (*.rd)') if filename != '': if '.rd' not in filename: filename += '.rd' f = open(filename, 'w') f.write( jsonpickle.encode(self.get_selected_simulation_data_items())) f.close() def process_cmd_line_args(self, args): """Processes cmd line arguments. Those are only pathes or files.""" for path in args[1:]: if not isdir(path) and not isfile(path): continue if path.endswith('.rd'): f = open(path, 'r') json_str = f.read() sim_data_items = jsonpickle.decode(json_str) self.simDataItemTreeModel.update(sim_data_items, False) f.close() continue self.simDataItemTreeView.msg.show() self.simDataItemTreeView.parserThread.add_path(path) self.simDataItemTreeView.parserThread.start() def open_about_page(self): """Opens and displays an Html About file""" try: html_path = path.abspath(here + '/docs/about.html') html_file = open(html_path, 'r', encoding='utf-8', errors='ignore') source_code = html_file.read() try: f = open(here + '/version.txt', 'r') app_version = f.readline() except: app_version = 'could not detect version' source_code = source_code.replace("##VERSION##", app_version) source_code = source_code.replace("##here##", here) about_dialog = QtWidgets.QDialog(self) about_dialog.setWindowTitle("About RDPlot") about_dialog.setMaximumSize(950, 800) about_text = QtWidgets.QTextBrowser(about_dialog) about_text.setMinimumWidth(950) about_text.setMinimumHeight(800) about_text.setHtml(source_code) about_text.setOpenExternalLinks(True) about_text.show() about_dialog.exec_() about_dialog.close() about_text.close() except IOError: html_error = QtWidgets.QMessageBox() html_error.setIcon(QtWidgets.QMessageBox.Critical) html_error.setText("Error opening about or help") html_error.setInformativeText( "The html file from the resource could not be loaded.") html_error.exec_() def generate_new_curve(self): plot_data_collection = self.get_plot_data_collection_from_selected_variables( ) if plot_data_collection: new_plot_values = [] for _plot_data in plot_data_collection: new_plot_values.extend(_plot_data.values) if len(new_plot_values) < 4: QtWidgets.QMessageBox.warning( self, "Warning!", "You didn't select at least 4 points.") else: curve_name, ok = QtWidgets.QInputDialog.getText( self, "New curve", "Please enter a name for the new curve.\n" "If you enter an already existing name,\nits data will be overwritten." ) curve_name = curve_name.strip() if curve_name is not '': new_plot_data = PlotData([curve_name], new_plot_values, [], plot_data_collection[0].label) self.add_curve(curve_name, new_plot_data) else: QtWidgets.QMessageBox.warning( self, "Warning!", "Please enter a valid name.") else: QtWidgets.QMessageBox.warning( self, "Warning!", "You didn't select at least 4 points.") def add_curve(self, name, data): if self.curveWidget.isHidden(): self.curveWidget.show() data_tuple = (name, data) self.curveListModel.update_from_tuples((data_tuple, )) # all_indexes = QItemSelection(self.curveListModel.index(0), # self.curveListModel.index(self.curveListModel.rowCount(QModelIndex()))) # self.curveListSelectionModel.select(all_indexes, QItemSelectionModel.Clear) # self.curveListSelectionModel.select(self.curveListModel.index(self.curveListModel.rowCount(QModelIndex())-1), # QItemSelectionModel.Select) # self.curveListView.setFocus() def remove_curves(self): # todo integrate bjontegaard for generated curves(should be fully functional already) curves_to_remove = [] for index in self.curveListSelectionModel.selectedIndexes(): curves_to_remove.append(index.data()) self.curveListModel.remove_keys(curves_to_remove) if len(self.curveListModel) > 0: self.curveListSelectionModel.select(self.curveListModel.index(0), QItemSelectionModel.Select) else: self.curveWidget.hide() self.update_plot() def get_recent_files(self): recent_files = self.settings.value('recentFiles') if recent_files is not None: for recent_file in recent_files: if path.exists(recent_file): action = self.menuRecent_files.addAction(recent_file) action.triggered.connect(self.open_recent_file) def open_recent_file(self): path_recent = self.sender().text() if path.isdir(path_recent): self.simDataItemTreeView.add_folder(path_recent) else: self.simDataItemTreeView.add_file(path_recent) def add_recent_files(self, files, reload): # files doesn't necessarily have to just be a list of files # it can also be a directory if not reload: recent_files = self.settings.value('recentFiles') if recent_files is None: recent_files = [] for file in files: if file in recent_files: # put our file on top of the list recent_files.remove(file) recent_files.insert(0, file) while len(recent_files) > 5: del recent_files[-1] self.settings.setValue('recentFiles', recent_files) self.menuRecent_files.clear() for recent_file in recent_files: if path.exists(recent_file): action = self.menuRecent_files.addAction(recent_file) action.triggered.connect(self.open_recent_file) def add_files_to_watcher(self, items): for item in items: if isfile(item.path): self.watcher.addPath(item.path) def warning_file_change(self, path_item): # inform user about the fact that one of the loaded files has been changed since the application has started # timer is used to avoid spamming the user when multiple files are deleted in a row # retrieve affected notes and parent nodes # change their style in the tree view to indicate which files are affected if self.show_file_changed_message: self.show_file_changed_message = False self.reset_timer.start() QtWidgets.QMessageBox.warning( self, 'File change', 'One or more of your loaded files have been changed.\n' 'You can choose to reload them.\n' 'Hint: Changed files are greyed out in the sequences widget.') else: self.reset_timer.stop() self.reset_timer.start() affected_notes = [] for leaf in self.simDataItemTreeModel.root.leafs: for value in leaf.values: if value.path == path_item: affected_notes.append(leaf) for node in affected_notes: node_index = self.simDataItemTreeModel._get_index_from_item(node) node.setProperty('needs_reload', 'True') parent = self.simDataItemTreeModel.parent(node_index) level = 0 while parent.isValid() and level < 2: #MAX_LEVEL parent.internalPointer().setProperty('needs_reload', 'True') parent = self.simDataItemTreeModel.parent(parent) level += 1 def _reset_file_changed_message(self): self.show_file_changed_message = True def reload_files(self): # remove all selected files first # reload available files # could possibly limit this to only files that we know have been changed def check_children(parent): if len(parent.children) > 0: for child in parent.children: if not check_children(child): return False parent.setProperty('needs_reload', 'False') return True else: if parent.property('needs_reload') == 'True': return False return True values = self.selectedSimulationDataItemListModel.values() if len(values) == 0: for index in self.simDataItemTreeModel.root.leafs: for sim_data_item in index.values: values.append(sim_data_item) items_to_be_reloaded = [] for value in values: if path.exists(value.path): # reload file items_to_be_reloaded.append(value) self._variable_tree_selection_model.selectionChanged.disconnect() self._selection_model.selectionChanged.disconnect(self.change_list) self.simDataItemTreeModel.remove(values) self.simDataItemTreeView.msg.show() for item in items_to_be_reloaded: self.simDataItemTreeView.add_file(item.path, reload=True) self.change_list(QItemSelection(), QItemSelection()) self._selection_model.selectionChanged.connect(self.change_list) self._variable_tree_selection_model.selectionChanged.connect( self.update_plot) for node in self.simDataItemTreeModel.root.children: # remove grey font color if all changed files have been reloaded # have to check every single item because possible deletion of older nodes makes things very difficult check_children(node) def show_sequences_context_menu(self, position): self.menuEdit.exec(self.simDataItemTreeView.mapToGlobal(position))
class AccountsManager(ui.manageAccounts.Ui_Dialog, QDialog): """ GUI that handles creation, editing and deletion of accounts. """ def __init__(self, orm): super().__init__() self.setupUi(self) self.orm = orm self.accounts = ListModel() self.accountsView.setModel(self.accounts) self.selection = QItemSelectionModel(self.accounts) self.accountsView.setSelectionModel(self.selection) self.selection.currentRowChanged.connect(self.selection_changed) self.typeBox.addItems(ACCOUNT_TYPES) self.typeBox.currentTextChanged.connect(self.type_changed) self.closedBox.stateChanged.connect(self.closed_changed) self.exBudgetBox.stateChanged.connect(self.budget_changed) self.addAccountButton.clicked.connect(self.add_account) self.deleteAccountButton.clicked.connect(self.delete_account) for acc in self.orm.fetch_accounts(): self.accounts.addItem(acc) def selection_changed(self, curr_index: QModelIndex, prev_index: QModelIndex): """ Updates the information about currently selected account. """ if not curr_index.isValid(): return None # Make sure selection is visible in the view self.selection.setCurrentIndex( curr_index, QItemSelectionModel.SelectCurrent) acc = curr_index.data(role=Qt.UserRole) # Set the type of account self.typeBox.setCurrentText(acc.type) # Set the checkboxes self.closedBox.setChecked(acc.closed) self.exBudgetBox.setChecked(acc.exbudget) def type_changed(self, text: str): """ Changes the type of the account in DB and GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) # Catch only changes that differ for selected account if acc.type != text: self.orm.update_account_type(acc, text) acc.type = text def closed_changed(self, state: int): """ Changes close status of the account in DB and GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) # Catch only changes that differ for selected account # acc states in (0,1); Qt.CheckState in (0,1,2) # (0,1,2)//2 -> (0,0,1) state //= 2 if acc.closed != state: self.orm.update_account_status(acc, state) acc.closed = state def budget_changed(self, state: int): """ Changes the budget status of account in DB and GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) # Catch only changes that differ for selected account # acc states in (0,1); Qt.CheckState in (0,1,2) # (0,1,2)//2 -> (0,0,1) state //= 2 if acc.exbudget != state: self.orm.update_account_budget_status(acc, state) acc.exbudget = state def add_account(self): """ Creates account with default attributes in DB and adds it to GUI. """ name, ok = QInputDialog.getText(self, "Add new account", "Enter the name of the new account:") if ok and name: acc = self.orm.add_account(name) self.accounts.addItem(acc) def delete_account(self): """ Tries to delete the account from DB, if successful deletes it from GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: # the list of accounts is empty return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) msgBox = QMessageBox() msgBox.setText("Delete the {} account?".format(acc.name)) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ret = msgBox.exec() if ret == QMessageBox.Ok: deletion = self.orm.delete_account(acc) if not deletion: show_warning("Can't delete account.") else: self.accounts.removeRow(model_index.row())