class ListPartyView(QWidget): def __init__(self, controller): QWidget.__init__(self) self.controller = controller self.view_table = QTableWidget(0, 3) self.init() self.update_list() def init(self): # LAYOUT layout = QHBoxLayout() layout.addWidget(self.view_table) self.setLayout(layout) self.setMinimumWidth(500) # VIEW EVENT self.view_table.mouseDoubleClickEvent = self.callback_double_click # VIEW SELECTION MODE self.view_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.view_table.setSelectionMode(QAbstractItemView.SingleSelection) self.view_table.setEditTriggers(QAbstractItemView.NoEditTriggers) # self.view_table.setSortingEnabled(True) # VIEW COSMETIC # > Column format for _i, _label in enumerate(['Name', 'Number', 'Creator']): self.view_table.setHorizontalHeaderItem(_i, QTableWidgetItem(_label)) self.view_table.horizontalHeader().setStretchLastSection(True) self.header_min_size = 100 # > Row format self.view_table.verticalHeader().setVisible(False) def update_list(self): parties_model = self.controller.get_parties_from_server() self.view_table.setRowCount(len(parties_model)) for _i, _party_model in enumerate(parties_model): for _j, _label in enumerate(_party_model): self.view_table.setItem(_i, _j, QTableWidgetItem(str(_label))) def resizeEvent(self, event): for _i in range(self.view_table.columnCount()): if not _i: self.view_table.setColumnWidth(_i, self.width() - self.header_min_size * (self.view_table.columnCount() - 1)) else: self.view_table.setColumnWidth(_i, self.header_min_size) def get_selected(self): _row_selection = self.view_table.selectedItems() _party_name = None if len(_row_selection): _party_name = _row_selection[0].text() return _party_name def callback_double_click(self, event): if self.view_table.selectedItems(): self.controller.callback_join_party()
class InvoiceForm(QWidget): submitted = Signal(dict) def __init__(self, parent=None): # + parent=None super().__init__(parent) # + parent self.setLayout(QFormLayout()) self.inputs = dict() self.inputs['Customer Name'] = QLineEdit() self.inputs['Customer Address'] = QPlainTextEdit() self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True) self.inputs['Days until Due'] = QSpinBox() for label, widget in self.inputs.items(): self.layout().addRow(label, widget) self.line_items = QTableWidget(rowCount=10, columnCount=3) self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours']) self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.layout().addRow(self.line_items) for row in range(self.line_items.rowCount()): for col in range(self.line_items.columnCount()): if col > 0: w = QSpinBox() self.line_items.setCellWidget(row, col, w) submit = QPushButton('Create Invoice', clicked=self.on_submit) # + vvvvvv vvvvvvvvvvvvv _print = QPushButton('Print Invoice', clicked=self.window().printpreviewDialog) # + _print, + self.window() self.layout().addRow(submit, _print) # + _print def on_submit(self): data = {'c_name': self.inputs['Customer Name'].text(), 'c_addr': self.inputs['Customer Address'].toPlainText(), 'i_date': self.inputs['Invoice Date'].date().toString(), 'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(), 'i_terms': '{} days'.format(self.inputs['Days until Due'].value()), 'line_items': list()} for row in range(self.line_items.rowCount()): if not self.line_items.item(row, 0): continue job = self.line_items.item(row, 0).text() rate = self.line_items.cellWidget(row, 1).value() hours = self.line_items.cellWidget(row, 2).value() total = rate * hours row_data = [job, rate, hours, total] if any(row_data): data['line_items'].append(row_data) data['total_due'] = sum(x[3] for x in data['line_items']) self.submitted.emit(data) # remove everything else in this function below this point # + return data # +++
class MainWindow(QMainWindow): # Main window def __init__(self): super().__init__() self.setWindowTitle = 'DD烤肉机' self.resize(1870, 820) self.mainWidget = QWidget() self.mainLayout = QGridLayout() # Grid layout self.mainLayout.setSpacing(10) self.mainWidget.setLayout(self.mainLayout) self.duration = 60000 self.bitrate = 2000 self.fps = 60 self.initProcess = InitProcess() self.previewSubtitle = PreviewSubtitle() self.dnldWindow = YoutubeDnld() self.exportWindow = exportSubtitle() self.videoDecoder = VideoDecoder() self.exportWindow.exportArgs.connect(self.exportSubtitle) self.stack = QStackedWidget() self.stack.setFixedWidth(1300) self.mainLayout.addWidget(self.stack, 0, 0, 10, 8) buttonWidget = QWidget() buttonLayout = QHBoxLayout() buttonWidget.setLayout(buttonLayout) self.playButton = QPushButton('从本地打开') self.playButton.clicked.connect(self.open) self.playButton.setFixedWidth(400) self.playButton.setFixedHeight(75) self.dnldButton = QPushButton('Youtube下载器') self.dnldButton.clicked.connect(self.popDnld) self.dnldButton.setFixedWidth(400) self.dnldButton.setFixedHeight(75) buttonLayout.addWidget(self.playButton) buttonLayout.addWidget(self.dnldButton) self.stack.addWidget(buttonWidget) self.videoPath = '' self.videoWidth = 1920 self.videoHeight = 1080 self.globalInterval = 200 self.setPlayer() self.setSubtitle() self.setToolBar() self.setCentralWidget(self.mainWidget) self.playStatus = False self.volumeStatus = True self.volumeValue = 100 self.subSelectedTxt = '' self.subReplayTime = 1 self.clipBoard = [] self.grabKeyboard() self.show() def setPlayer(self): self.playerWidget = QGraphicsVideoItem() self.scene = QGraphicsScene() self.view = QGraphicsView(self.scene) self.view.resize(1280, 730) self.scene.addItem(self.playerWidget) self.stack.addWidget(self.view) self.player = QMediaPlayer(self, QMediaPlayer.VideoSurface) self.player.setVideoOutput(self.playerWidget) self.view.installEventFilter(self) self.view.show() self.srtTextItemDict = {0: QGraphicsTextItem(), 1: QGraphicsTextItem(), 2: QGraphicsTextItem(), 3: QGraphicsTextItem(), 4: QGraphicsTextItem()} for _, srtTextItem in self.srtTextItemDict.items(): self.scene.addItem(srtTextItem) def setSubtitle(self): self.subtitleDict = {x: {-1: [100, '']} for x in range(5)} self.subTimer = QTimer() self.subTimer.setInterval(100) self.subtitle = QTableWidget() self.subtitle.setAutoScroll(False) self.subtitle.setEditTriggers(QAbstractItemView.NoEditTriggers) self.mainLayout.addWidget(self.subtitle, 0, 8, 10, 12) self.subtitle.setColumnCount(5) self.subtitle.selectRow(0) self.subtitle.setHorizontalHeaderLabels(['%s' % (i + 1) for i in range(5)]) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) for index in range(5): self.subtitle.setColumnWidth(index, 130) self.subtitle.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.subtitle.setEditTriggers(QAbstractItemView.DoubleClicked) self.subtitle.horizontalHeader().sectionClicked.connect(self.addSubtitle) self.subtitle.doubleClicked.connect(self.releaseKeyboard) self.subtitle.cellChanged.connect(self.subEdit) self.subtitle.verticalHeader().sectionClicked.connect(self.subHeaderClick) self.subtitle.setContextMenuPolicy(Qt.CustomContextMenu) self.subtitle.customContextMenuRequested.connect(self.popTableMenu) self.initSubtitle() def initSubtitle(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) for x in range(self.subtitle.columnCount()): for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, x, 1, 1) self.subtitle.setRowCount(self.duration // self.globalInterval + 1) for x in range(self.subtitle.columnCount()): for y in range(self.subtitle.rowCount()): self.subtitle.setItem(y, x, QTableWidgetItem('')) self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def addSubtitle(self, index): subtitlePath = QFileDialog.getOpenFileName(self, "请选择字幕", None, "字幕文件 (*.srt *.vtt *.ass *.ssa)")[0] if subtitlePath: self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) if subtitlePath.endswith('.ass') or subtitlePath.endswith('.ssa'): p = subprocess.Popen(['utils/ffmpeg.exe', '-y', '-i', subtitlePath, 'temp_sub.srt']) p.wait() subtitlePath = 'temp_sub.srt' subData = {} with open(subtitlePath, 'r', encoding='utf-8') as f: f = f.readlines() subText = '' YoutubeAutoSub = False for l in f: if '<c>' in l: YoutubeAutoSub = True break for cnt, l in enumerate(f): if '<c>' in l: lineData = l.split('c>') if len(lineData) > 3: subText, start, _ = lineData[0].split('<') start = calSubTime(start[:-1]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(lineData[-3][1:-2]) // self.globalInterval * self.globalInterval for i in range(len(lineData) // 2): subText += lineData[i * 2 + 1][:-2] subData[start] = [end - start, subText] else: subText, start, _ = lineData[0].split('<') start = calSubTime(start[:-1]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: subText += lineData[1][:-2] subData[start] = [self.globalInterval, subText] elif '-->' in l and f[cnt + 2].strip() and '<c>' not in f[cnt + 2]: subText = f[cnt + 2][:-1] start = calSubTime(l[:12]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(l[17:29]) // self.globalInterval * self.globalInterval subData[start] = [end - start, subText] if '-->' in l and f[cnt + 1].strip() and not YoutubeAutoSub: start = calSubTime(l[:12]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(l[17:29]) // self.globalInterval * self.globalInterval delta = end - start if delta > 10: if '<b>' in f[cnt + 1]: subData[start] = [delta, f[cnt + 1].split('<b>')[1].split('<')[0]] else: subData[start] = [delta, f[cnt + 1][:-1]] self.subtitleDict[index].update(subData) maxRow = 0 for _, v in self.subtitleDict.items(): startMax = max(v.keys()) rowCount = (startMax + v[startMax][0]) // self.globalInterval if rowCount > maxRow: maxRow = rowCount if maxRow < self.duration // self.globalInterval + 1: maxRow = self.duration // self.globalInterval else: self.duration = maxRow * self.globalInterval self.subtitle.setRowCount(maxRow) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) for start, rowData in subData.items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.refreshComboBox() self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def subTimeOut(self): fontColor = self.previewSubtitle.fontColor fontSize = (self.previewSubtitle.fontSize + 5) / 2.5 fontBold = self.previewSubtitle.bold fontItalic = self.previewSubtitle.italic fontShadowOffset = self.previewSubtitle.shadowOffset for _, srtTextItem in self.srtTextItemDict.items(): srtTextItem.setDefaultTextColor(fontColor) font = QFont() font.setFamily("微软雅黑") font.setPointSize(fontSize) font.setBold(fontBold) font.setItalic(fontItalic) srtTextItem.setFont(font) srtTextShadow = QGraphicsDropShadowEffect() srtTextShadow.setOffset(fontShadowOffset) srtTextItem.setGraphicsEffect(srtTextShadow) try: selected = self.subtitle.selectionModel().selection().indexes() for x, i in enumerate(selected): if self.subtitle.item(i.row(), x): txt = self.subtitle.item(i.row(), x).text() if txt: self.srtTextItemDict[x].setPlainText('#%s:' % (x + 1) + txt) txtSize = self.srtTextItemDict[x].boundingRect().size() posY = self.playerWidget.size().height() - txtSize.height() * (x + 1) posX = (self.playerWidget.size().width() - txtSize.width()) / 2 self.srtTextItemDict[x].setPos(posX, posY) else: self.srtTextItemDict[x].setPlainText('') else: self.srtTextItemDict[x].setPlainText('') except: pass def subHeaderClick(self, index): if self.player.duration(): position = index * self.globalInterval self.player.setPosition(position) self.videoSlider.setValue(position * 1000 // self.player.duration()) self.setTimeLabel() def subEdit(self, row, index): repeat = self.subtitle.rowSpan(row, index) self.setSubtitleDict(row, index, repeat, self.subtitle.item(row, index).text()) self.subtitle.cellChanged.disconnect(self.subEdit) for cnt in range(repeat): if self.subtitle.item(row + cnt, index).text(): self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) else: self.subtitle.item(row, index).setBackground(QBrush(QColor('#232629'))) self.subtitle.cellChanged.connect(self.subEdit) def setSubtitleDict(self, row, index, num, text): self.subtitleDict[index][row * self.globalInterval] = [num * self.globalInterval, text] def popTableMenu(self, pos): self.subtitle.cellChanged.disconnect(self.subEdit) pos = QPoint(pos.x() + 55, pos.y() + 30) menu = QMenu() copy = menu.addAction('复制') paste = menu.addAction('粘贴') setSpan = menu.addAction('合并') clrSpan = menu.addAction('拆分') addSub = menu.addAction('导入字幕') cutSub = menu.addAction('裁剪字幕') action = menu.exec_(self.subtitle.mapToGlobal(pos)) selected = self.subtitle.selectionModel().selection().indexes() yList = [selected[0].row(), selected[-1].row()] xSet = set() for i in range(len(selected)): xSet.add(selected[i].column()) if action == copy: for x in xSet: self.clipBoard = [] for y in range(yList[0], yList[1] + 1): if self.subtitle.item(y, x): self.clipBoard.append(self.subtitle.item(y, x).text()) else: self.clipBoard.append('') break elif action == paste: self.subtitle.cellChanged.connect(self.subEdit) for x in xSet: for cnt, text in enumerate(self.clipBoard): self.subtitle.setItem(yList[0] + cnt, x, QTableWidgetItem(text)) self.subtitleDict[x][(yList[0] + cnt) * self.globalInterval] = [self.globalInterval, text] self.subtitle.cellChanged.disconnect(self.subEdit) elif action == setSpan: for x in xSet: if not self.subtitle.item(yList[0], x): firstItem = '' else: firstItem = self.subtitle.item(yList[0], x).text() for y in range(yList[0], yList[1] + 1): self.subtitle.setSpan(y, x, 1, 1) self.subtitle.setItem(y, x, QTableWidgetItem(firstItem)) self.subtitle.item(y, x).setBackground(QBrush(QColor('#35545d'))) if y * self.globalInterval in self.subtitleDict[x]: del self.subtitleDict[x][y * self.globalInterval] for x in xSet: self.subtitle.setSpan(yList[0], x, yList[1] - yList[0] + 1, 1) self.setSubtitleDict(yList[0], x, yList[1] - yList[0] + 1, firstItem) elif action == clrSpan: for x in xSet: if not self.subtitle.item(yList[0], x): firstItem = '' else: firstItem = self.subtitle.item(yList[0], x).text() for cnt, y in enumerate(range(yList[0], yList[1] + 1)): self.subtitle.setSpan(y, x, 1, 1) if not cnt: self.subtitle.setItem(yList[0], x, QTableWidgetItem(firstItem)) if firstItem: self.subtitle.item(y, x).setBackground(QBrush(QColor('#35545d'))) else: self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) else: self.subtitle.setItem(y, x, QTableWidgetItem('')) self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) self.setSubtitleDict(yList[0], x, yList[1] - yList[0] + 1, firstItem) break elif action == addSub: self.subtitle.cellChanged.connect(self.subEdit) for x in xSet: self.addSubtitle(x) self.subtitle.cellChanged.disconnect(self.subEdit) elif action == cutSub: for x in xSet: start = yList[0] * self.globalInterval end = yList[1] * self.globalInterval self.exportSubWindow(start, end, x + 1) self.subtitle.cellChanged.connect(self.subEdit) def setToolBar(self): ''' menu bar, file menu, play menu, tool bar. ''' toolBar = QToolBar() self.setContextMenuPolicy(Qt.NoContextMenu) self.addToolBar(toolBar) fileMenu = self.menuBar().addMenu('&文件') openAction = QAction(QIcon.fromTheme('document-open'), '&打开...', self, shortcut=QKeySequence.Open, triggered=self.open) fileMenu.addAction(openAction) downloadAction = QAction(QIcon.fromTheme('document-open'), '&Youtube下载器', self, triggered=self.popDnld) fileMenu.addAction(downloadAction) exitAction = QAction(QIcon.fromTheme('application-exit'), '&退出', self, shortcut='Ctrl+Q', triggered=self.close) fileMenu.addAction(exitAction) playMenu = self.menuBar().addMenu('&功能') self.playIcon = self.style().standardIcon(QStyle.SP_MediaPlay) self.pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause) self.playAction = toolBar.addAction(self.playIcon, '播放') self.playAction.triggered.connect(self.mediaPlay) self.volumeIcon = self.style().standardIcon(QStyle.SP_MediaVolume) self.volumeMuteIcon = self.style().standardIcon(QStyle.SP_MediaVolumeMuted) self.volumeAction = toolBar.addAction(self.volumeIcon, '静音') self.volumeAction.triggered.connect(self.volumeMute) previewAction = QAction(QIcon.fromTheme('document-open'), '&设置预览字幕', self, triggered=self.popPreview) playMenu.addAction(previewAction) decodeMenu = self.menuBar().addMenu('&输出') decodeAction = QAction(QIcon.fromTheme('document-open'), '&输出字幕及视频', self, triggered=self.decode) decodeMenu.addAction(decodeAction) self.volSlider = Slider() self.volSlider.setOrientation(Qt.Horizontal) self.volSlider.setMinimum(0) self.volSlider.setMaximum(100) self.volSlider.setFixedWidth(120) self.volSlider.setValue(self.player.volume()) self.volSlider.setToolTip(str(self.volSlider.value())) self.volSlider.pointClicked.connect(self.setVolume) toolBar.addWidget(self.volSlider) self.videoPositionEdit = LineEdit('00:00') self.videoPositionEdit.setAlignment(Qt.AlignRight) self.videoPositionEdit.setFixedWidth(75) self.videoPositionEdit.setFont(QFont('Timers', 14)) self.videoPositionEdit.clicked.connect(self.mediaPauseOnly) self.videoPositionEdit.editingFinished.connect(self.mediaPlayOnly) self.videoPositionLabel = QLabel(' / 00:00 ') self.videoPositionLabel.setFont(QFont('Timers', 14)) toolBar.addWidget(QLabel(' ')) toolBar.addWidget(self.videoPositionEdit) toolBar.addWidget(self.videoPositionLabel) self.timer = QTimer() self.timer.setInterval(100) self.videoSlider = Slider() self.videoSlider.setEnabled(False) self.videoSlider.setOrientation(Qt.Horizontal) self.videoSlider.setMinimum(0) self.videoSlider.setMaximum(1000) self.videoSlider.setFixedWidth(1000) self.videoSlider.sliderMoved.connect(self.timeStop) self.videoSlider.sliderReleased.connect(self.timeStart) self.videoSlider.pointClicked.connect(self.videoSliderClick) toolBar.addWidget(self.videoSlider) toolBar.addWidget(QLabel(' ')) self.globalIntervalComBox = QComboBox() self.globalIntervalComBox.addItems(['间隔 100ms', '间隔 200ms', '间隔 500ms', '间隔 1s']) self.globalIntervalComBox.setCurrentIndex(1) self.globalIntervalComBox.currentIndexChanged.connect(self.setGlobalInterval) toolBar.addWidget(self.globalIntervalComBox) toolBar.addWidget(QLabel(' ')) self.subEditComBox = QComboBox() self.refreshComboBox() toolBar.addWidget(self.subEditComBox) toolBar.addWidget(QLabel(' ')) moveForward = QPushButton('- 1') moveForward.setFixedWidth(50) toolBar.addWidget(moveForward) toolBar.addWidget(QLabel(' ')) moveAfterward = QPushButton('+ 1') moveAfterward.setFixedWidth(50) toolBar.addWidget(moveAfterward) toolBar.addWidget(QLabel(' ')) clearSub = QPushButton('清空') clearSub.setFixedWidth(50) toolBar.addWidget(clearSub) toolBar.addWidget(QLabel(' ')) outputSub = QPushButton('裁剪') outputSub.setFixedWidth(50) toolBar.addWidget(outputSub) moveForward.clicked.connect(self.moveForward) moveAfterward.clicked.connect(self.moveAfterward) clearSub.clicked.connect(self.clearSub) outputSub.clicked.connect(self.exportSubWindow) def setGlobalInterval(self, index): if not self.playStatus: self.mediaPlay() self.globalInterval = {0: 100, 1: 200, 2: 500, 3: 1000}[index] self.initSubtitle() self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) for index, subData in self.subtitleDict.items(): for start, rowData in subData.items(): startRow = start // self.globalInterval deltaRow = rowData[0] // self.globalInterval if deltaRow: endRow = startRow + deltaRow for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) if row >= 0: self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def moveForward(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) index = self.subEditComBox.currentIndex() for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, index, 1, 1) self.subtitle.setItem(y, index, QTableWidgetItem('')) self.subtitle.item(y, index).setBackground(QBrush(QColor('#232629'))) tmpDict = self.subtitleDict[index] self.subtitleDict[index] = {} for start, rowData in tmpDict.items(): self.subtitleDict[index][start - self.globalInterval] = rowData for start, rowData in self.subtitleDict[index].items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def moveAfterward(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) index = self.subEditComBox.currentIndex() for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, index, 1, 1) self.subtitle.setItem(y, index, QTableWidgetItem('')) self.subtitle.item(y, index).setBackground(QBrush(QColor('#232629'))) tmpDict = self.subtitleDict[index] self.subtitleDict[index] = {} for start, rowData in tmpDict.items(): self.subtitleDict[index][start + self.globalInterval] = rowData for start, rowData in self.subtitleDict[index].items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def clearSub(self): index = self.subEditComBox.currentIndex() reply = QMessageBox.information(self, '清空字幕', '清空第 %s 列字幕条?' % (index + 1), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) self.subtitleDict[index] = {0: [self.globalInterval, '']} for i in range(self.subtitle.rowCount()): self.subtitle.setSpan(i, index, 1, 1) self.subtitle.setItem(i, index, QTableWidgetItem('')) self.subtitle.item(i, index).setBackground(QBrush(QColor('#232629'))) self.subtitle.setHorizontalHeaderItem(index, QTableWidgetItem('%s' % (index + 1))) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def exportSubWindow(self, start=0, end=0, index=None): self.releaseKeyboard() self.exportWindow.hide() self.exportWindow.show() start = '00:00.0' if not start else self.splitTime(start) end = self.splitTime(self.duration) if not end else self.splitTime(end) if not index: index = self.subEditComBox.currentIndex() + 1 self.exportWindow.setDefault(start, end, index) def exportSubtitle(self, exportArgs): start = calSubTime2(exportArgs[0]) end = calSubTime2(exportArgs[1]) subStart = calSubTime2(exportArgs[2]) index = exportArgs[3] - 1 subData = self.subtitleDict[index] rowList = sorted(subData.keys()) exportRange = [] for t in rowList: if t >= start and t <= end: exportRange.append(t) subNumber = 1 with open(exportArgs[-1], 'w', encoding='utf-8') as exportFile: for t in exportRange: text = subData[t][1] if text: start = ms2Time(t + subStart) end = ms2Time(t + subStart + subData[t][0]) exportFile.write('%s\n%s --> %s\n%s\n\n' % (subNumber, start, end, text)) subNumber += 1 QMessageBox.information(self, '导出字幕', '导出完成', QMessageBox.Yes) self.exportWindow.hide() def refreshComboBox(self): self.subEditComBox.clear() for i in range(self.subtitle.columnCount()): self.subEditComBox.addItem('字幕 ' + str(i + 1)) def open(self): self.videoPath = QFileDialog.getOpenFileName(self, "请选择视频文件", None, "MP4格式 (*.mp4);;所有文件(*.*)")[0] if self.videoPath: cmd = ['utils/ffmpeg.exe', '-i', self.videoPath] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for l in p.stdout.readlines(): l = l.decode('utf8') if 'Duration' in l: self.duration = calSubTime(l.split(' ')[3][:-1]) if 'Stream' in l and 'DAR' in l: self.videoWidth, self.videoHeight = map(int, l.split(' [')[0].split(' ')[-1].split('x')) args = l.split(',') for cnt, arg in enumerate(args): if 'kb' in arg: self.bitrate = int(arg.split('kb')[0]) self.fps = int(args[cnt + 1].split('fps')[0]) break break self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) self.subtitle.setRowCount(self.duration // self.globalInterval + 1) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() url = QUrl.fromLocalFile(self.videoPath) self.stack.setCurrentIndex(1) self.playerWidget.setSize(QSizeF(1280, 720)) self.player.setMedia(url) self.playStatus = True self.videoSlider.setEnabled(True) self.mediaPlay() self.timer.start() self.timer.timeout.connect(self.timeOut) self.subTimer.start() self.subTimer.timeout.connect(self.subTimeOut) def popDnld(self): self.releaseKeyboard() self.dnldWindow.hide() self.dnldWindow.show() def popPreview(self): self.releaseKeyboard() self.previewSubtitle.hide() self.previewSubtitle.show() def decode(self): self.releaseKeyboard() self.videoDecoder.setDefault(self.videoPath, self.videoWidth, self.videoHeight, self.duration, self.bitrate, self.fps, self.subtitleDict) self.videoDecoder.hide() self.videoDecoder.show() def mediaPlay(self): if self.playStatus: self.player.play() self.grabKeyboard() self.timeStart() self.playStatus = False self.playAction.setIcon(self.pauseIcon) self.playAction.setText('暂停') else: self.player.pause() self.timeStop() self.playStatus = True self.playAction.setIcon(self.playIcon) self.playAction.setText('播放') def mediaPlayOnly(self): self.grabKeyboard() try: timeText = self.videoPositionEdit.text().split(':') m, s = timeText[:2] if not m: m = '00' if not s: s = '00' if len(m) > 3: m = m[:3] if len(s) > 2: s = s[:2] if m.isdigit(): m = int(m) if s.isdigit(): s = int(s) if s > 60: s = 60 total_m = self.player.duration() // 60000 if m > total_m: m = total_m self.player.setPosition(m * 60000 + s * 1000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) except: pass self.videoPositionEdit.setReadOnly(True) self.timeStart() def mediaPauseOnly(self): self.releaseKeyboard() self.videoPositionEdit.setReadOnly(False) self.player.pause() self.timeStop() self.playStatus = True self.playAction.setIcon(self.playIcon) self.playAction.setText('播放') def splitTime(self, playTime): playTime = playTime // 1000 m = str(playTime // 60) s = playTime % 60 s = ('0%s' % s)[-2:] if len(m) > 2: t = '%3s:%2s' % (m, s) else: t = '%2s:%2s' % (m, s) return t def timeOut(self): row = self.player.position() // self.globalInterval self.subtitle.selectRow(row) self.subtitle.verticalScrollBar().setValue(row - 10) if self.dnldWindow.isHidden() or self.exportWindow.isHidden() or self.videoDecoder.isHidden(): self.grabKeyboard() try: self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() except: pass def timeStop(self): self.timer.stop() def timeStart(self): self.timer.start() def videoSliderClick(self, p): self.videoSlider.setValue(p.x()) self.player.setPosition(p.x() * self.player.duration() // 1000) self.setTimeLabel() def setVolume(self, p): self.volumeValue = p.x() if self.volumeValue > 100: self.volumeValue = 100 if self.volumeValue < 0: self.volumeValue = 0 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) self.volSlider.setToolTip(str(self.volSlider.value())) if self.volumeValue: self.volumeStatus = True self.volumeAction.setIcon(self.volumeIcon) else: self.volumeStatus = False self.volumeAction.setIcon(self.volumeMuteIcon) def volumeMute(self): if self.volumeStatus: self.volumeStatus = False self.old_volumeValue = self.player.volume() self.player.setVolume(0) self.volSlider.setValue(0) self.volumeAction.setIcon(self.volumeMuteIcon) else: self.volumeStatus = True self.player.setVolume(self.old_volumeValue) self.volSlider.setValue(self.old_volumeValue) self.volumeAction.setIcon(self.volumeIcon) def setTimeLabel(self): now = self.player.position() total = self.player.duration() now = self.splitTime(now) total = self.splitTime(total) self.videoPositionEdit.setText(now) self.videoPositionLabel.setText(' / %s ' % total) def eventFilter(self, obj, event): if obj == self.view: if event.type() == QEvent.MouseButtonPress: self.mediaPlay() return QMainWindow.eventFilter(self, obj, event) def keyPressEvent(self, QKeyEvent): key = QKeyEvent.key() if key == Qt.Key_Left: if self.videoSlider.isEnabled(): self.player.setPosition(self.player.position() - 5000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() elif key == Qt.Key_Right: if self.videoSlider.isEnabled(): self.player.setPosition(self.player.position() + 5000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() elif key == Qt.Key_Up: self.volumeValue += 10 if self.volumeValue > 100: self.volumeValue = 100 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) elif key == Qt.Key_Down: self.volumeValue -= 10 if self.volumeValue < 0: self.volumeValue = 0 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) elif key == Qt.Key_Space: self.mediaPlay()
class EntityViewer(QWidget): def __init__(self, parent, entity): self.parent = parent QWidget.__init__(self) self.entity = entity self.initUI() def initUI(self): self.layout = QGridLayout(self) self.setLayout(self.layout) self.back = QPushButton("Back") self.back.clicked.connect(self.chooseotherentity) self.layout.addWidget(self.back, 0, 0) self.pullb = QPushButton("Reload from Shotgun") self.pullb.clicked.connect(self.pull) self.layout.addWidget(self.pullb, 2, 0) self.savePage = QPushButton("Save Page") self.savePage.clicked.connect(self.setPageSettings) self.layout.addWidget(self.savePage, 3, 0) self.grid = QTableWidget(self) self.layout.addWidget(self.grid, 1, 0) self.grid.cellDoubleClicked.connect(self.edit) self.grid.cellClicked.connect(self.unedit) self.activeCell = None self.initData() self.grid.horizontalHeader().setSectionsMovable(True) self.grid.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu) self.grid.horizontalHeader().customContextMenuRequested.connect( self.hideF) def initData(self): self.entdata = self.parent.parent.db.get(self.entity) self.fieldsToShow = self.sanitizeFields(self.entdata) i = 0 indexMap = {} for field in self.fieldsToShow: self.grid.insertColumn(self.grid.columnCount()) self.grid.setHorizontalHeaderItem(i, QTableWidgetItem(field)) indexMap[field] = i i += 1 for ent in self.entdata.data: self.grid.insertRow(self.grid.rowCount()) for field in ent: if field in indexMap: cell = cellmanager.barfCell(ent[field], self.parent) self.grid.setCellWidget(self.grid.rowCount() - 1, indexMap[field], cell) def edit(self, row, column): newCell = cellmanager.edit(self.grid.cellWidget(row, column)) if newCell: self.grid.setCellWidget(row, column, newCell) self.activeCell = (row, column) def unedit(self, row, column): #pylint:disable=unused-argument if self.activeCell: self.grid.setCellWidget( self.activeCell[0], self.activeCell[1], cellmanager.save( self.grid.cellWidget(self.activeCell[0], self.activeCell[1]))) self.activeCell = None def chooseotherentity(self): self.parent.parent.changeState(self.parent) def pull(self): self.parent.parent.db.reloadTable(self.entity) self.parent.parent.changeState(EntityViewer(self.parent, self.entity)) def hideF(self, position): menu = QMenu() hideField = menu.addAction("Hide") ac = menu.exec_(self.grid.mapToGlobal(position)) if ac == hideField: index = self.grid.horizontalHeader().logicalIndexAt(position) self.grid.removeColumn(index) def setPageSettings(self): rules = [] for i in range(0, self.grid.columnCount()): rules.append( self.grid.horizontalHeaderItem( self.grid.horizontalHeader().visualIndex(i)).text()) json_reader.write(rules, "data/pagesettings/%s" % self.entity) def sanitizeFields(self, allData): try: rules = json_reader.read("data/pagesettings/%s" % self.entity) except IOError: print("No page settings saved for %ss." % self.entity) return allData.data[0] return rules
class StreamFieldsWidget(QDialog): """ A stream widget containing schema-specific properties. """ def __init__(self, parent, show_only_f142_stream: bool = False): super().__init__() self.setParent(parent) self.setLayout(QGridLayout()) self.setWindowModality(Qt.WindowModal) self.setModal(True) self._show_only_f142_stream = show_only_f142_stream self.minimum_spinbox_value = 0 self.maximum_spinbox_value = 100_000_000 self.advanced_options_enabled = False self.hs00_unimplemented_label = QLabel( "hs00 (Event histograms) has not yet been fully implemented.") self.schema_label = QLabel("Schema: ") self.schema_combo = DropDownList() self.schema_validator = SchemaSelectionValidator() self.schema_combo.setValidator(self.schema_validator) self.schema_validator.is_valid.connect( partial(validate_general_widget, self.schema_combo)) self.topic_label = QLabel("Topic: ") self.topic_line_edit = QLineEdit() self.topic_validator = NoEmptyStringValidator() self.topic_line_edit.setValidator(self.topic_validator) self.topic_validator.is_valid.connect( partial( validate_line_edit, self.topic_line_edit, tooltip_on_reject="Topic name can not be empty.", )) validate_line_edit(self.topic_line_edit, False) self.source_label = QLabel("Source: ") self.source_line_edit = QLineEdit() self.source_validator = NoEmptyStringValidator() self.source_line_edit.setValidator(self.source_validator) self.source_validator.is_valid.connect( partial( validate_line_edit, self.source_line_edit, tooltip_on_reject="Source name can not be empty.", )) validate_line_edit(self.source_line_edit, False) self.array_size_label = QLabel("Array size: ") self.array_size_spinbox = QSpinBox() self.array_size_spinbox.setMaximum(np.iinfo(np.int32).max) self.array_size_table = QTableWidget(1, 3) self.array_size_table.setHorizontalHeaderLabels(["x", "y", "z"]) self.array_size_table.setVerticalHeaderLabels([""]) table_height = self.array_size_table.sizeHintForRow( 0) + self.array_size_table.sizeHintForRow(1) self.array_size_table.setMaximumHeight(table_height) self.array_size_table.setFrameStyle(QFrame.NoFrame) self.array_size_table.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.array_size_table.resizeColumnsToContents() self.array_size_table.resizeRowsToContents() self.array_size_table.setItemDelegate( ValueDelegate(int, self.array_size_table)) self.type_label = QLabel("Type: ") self.type_combo = QComboBox() self.type_combo.addItems(F142_TYPES) self.type_combo.setCurrentText("double") self.value_units_edit = QLineEdit() self.value_units_label = QLabel("Value Units:") self.show_advanced_options_button = QPushButton( text="Show/hide advanced options") self.show_advanced_options_button.setCheckable(True) self.show_advanced_options_button.clicked.connect( self.advanced_options_button_clicked) self._set_up_f142_group_box() self._set_up_ev42_group_box() self.scalar_radio = QRadioButton(text=SCALAR) self.scalar_radio.clicked.connect(partial(self._show_array_size, False)) self.scalar_radio.setChecked(True) self.scalar_radio.clicked.emit() self.array_radio = QRadioButton(text=ARRAY) self.array_radio.clicked.connect(partial(self._show_array_size, True)) self.schema_combo.currentTextChanged.connect(self._schema_type_changed) if self._show_only_f142_stream: self.schema_combo.addItems([StreamModules.F142.value]) else: self.schema_combo.addItems([e.value for e in StreamModules]) self.ok_button = QPushButton("OK") self.ok_button.clicked.connect(self.parent().close) self.layout().addWidget(self.schema_label, 0, 0) self.layout().addWidget(self.schema_combo, 0, 1) self.layout().addWidget(self.topic_label, 1, 0) self.layout().addWidget(self.topic_line_edit, 1, 1) self.layout().addWidget(self.source_label, 2, 0) self.layout().addWidget(self.source_line_edit, 2, 1) self.layout().addWidget(self.value_units_label, 3, 0) self.layout().addWidget(self.value_units_edit, 3, 1) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) self.layout().addWidget(self.type_label, 4, 0) self.layout().addWidget(self.type_combo, 4, 1) self.layout().addWidget(self.scalar_radio, 5, 0) self.layout().addWidget(self.array_radio, 5, 1) self.layout().addWidget(self.array_size_label, 6, 0) self.layout().addWidget(self.array_size_spinbox, 6, 1) self.layout().addWidget(self.array_size_table, 6, 1) self.layout().addWidget(self.hs00_unimplemented_label, 7, 0, 1, 2) # Spans both rows self.layout().addWidget(self.show_advanced_options_button, 8, 0, 1, 2) self.layout().addWidget(self.f142_advanced_group_box, 9, 0, 1, 2) self.layout().addWidget(self.ev42_advanced_group_box, 10, 0, 1, 2) self.layout().addWidget(self.ok_button, 11, 0, 1, 2) self._schema_type_changed(self.schema_combo.currentText()) self.parent().parent().field_name_edit.setVisible(False) def advanced_options_button_clicked(self): self._show_advanced_options( show=self.show_advanced_options_button.isChecked()) def _set_up_ev42_group_box(self): """ Sets up the UI for ev42 advanced options. """ self.ev42_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.ev42_advanced_group_box.setLayout(QFormLayout()) self.ev42_adc_pulse_debug_label = QLabel(ADC_PULSE_DEBUG) self.ev42_adc_pulse_debug_checkbox = QCheckBox() self.ev42_advanced_group_box.layout().addRow( self.ev42_adc_pulse_debug_label, self.ev42_adc_pulse_debug_checkbox) self.ev42_chunk_size_spinner = ( self.create_label_and_spinbox_for_advanced_option( CHUNK_SIZE, self.ev42_advanced_group_box)) self.ev42_cue_interval_spinner = ( self.create_label_and_spinbox_for_advanced_option( CUE_INTERVAL, self.ev42_advanced_group_box)) def create_label_and_spinbox_for_advanced_option(self, nexus_string: str, group_box: QGroupBox): """ Creates a SpinBox with a label and adds them to GroupBox then returns the SpinBox. :param nexus_string: The nexus string label for the SpinBox. :param group_box: The GroupBox that the label and SpinBox should be added to. :return: The newly created SpinBox. """ label = QLabel(nexus_string) spinner = QSpinBox() spinner.setRange(self.minimum_spinbox_value, self.maximum_spinbox_value) group_box.layout().addRow(label, spinner) return spinner def _set_up_f142_group_box(self): """ Sets up the UI for the f142 advanced options. """ self.f142_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.f142_advanced_group_box.setLayout(QFormLayout()) self.f142_chunk_size_spinner = ( self.create_label_and_spinbox_for_advanced_option( CHUNK_SIZE, self.f142_advanced_group_box)) self.f142_cue_interval_spinner = ( self.create_label_and_spinbox_for_advanced_option( CUE_INTERVAL, self.f142_advanced_group_box)) def _show_advanced_options(self, show): schema = self.schema_combo.currentText() if schema == WriterModules.F142.value: self.f142_advanced_group_box.setVisible(show) elif schema == WriterModules.EV42.value: self.ev42_advanced_group_box.setVisible(show) self.advanced_options_enabled = show def _show_array_size(self, show: bool): self.array_size_spinbox.setVisible(show) self.array_size_label.setVisible(show) def _schema_type_changed(self, schema: str): self.parent().setWindowTitle(f"Editing {schema} stream field") self.hs00_unimplemented_label.setVisible(False) self.f142_advanced_group_box.setVisible(False) self.ev42_advanced_group_box.setVisible(False) self.show_advanced_options_button.setVisible(False) self.show_advanced_options_button.setChecked(False) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) self.array_size_table.setVisible(False) if schema == WriterModules.F142.value: self.value_units_label.setVisible(True) self.value_units_edit.setVisible(True) self._set_edits_visible(True, True) self.show_advanced_options_button.setVisible(True) self.f142_advanced_group_box.setVisible(False) elif schema == WriterModules.EV42.value: self._set_edits_visible(True, False) self.show_advanced_options_button.setVisible(True) self.ev42_advanced_group_box.setVisible(False) elif schema == WriterModules.ADAR.value: self._set_edits_visible(True, False) self._show_array_size_table(True) elif schema == WriterModules.HS00.value: self._set_edits_visible(True, False) self.hs00_unimplemented_label.setVisible(True) elif schema == WriterModules.NS10.value: self._set_edits_visible(True, False, "nicos/<device>/<parameter>") elif (schema == WriterModules.TDCTIME.value or schema == WriterModules.SENV.value): self._set_edits_visible(True, False) def _show_array_size_table(self, show: bool): self.array_size_label.setVisible(show) self.array_size_table.setVisible(show) def _set_edits_visible(self, source: bool, type: bool, source_hint=None): self.source_label.setVisible(source) self.source_line_edit.setVisible(source) self.type_label.setVisible(type) self.type_combo.setVisible(type) self.array_radio.setVisible(type) self.scalar_radio.setVisible(type) if source_hint: self.source_line_edit.setPlaceholderText(source_hint) else: self.source_line_edit.setPlaceholderText("") def get_stream_module(self, parent) -> StreamModule: """ Create the stream module :return: The created stream module """ source = self.source_line_edit.text() topic = self.topic_line_edit.text() stream: StreamModule = None type = self.type_combo.currentText() current_schema = self.schema_combo.currentText() if current_schema == WriterModules.F142.value: value_units = self.value_units_edit.text() array_size = self.array_size_spinbox.value() stream = F142Stream( parent_node=parent, source=source, topic=topic, type=type, value_units=value_units, array_size=array_size, ) if array_size: stream.array_size = array_size if self.advanced_options_enabled: self.record_advanced_f142_values(stream) elif current_schema == WriterModules.ADAR.value: array_size = [] for i in range(self.array_size_table.columnCount()): table_value = self.array_size_table.item(0, i) if table_value: array_size.append(int(table_value.text())) stream = ADARStream(parent_node=parent, source=source, topic=topic) stream.array_size = array_size elif current_schema == WriterModules.EV42.value: stream = EV42Stream(parent_node=parent, source=source, topic=topic) if self.advanced_options_enabled: self.record_advanced_ev42_values(stream) elif current_schema == WriterModules.NS10.value: stream = NS10Stream(parent_node=parent, source=source, topic=topic) elif current_schema == WriterModules.SENV.value: stream = SENVStream(parent_node=parent, source=source, topic=topic) elif current_schema == WriterModules.HS00.value: stream = HS00Stream( # type: ignore parent=parent, source=source, topic=topic, data_type=NotImplemented, edge_type=NotImplemented, error_type=NotImplemented, shape=[], ) elif current_schema == WriterModules.TDCTIME.value: stream = TDCTStream(parent_node=parent, source=source, topic=topic) return stream def record_advanced_f142_values(self, stream: F142Stream): """ Save the advanced f142 properties to the stream data object. :param stream: The stream data object to be modified. """ stream.chunk_size = self.f142_chunk_size_spinner.value() stream.cue_interval = self.f142_cue_interval_spinner.value() def record_advanced_ev42_values(self, stream: EV42Stream): """ Save the advanced ev42 properties to the stream data object. :param stream: The stream data object to be modified. """ stream.adc_pulse_debug = self.ev42_adc_pulse_debug_checkbox.isChecked() stream.chunk_size = self.ev42_chunk_size_spinner.value() stream.cue_interval = self.ev42_cue_interval_spinner.value() def fill_in_existing_ev42_fields(self, field: EV42Stream): """ Fill in specific existing ev42 fields into the new UI field. :param field: The stream group """ if check_if_advanced_options_should_be_enabled( [field.adc_pulse_debug, field.chunk_size, field.cue_interval]): self._show_advanced_options(True) self._fill_existing_advanced_ev42_fields(field) def _fill_existing_advanced_ev42_fields(self, field: EV42Stream): """ Fill the fields in the interface with the existing ev42 stream data. :param field: The ev42 stream data object. """ self.ev42_adc_pulse_debug_checkbox.setChecked(field.adc_pulse_debug) self.ev42_chunk_size_spinner.setValue(field.chunk_size) self.ev42_cue_interval_spinner.setValue(field.cue_interval) def fill_in_existing_f142_fields(self, field: F142Stream): """ Fill in specific existing f142 fields into the new UI field. :param field: The stream group """ self.type_combo.setCurrentText(field.type) if field.array_size is not None: self.array_radio.setChecked(True) self.scalar_radio.setChecked(False) self.array_size_spinbox.setValue(field.array_size) else: self.array_radio.setChecked(False) self.scalar_radio.setChecked(True) if field.value_units is not None: self.value_units_edit.setText(field.value_units) if check_if_advanced_options_should_be_enabled( [field.chunk_size, field.cue_interval]): self._show_advanced_options(True) self._fill_existing_advanced_f142_fields(field) def _fill_existing_advanced_f142_fields(self, field: F142Stream): """ Fill the advanced fields in the interface with the existing f142 stream data. :param field: The f412 stream data object. """ self.f142_chunk_size_spinner.setValue(field.chunk_size) self.f142_cue_interval_spinner.setValue(field.cue_interval) def update_existing_stream_info(self, field): """ Fill in stream fields and properties into the new UI field. :param field: The stream group """ if isinstance(field, Group): field = field.children[0] if hasattr(field, "parent_node") and isinstance( field.parent_node, Group): self.schema_validator.set_group(field.parent_node) else: self.schema_validator.set_group(None) schema = field.writer_module self.schema_combo.setCurrentText(schema) self.schema_validator.validate(schema, 0) self.topic_line_edit.setText(field.topic) self.topic_validator.validate(field.topic, 0) self.source_line_edit.setText(field.source) self.source_validator.validate(field.source, 0) if schema == WriterModules.F142.value: self.fill_in_existing_f142_fields(field) elif schema == WriterModules.EV42.value: self.fill_in_existing_ev42_fields(field) elif schema == WriterModules.ADAR.value: for i, val in enumerate(field.array_size): self.array_size_table.setItem(0, i, QTableWidgetItem(str(val)))
class Ui_Whatspy(object): def setupUi(self, Ventana): if not Ventana.objectName(): Ventana.setObjectName(u"Ventana") Ventana.resize(424, 600) Ventana.setAttribute(Qt.WA_TranslucentBackground) Ventana.setWindowFlag(Qt.FramelessWindowHint) Ventana.setStyleSheet( u"*{\n" " font-family: century gothic;\n" "}\n" "\n" "QSizeGrip{\n" " background: transparent;\n" "}\n" "\n" "QLabel{\n" " color: white;\n" "}\n" "\n" "QScrollBar:vertical {\n" " border: 1px solid #999999;\n" " width:12px; \n" " margin: 0px 0px 0px 0px;\n" " }\n" " QScrollBar::handle:vertical { \n" " \n" " min-height: 0px;\n" " border: 0px solid red;\n" " border-radius: 4px;\n" " background-color: rgb(182, 182, 182);\n" " }\n" " QScrollBar::add-line:vertical { \n" " height: 0px;\n" " subcontrol-position: bottom;\n" " subcontrol-origin: margin;\n" " }\n" " QScrollBar::sub-line:vertical {\n" " height: 0 px;\n" " subcontrol-position: top;\n" " subcontrol-origin: margin;\n" " }") self.gridLayout = QGridLayout(Ventana) self.gridLayout.setObjectName(u"gridLayout") self.gridLayout.setContentsMargins(0, 0, 0, 0) self.VentanaFrame = QFrame(Ventana) self.VentanaFrame.setObjectName(u"VentanaFrame") self.VentanaFrame.setStyleSheet( u"QFrame#VentanaFrame{\n" " background-color: qlineargradient(spread:pad, x1:0.953, y1:1, x2:1, y2:0, stop:0 #075E54, stop:1 #128C7E);\n" " border-radius: 5px;\n" "}") self.VentanaFrame.setFrameShape(QFrame.StyledPanel) self.VentanaFrame.setFrameShadow(QFrame.Raised) self.gridLayout_2 = QGridLayout(self.VentanaFrame) self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2.setContentsMargins(-1, 0, -1, -1) self.Contenedor = QFrame(self.VentanaFrame) self.Contenedor.setObjectName(u"Contenedor") self.Contenedor.setStyleSheet(u"QFrame#Contenedor{\n" " background-color: #128C7E;\n" " border-radius: 5px;\n" "}") self.Contenedor.setFrameShape(QFrame.StyledPanel) self.Contenedor.setFrameShadow(QFrame.Raised) self.gridLayout_5 = QGridLayout(self.Contenedor) self.gridLayout_5.setObjectName(u"gridLayout_5") self.gridLayout_5.setContentsMargins(0, 10, 0, 10) self.frameAcciones = QFrame(self.Contenedor) self.frameAcciones.setObjectName(u"frameAcciones") sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.frameAcciones.sizePolicy().hasHeightForWidth()) self.frameAcciones.setSizePolicy(sizePolicy) self.frameAcciones.setStyleSheet(u"") self.frameAcciones.setFrameShape(QFrame.StyledPanel) self.frameAcciones.setFrameShadow(QFrame.Raised) self.gridLayout_4 = QGridLayout(self.frameAcciones) self.gridLayout_4.setObjectName(u"gridLayout_4") self.btnExportar = QPushButton(self.frameAcciones) self.btnExportar.setObjectName(u"btnExportar") sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum) sizePolicy1.setHorizontalStretch(0) sizePolicy1.setVerticalStretch(0) sizePolicy1.setHeightForWidth( self.btnExportar.sizePolicy().hasHeightForWidth()) self.btnExportar.setSizePolicy(sizePolicy1) self.btnExportar.setMinimumSize(QSize(100, 30)) self.btnExportar.setCursor(QCursor(Qt.PointingHandCursor)) self.btnExportar.setStyleSheet(u"QPushButton#btnExportar{\n" "background-color: #ecf0f1;\n" "border-radius: 5px;\n" "}\n" "\n" "QPushButton#btnExportar:hover{\n" "background-color: #bdc3c7;\n" "border-radius: 5px;\n" "}") icon1 = QIcon() icon1.addFile(u":/logo/exportar.png", QSize(), QIcon.Normal, QIcon.Off) self.btnExportar.setIcon(icon1) self.btnExportar.setIconSize(QSize(16, 16)) self.gridLayout_4.addWidget(self.btnExportar, 0, 2, 1, 1) self.btnAbrir = QPushButton(self.frameAcciones) self.btnAbrir.setObjectName(u"btnAbrir") sizePolicy1.setHeightForWidth( self.btnAbrir.sizePolicy().hasHeightForWidth()) self.btnAbrir.setSizePolicy(sizePolicy1) self.btnAbrir.setMinimumSize(QSize(100, 30)) self.btnAbrir.setCursor(QCursor(Qt.PointingHandCursor)) self.btnAbrir.setStyleSheet(u"QPushButton#btnAbrir{\n" "background-color: #ecf0f1;\n" "border-radius: 5px;\n" "}\n" "\n" "QPushButton#btnAbrir:hover{\n" "background-color: #bdc3c7;\n" "}") icon2 = QIcon() icon2.addFile(u":/logo/anadir.png", QSize(), QIcon.Normal, QIcon.Off) self.btnAbrir.setIcon(icon2) self.btnAbrir.setIconSize(QSize(16, 16)) self.gridLayout_4.addWidget(self.btnAbrir, 0, 1, 1, 1) self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayout_4.addItem(self.horizontalSpacer_2, 0, 0, 1, 1) self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayout_4.addItem(self.horizontalSpacer_3, 0, 3, 1, 1) self.gridLayout_5.addWidget(self.frameAcciones, 2, 0, 1, 1) self.frameTabla = QFrame(self.Contenedor) self.frameTabla.setObjectName(u"frameTabla") sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) sizePolicy2.setHorizontalStretch(0) sizePolicy2.setVerticalStretch(0) sizePolicy2.setHeightForWidth( self.frameTabla.sizePolicy().hasHeightForWidth()) self.frameTabla.setSizePolicy(sizePolicy2) self.frameTabla.setStyleSheet(u"") self.frameTabla.setFrameShape(QFrame.StyledPanel) self.frameTabla.setFrameShadow(QFrame.Raised) self.horizontalLayout_2 = QHBoxLayout(self.frameTabla) self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(self.horizontalSpacer_4) self.tableWidget = QTableWidget(self.frameTabla) if (self.tableWidget.columnCount() < 2): self.tableWidget.setColumnCount(2) __qtablewidgetitem = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem) __qtablewidgetitem1 = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1) if (self.tableWidget.rowCount() < 50): self.tableWidget.setRowCount(50) __qtablewidgetitem2 = QTableWidgetItem() self.tableWidget.setItem(0, 0, __qtablewidgetitem2) self.tableWidget.setObjectName(u"tableWidget") sizePolicy1.setHeightForWidth( self.tableWidget.sizePolicy().hasHeightForWidth()) self.tableWidget.setSizePolicy(sizePolicy1) self.tableWidget.setMinimumSize(QSize(350, 0)) self.tableWidget.setStyleSheet(u"QTableWidget#tableWidget:item{\n" "color:white;\n" "font-weight: bold;\n" "}\n" "\n" "QTableWidget#tableWidget{\n" "border-radius:4px;\n" "background-color: #128C7E;\n" "}") self.tableWidget.setRowCount(50) self.horizontalLayout_2.addWidget(self.tableWidget) self.horizontalSpacer_5 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(self.horizontalSpacer_5) self.gridLayout_5.addWidget(self.frameTabla, 3, 0, 1, 1) self.header = QFrame(self.Contenedor) self.header.setObjectName(u"header") sizePolicy.setHeightForWidth( self.header.sizePolicy().hasHeightForWidth()) self.header.setSizePolicy(sizePolicy) self.header.setStyleSheet(u"") self.header.setFrameShape(QFrame.StyledPanel) self.header.setFrameShadow(QFrame.Raised) self.horizontalLayout = QHBoxLayout(self.header) self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setContentsMargins(0, 0, 5, 0) self.horizontalSpacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(self.horizontalSpacer) self.btnVerde = QPushButton(self.header) self.btnVerde.setObjectName(u"btnVerde") self.btnVerde.setMaximumSize(QSize(16, 16)) self.btnVerde.setCursor(QCursor(Qt.PointingHandCursor)) self.btnVerde.setStyleSheet(u"QPushButton#btnVerde{\n" " background-color: #27ae60;\n" " border-radius: 8px;\n" "}\n" "\n" "QPushButton#btnVerde:hover{\n" " background-color: #10ac84;\n" " border-radius: 8px;\n" "}") self.horizontalLayout.addWidget(self.btnVerde) self.btnAmarillo = QPushButton(self.header) self.btnAmarillo.setObjectName(u"btnAmarillo") self.btnAmarillo.setMaximumSize(QSize(16, 16)) self.btnAmarillo.setCursor(QCursor(Qt.PointingHandCursor)) self.btnAmarillo.setStyleSheet(u"QPushButton#btnAmarillo{\n" "background-color: yellow;\n" "border-radius: 8px;\n" "}\n" "\n" "QPushButton#btnAmarillo:hover{\n" "background-color: #f1c40f;\n" "border-radius: 8px;\n" "}\n" "") self.horizontalLayout.addWidget(self.btnAmarillo) self.btnRojo = QPushButton(self.header) self.btnRojo.setObjectName(u"btnRojo") self.btnRojo.setMaximumSize(QSize(16, 16)) self.btnRojo.setCursor(QCursor(Qt.PointingHandCursor)) self.btnRojo.setStyleSheet(u"QPushButton#btnRojo{\n" "background-color: red;\n" "border-radius: 8px;\n" "}\n" "\n" "QPushButton#btnRojo:hover{\n" "background-color: #e74c3c;\n" "border-radius: 8px;\n" "}") self.horizontalLayout.addWidget(self.btnRojo) self.gridLayout_5.addWidget(self.header, 0, 0, 1, 1) self.frameLogo = QFrame(self.Contenedor) self.frameLogo.setObjectName(u"frameLogo") sizePolicy.setHeightForWidth( self.frameLogo.sizePolicy().hasHeightForWidth()) self.frameLogo.setSizePolicy(sizePolicy) self.frameLogo.setStyleSheet(u"") self.frameLogo.setFrameShape(QFrame.StyledPanel) self.frameLogo.setFrameShadow(QFrame.Raised) self.gridLayout_3 = QGridLayout(self.frameLogo) self.gridLayout_3.setObjectName(u"gridLayout_3") self.lblWhatsPy = QLabel(self.frameLogo) self.lblWhatsPy.setObjectName(u"lblWhatsPy") font = QFont() font.setFamily(u"century gothic") font.setBold(True) font.setWeight(75) self.lblWhatsPy.setFont(font) self.lblWhatsPy.setAlignment(Qt.AlignCenter) self.gridLayout_3.addWidget(self.lblWhatsPy, 1, 0, 1, 1) self.icon = QLabel(self.frameLogo) self.icon.setObjectName(u"icon") self.icon.setMaximumSize(QSize(70, 70)) self.icon.setPixmap(QPixmap(u":/logo/logo.png")) self.icon.setScaledContents(True) self.gridLayout_3.addWidget(self.icon, 0, 0, 1, 1) self.gridLayout_5.addWidget(self.frameLogo, 1, 0, 1, 1) self.label = QLabel(self.Contenedor) self.label.setObjectName(u"label") sizePolicy.setHeightForWidth( self.label.sizePolicy().hasHeightForWidth()) self.label.setSizePolicy(sizePolicy) self.label.setFont(font) self.label.setAlignment(Qt.AlignCenter) self.gridLayout_5.addWidget(self.label, 4, 0, 1, 1) self.gridLayout_2.addWidget(self.Contenedor, 0, 0, 1, 1) self.lblGrip = QLabel(self.VentanaFrame) self.lblGrip.setObjectName(u"lblGrip") sizePolicy.setHeightForWidth( self.lblGrip.sizePolicy().hasHeightForWidth()) self.lblGrip.setSizePolicy(sizePolicy) self.lblGrip.setMaximumSize(QSize(10, 10)) self.lblGrip.setCursor(QCursor(Qt.BusyCursor)) self.lblGrip.setLayoutDirection(Qt.RightToLeft) self.lblGrip.setStyleSheet( u"QLabel#lblGrip:hover{\n" " background: rgb(18, 140, 126);\n" " border-radius: 5px;\n" "}\n" "\n" "QLabel#lblGrip{\n" " background-color: qlineargradient(spread:pad, x1:0.953, y1:1, x2:1, y2:0, stop:0 #075E54, stop:1 #128C7E);\n" " border-radius: 5px;\n" "}") self.gridLayout_2.addWidget(self.lblGrip, 1, 0, 1, 1) self.gridLayout.addWidget(self.VentanaFrame, 0, 0, 1, 1) self.retranslateUi(Ventana) QMetaObject.connectSlotsByName(Ventana) # setupUi def retranslateUi(self, Ventana): Ventana.setWindowTitle( QCoreApplication.translate("Ventana", u"Form", None)) self.btnExportar.setText( QCoreApplication.translate("Ventana", u"Exportar", None)) self.btnAbrir.setText( QCoreApplication.translate("Ventana", u"Abrir TXT", None)) ___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(0) ___qtablewidgetitem.setText( QCoreApplication.translate("Ventana", u"N\u00fameros", None)) ___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(1) ___qtablewidgetitem1.setText( QCoreApplication.translate("Ventana", u"Mensajes", None)) __sortingEnabled = self.tableWidget.isSortingEnabled() self.tableWidget.setSortingEnabled(False) self.tableWidget.setSortingEnabled(__sortingEnabled) self.btnVerde.setText("") self.btnAmarillo.setText("") self.btnRojo.setText("") self.lblWhatsPy.setText( QCoreApplication.translate("Ventana", u"WhatsPy", None)) self.icon.setText("") self.label.setText( QCoreApplication.translate( "Ventana", u"Todav\u00eda no haz cargado ning\u00fan chat", None)) self.lblGrip.setText("")
class Table(QWidget): def __init__(self): super().__init__() self.layout = QHBoxLayout() size = 5 self.table = QTableWidget(size, size) self.table.setMaximumSize(275, 295) self.table.setHorizontalHeaderLabels([str(i + 1) + "\n " for i in range(size)]) self.tableResults = QTableWidget(size, 2) self.tableResults.horizontalHeader().setDefaultSectionSize(100) self.tableResults.verticalHeader().setDefaultSectionSize(50) self.tableResults.setMaximumSize(225, 295) self.tableResults.setHorizontalHeaderLabels(["Предельные\nвероятности", "Время\nстабилизации"]) #self.table.setCellWidget(1000, 1000, self) self.table.horizontalHeader().setDefaultSectionSize(50) self.table.verticalHeader().setDefaultSectionSize(50) #self.table.setCellWidget(200, 200, self.table) delegate = IconDelegate(self.table) self.table.setItemDelegate(delegate) self.tableLayout = QHBoxLayout() self.tableLayout.addWidget(self.tableResults) tmp = QHBoxLayout() tmp.addWidget(self.table) tmp.setMargin(10) self.tableLayout.addLayout(tmp) self.tableLayout.setAlignment(Qt.AlignRight) self.panel = QVBoxLayout() self.cell = QLineEdit() self.cell.setMaximumSize(150, 20) self.buttonSize = QPushButton("Задать размер") self.buttonSize.setMaximumSize(150, 50) self.buttonSize.clicked.connect(self.resizeTable) self.buttonCalculate = QPushButton("Вычислить") self.buttonCalculate.clicked.connect(self.solve) self.buttonCalculate.setMaximumSize(150, 50) self.panel.addWidget(self.cell) self.panel.addWidget(self.buttonSize) self.panel.addWidget(self.buttonCalculate) self.panel.setAlignment(Qt.AlignTop) self.layout.addLayout(self.panel) self.layout.addLayout(self.tableLayout) self.layout.setAlignment(Qt.AlignTop) #self.layout.setAlignment(Qt.AlignRight) self.setLayout(self.layout) def resizeTable(self): print("fill") try: size = int(self.cell.text()) except ValueError: print("Wrong value") return print("resize") self.table.setColumnCount(size) self.table.setRowCount(size) self.tableResults.setRowCount(size) self.fillTable(size) def fillTable(self, size): print("fill") self.table.setMaximumSize(size*50 + 25, size*50 + 45) self.tableResults.setMaximumSize(225, size * 50 + 45) newFont = QFont("Arial", 12) for i in range(size): for j in range(size): if i == j: #self.table.setItem(i, j, QTableWidgetItem(QIcon("sticker.svg"), "")) item = QTableWidgetItem("0") item.setTextAlignment(Qt.AlignCenter) item.setFont(newFont) self.table.setItem(i, j, item) else: item = QTableWidgetItem(str(random.randrange(1))) item.setTextAlignment(Qt.AlignCenter) item.setFont(newFont) self.table.setItem(i, j, item) self.table.setHorizontalHeaderLabels([str(i + 1) + "\n " for i in range(size)]) print(self.table.item(0, 1).text()) for i in range(size): for j in range(2): item = QTableWidgetItem("__") item.setTextAlignment(Qt.AlignCenter) item.setFont(newFont) self.tableResults.setItem(i, j, item) def solve(self): self.calculatProbabilities() self.calculateStabilisationTime() def calculatProbabilities(self): matrix = [[float(self.table.item(j, i).text()) for j in range(self.table.rowCount())] for i in range(self.table.columnCount())] for i in range(self.table.columnCount()): matrix[i][i] = -sum([matrix[j][i] for j in range(self.table.columnCount())]) #print(matrix) b = [0]*self.table.columnCount() b[0] = 1 matrix[0] = [1 for _ in matrix[0]] #print(matrix) #print(b) a = numpy.array(matrix) b = numpy.array(b) answ = list(numpy.linalg.solve(a, b)) #print(answ) for i in range(len(answ)): self.tableResults.item(i, 0).setText("{:.4f}".format(answ[i])) def calculateStabilisationTime(self): matrix = [[float(self.table.item(i, j).text()) for j in range(self.table.rowCount())] for i in range(self.table.columnCount())] print(matrix) start_probabilities = [1/len(matrix[0]) for _ in matrix[0]] print(start_probabilities) limit_probabilities = calc_limit_probabilities(matrix) print(limit_probabilities) stabilization_times = calc_stabilization_times(matrix, start_probabilities, limit_probabilities) print(stabilization_times) for i in range(len(stabilization_times)): self.tableResults.item(i, 1).setText("{:.4f}".format(stabilization_times[i]))
class TableWidget(QWidget): def __init__(self, table, headers, bold=True, mono=True, tooltips=None, align=False, search=True, parent=None): super(TableWidget, self).__init__(parent) self.table_widget = QTableWidget(len(table), len(table[0])) for i, row in enumerate(table): for j, item in enumerate(row): if item is not None: self.table_widget.setItem(i, j, QTableWidgetItem(str(item))) if tooltips is not None: self.table_widget.setToolTip(tooltips[i][j]) modify_font(self.table_widget.item(i, j), bold=bold and j == 0, mono=mono) if align: self.table_widget.item(i, j).setTextAlignment( Qt.AlignRight) self.table_headers = headers self.table_widget.setHorizontalHeaderLabels(self.table_headers) self.table_widget.setSelectionMode(QAbstractItemView.SingleSelection) self.table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_widget.resizeColumnsToContents() self.table_widget.setAlternatingRowColors(True) self.table_widget.itemDoubleClicked.connect(self.copy) search_layout = QHBoxLayout() search_layout.addWidget(QLabel(self.tr('Search:'))) self.search_edit = QLineEdit() self.search_edit.textChanged.connect(self.start) self.search_edit.returnPressed.connect(self.next) search_layout.addWidget(self.search_edit) clear_button = QToolButton() clear_button.setIcon(QIcon('icons/clear.svg')) clear_button.setShortcut(QKeySequence.DeleteCompleteLine) clear_button.setToolTip(self.tr('Clear pattern')) clear_button.clicked.connect(self.search_edit.clear) search_layout.addWidget(clear_button) self.case_button = QToolButton() self.case_button.setText(self.tr('Aa')) self.case_button.setCheckable(True) self.case_button.toggled.connect(self.start) self.case_button.setToolTip(self.tr('Case sensitive')) search_layout.addWidget(self.case_button) self.word_button = QToolButton() self.word_button.setText(self.tr('W')) self.word_button.setCheckable(True) self.word_button.toggled.connect(self.start) self.word_button.setToolTip(self.tr('Whole words')) search_layout.addWidget(self.word_button) self.regex_button = QToolButton() self.regex_button.setText(self.tr('.*')) self.regex_button.setCheckable(True) self.regex_button.toggled.connect(self.start) self.regex_button.setToolTip(self.tr('Regular expression')) search_layout.addWidget(self.regex_button) prev_button = QToolButton() prev_button.setIcon(QIcon('icons/up.svg')) prev_button.setShortcut(QKeySequence.FindPrevious) prev_button.clicked.connect(self.previous) prev_button.setToolTip(self.tr('Previous occurence')) search_layout.addWidget(prev_button) next_button = QToolButton() next_button.setIcon(QIcon('icons/down.svg')) next_button.setShortcut(QKeySequence.FindNext) next_button.clicked.connect(self.next) next_button.setToolTip(self.tr('Next occurence')) search_layout.addWidget(next_button) self.matches_label = QLabel() search_layout.addWidget(self.matches_label) search_layout.addStretch() export_button = QToolButton() export_button.setText(self.tr('Export...')) export_button.clicked.connect(self.export) search_layout.addWidget(export_button) main_layout = QVBoxLayout() main_layout.addWidget(self.table_widget) if search: main_layout.addLayout(search_layout) self.setLayout(main_layout) def start(self): self.search(self.search_edit.text(), -1, -1, 1) def next(self): row = self.table_widget.currentRow() col = self.table_widget.currentColumn() + 1 if col == self.table_widget.columnCount(): row += 1 col = 0 self.search(self.search_edit.text(), row, col, +1) def previous(self): row = self.table_widget.currentRow() col = self.table_widget.currentColumn() - 1 if col == -1: row -= 1 col = self.table_widget.columnCount() - 1 self.search(self.search_edit.text(), row, col, -1) def search(self, pattern, row, col, direction): nocase = not self.case_button.isChecked() word = self.word_button.isChecked() regex = self.regex_button.isChecked() matches = 0 index = 0 if direction > 0: row_range = range(self.table_widget.rowCount() - 1, -1, -1) col_range = range(self.table_widget.columnCount() - 1, -1, -1) else: row_range = range(self.table_widget.rowCount()) col_range = range(self.table_widget.columnCount()) for i in row_range: for j in col_range: item = self.table_widget.item(i, j) if item is not None: text = item.text() if regex: match = QRegularExpression(pattern).match( text).hasMatch() else: if nocase: text = text.lower() pattern = pattern.lower() if word: match = text == pattern else: match = pattern in text if match and pattern: self.table_widget.item(i, j).setBackground(Qt.yellow) if (direction > 0 and (i > row or i == row and j > col)) or \ (direction < 0 and (i < row or i == row and j < col)): self.table_widget.setCurrentCell(i, j) index = matches matches += 1 else: self.table_widget.item(i, j).setBackground(Qt.transparent) if pattern: self.matches_label.setVisible(True) if matches > 0: match = matches - index if direction > 0 else index + 1 self.matches_label.setText( self.tr('match #{}/{}'.format(match, matches))) self.matches_label.setStyleSheet('color: #000000') modify_font(self.matches_label, bold=True) else: self.matches_label.setText(self.tr('not found!')) self.matches_label.setStyleSheet('color: #FF0000') modify_font(self.matches_label, italic=True) else: self.matches_label.setText('') def export(self): settings = QSettings() filename = QFileDialog.getSaveFileName(self, self.tr('Export metadata'), settings.value('save_folder'), self.tr('CSV files (*.csv)'))[0] if not filename: return if not filename.endswith('.csv'): filename += '.csv' settings.setValue('save_folder', QFileInfo(filename).absolutePath()) rows = self.table_widget.rowCount() cols = self.table_widget.columnCount() table = [[None for _ in range(cols)] for __ in range(rows)] for i in range(rows): for j in range(cols): item = self.table_widget.item(i, j) if item is not None: table[i][j] = item.text() with open(filename, 'w') as file: writer = csv.writer(file) writer.writerow(self.table_headers) writer.writerows(table) self.info_message.emit(self.tr('Table contents exported to disk')) def copy(self, item): QApplication.clipboard().setText(item.text()) QToolTip.showText(QCursor.pos(), self.tr('Cell contents copied to clipboard'), self, QRect(), 3000)
class Widget(QWidget): def __init__(self): QWidget.__init__(self) self.setWindowTitle("Algorytm DNF") """Inicjlizacja zmiennych, na których będą dokynowane obliczenia oraz utworzenie obiektów (tabela,pola edycyjne,przyciski)""" self.table = QTableWidget() self.features = 0 self.examples = 0 self.file_name = QLineEdit() self.from_file = QPushButton("Wprowadź dane z pliku") self.solve = QPushButton("Rozwiąż") self.result = QLabel() self.error_info = QMessageBox() """Tworzenie layoutów a następnie dodawanie do nich widgetów""" self.left = QVBoxLayout() self.left.addWidget(self.from_file) self.left.addWidget(self.solve) self.left.addWidget(self.result) self.solve.setEnabled(False) self.center = QVBoxLayout() """Tworzenie głównego layoutu a następnie dodawanie do nich trzech utworzonych wcześniej""" self.layout = QHBoxLayout() self.layout.addLayout(self.left) self.layout.addLayout(self.center) self.setLayout(self.layout) """Komunikacja pomiędzy obiektami""" self.from_file.clicked.connect(self.create_table) self.solve.clicked.connect(self.DNF) """Tworzenie tabeli o ilości cech i przykładów podanych przez użytkownika i uzupełnianie jej wartościami z pliku""" @Slot() def create_table(self): try: self.file_name.setText(QFileDialog.getOpenFileName()[0]) with open(self.file_name.text(), 'r') as f: for idx_line, line in enumerate(f): self.examples = idx_line for idx, item in enumerate(line.split(' ')): self.features = idx - 1 self.solve.setEnabled(True) self.table.setColumnCount(self.features + 1) self.table.setRowCount(self.examples) features = list(range(1, self.features + 1)) features = ["f" + str(x) for x in features] self.table.setHorizontalHeaderItem(self.features, QTableWidgetItem("Etykieta")) self.table.setHorizontalHeaderLabels(features) self.table.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.table.verticalHeader().setSectionResizeMode( QHeaderView.Stretch) self.center.addWidget(self.table) with open(self.file_name.text(), 'r') as f: for idx_line, line in enumerate(f): for idx, item in enumerate(line.split(' ')): self.table.setItem(idx_line, idx, QTableWidgetItem(str(item))) except FileNotFoundError: self.error_info.setWindowTitle("Uwaga!") self.error_info.setText("Nie wybrabrano pliku.") self.error_info.exec() """Konwertowanie obiektowej tabeli na listę D, na której będą dokonywane obliczenia""" def convert_to_lists(self): self.D = [] ex = [] try: for i in range(self.table.rowCount()): for j in range(self.table.columnCount()): ex.append(int(self.table.item(i, j).text())) if len(ex) == self.table.columnCount(): self.D.append(ex) ex = [] break except: self.error_info.setWindowTitle("Uwaga!") self.error_info.setText( "Uzupełnij poprawnie wszystkie pola w tabeli.") self.error_info.exec() """Algorytm DNF""" @Slot() def DNF(self): self.convert_to_lists() P = [self.D[i] for i in range(len(self.D)) if self.D[i][-1] == 1] h = [] fail = 0 while P != [[-1] * len(P[i]) for i in range(len(P))] and fail == 0: N = [self.D[i] for i in range(len(self.D)) if self.D[i][-1] == 0] r = [] while N != [[-1] * len(N[i]) for i in range(len(N))] and fail == 0: chosen_index = self.find_f(P, N) r.append(chosen_index) self.clear_negative_rows(chosen_index, N) if len(r) != len(set(r)): fail = 1 if self.clear_positive_rows(r, P) == 1: fail = 1 r = ["f" + str(i + 1) for i in r] r = " ʌ ".join(r) h.append(r) self.get_solution(h, fail) def find_f(self, P, N): sum_p = list(self.sum_over_columns(P)) sum_n = list(self.sum_over_columns(N)) v = 0 chosen_index = 0 for i in range(len(sum_p)): if sum_n[i] == 0: sum_n[i] = 0.001 if sum_p[i] // sum_n[i] > v: v = sum_p[i] // sum_n[i] chosen_index = i return chosen_index def sum_over_columns(self, tab): for i in range(len(tab[0]) - 1): count = 0 for j in range(len(tab)): if tab[j][i] == 1: count += 1 yield count def clear_negative_rows(self, index, N): n = len(N) for i in range(n): if N[i][index] == 0: N[i] = [-1] * len(N[i]) def clear_positive_rows(self, r, P): n = len(P) mark = 0 for i in range(n): sum = 0 for j in r: if P[i][j] == 1: sum += 1 if sum == len(r): P[i] = [-1] * len(P[i]) mark += 1 if mark == 0: return 1 return 0 def get_solution(self, h, fail): if fail == 1: self.result.setText("Brak rozwiązania") else: self.result.setText("Rozwiązanie:\n\nh= (" + ") v (".join(h) + ")")
class UiMainWindow(object): '''Интерфейс основного окна программы. Created by: Qt User Interface Compiler. Задано: панель инструментов с кнопками "Новый расчёт" (очистка всех полей), выбор режима расчёта, запуск расчёта; четыре поля ввода (длина заготовки, длина безусловного отхода, ширина реза, длина первичной обрезки торца заготовки) таблица ввода длин элементов и их количеств; кнопки "добавить строку в конец таблицы" и "удалить последнюю строку в таблице"; поле вывода результата расчёта. ''' def setupUi(self, MainWindow): if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") MainWindow.resize(700, 495) self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") self.verticalLayout = QVBoxLayout(self.centralwidget) self.verticalLayout.setSpacing(0) self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.main_frame = QFrame(self.centralwidget) self.main_frame.setObjectName(u"main_frame") self.main_frame.setFrameShape(QFrame.StyledPanel) self.main_frame.setFrameShadow(QFrame.Raised) self.verticalLayout_2 = QVBoxLayout(self.main_frame) self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setObjectName(u"verticalLayout_2") self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) # "панель инструментов" self.top_frame = QFrame(self.main_frame) self.top_frame.setObjectName(u"top_frame") self.top_frame.setMinimumSize(QSize(0, 60)) self.top_frame.setMaximumSize(QSize(16777215, 60)) self.top_frame.setFrameShape(QFrame.StyledPanel) self.top_frame.setFrameShadow(QFrame.Raised) self.horizontalLayout = QHBoxLayout(self.top_frame) self.horizontalLayout.setSpacing(5) self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setContentsMargins(5, 5, 5, 5) # кнопка "новый расчёт" self.new_calc_btn = QPushButton(self.top_frame) self.new_calc_btn.setObjectName(u"new_calc_btn") self.new_calc_btn.setMinimumSize(QSize(110, 50)) self.new_calc_btn.setMaximumSize(QSize(110, 50)) icon = QIcon() icon.addFile(NEW_CALC_ICON, QSize(), QIcon.Normal, QIcon.Off) self.new_calc_btn.setIcon(icon) self.new_calc_btn.setIconSize(QSize(45, 45)) self.horizontalLayout.addWidget(self.new_calc_btn) # кнопка "выбор режима расчёта" self.select_mode_btn = QPushButton(self.top_frame) self.select_mode_btn.setObjectName(u"select_mode_btn") self.select_mode_btn.setMinimumSize(QSize(110, 50)) self.select_mode_btn.setMaximumSize(QSize(110, 50)) icon1 = QIcon() icon1.addFile(SET_MODE_ICON, QSize(), QIcon.Normal, QIcon.Off) self.select_mode_btn.setIcon(icon1) self.select_mode_btn.setIconSize(QSize(45, 45)) self.horizontalLayout.addWidget(self.select_mode_btn) # кнопка "запуск расчёта" self.start_calc_btn = QPushButton(self.top_frame) self.start_calc_btn.setObjectName(u"start_calc_btn") self.start_calc_btn.setMinimumSize(QSize(110, 50)) self.start_calc_btn.setMaximumSize(QSize(110, 50)) icon2 = QIcon() icon2.addFile(START_CALC_ICON, QSize(), QIcon.Normal, QIcon.Off) self.start_calc_btn.setIcon(icon2) self.start_calc_btn.setIconSize(QSize(45, 45)) self.horizontalLayout.addWidget(self.start_calc_btn) self.horizontalSpacer = QSpacerItem(338, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(self.horizontalSpacer) self.verticalLayout_2.addWidget(self.top_frame) self.bottom_frame = QFrame(self.main_frame) self.bottom_frame.setObjectName(u"bottom_frame") self.bottom_frame.setFrameShape(QFrame.StyledPanel) self.bottom_frame.setFrameShadow(QFrame.Raised) self.horizontalLayout_2 = QHBoxLayout(self.bottom_frame) self.horizontalLayout_2.setSpacing(5) self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.horizontalLayout_2.setContentsMargins(5, 5, 5, 5) self.input_frame = QFrame(self.bottom_frame) self.input_frame.setObjectName(u"input_frame") self.input_frame.setMinimumSize(QSize(340, 0)) self.input_frame.setMaximumSize(QSize(340, 16777215)) self.input_frame.setFrameShape(QFrame.StyledPanel) self.input_frame.setFrameShadow(QFrame.Raised) self.verticalLayout_3 = QVBoxLayout(self.input_frame) self.verticalLayout_3.setSpacing(5) self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) # поле ввода длины заготовки self.length_layout = QHBoxLayout() self.length_layout.setSpacing(5) self.length_layout.setObjectName(u"length_layout") self.length_label = QLabel(self.input_frame) self.length_label.setObjectName(u"length_label") self.length_label.setMinimumSize(QSize(165, 25)) self.length_label.setMaximumSize(QSize(165, 25)) self.length_layout.addWidget(self.length_label) self.length_entry = QLineEdit(self.input_frame) self.length_entry.setObjectName(u"length_entry") self.length_entry.setMinimumSize(QSize(165, 25)) self.length_entry.setMaximumSize(QSize(165, 25)) self.length_layout.addWidget(self.length_entry) self.verticalLayout_3.addLayout(self.length_layout) # поле ввода длины безусловного отхода self.waste_layout = QHBoxLayout() self.waste_layout.setSpacing(5) self.waste_layout.setObjectName(u"waste_layout") self.waste_label = QLabel(self.input_frame) self.waste_label.setObjectName(u"waste_label") self.waste_label.setMinimumSize(QSize(165, 25)) self.waste_label.setMaximumSize(QSize(165, 25)) self.waste_layout.addWidget(self.waste_label) self.waste_entry = QLineEdit(self.input_frame) self.waste_entry.setObjectName(u"waste_entry") self.waste_entry.setMinimumSize(QSize(165, 25)) self.waste_entry.setMaximumSize(QSize(165, 25)) self.waste_layout.addWidget(self.waste_entry) self.verticalLayout_3.addLayout(self.waste_layout) # поле ввода ширины реза self.cut_layout = QHBoxLayout() self.cut_layout.setSpacing(5) self.cut_layout.setObjectName(u"cut_layout") self.cut_label = QLabel(self.input_frame) self.cut_label.setObjectName(u"cut_label") self.cut_label.setMinimumSize(QSize(165, 25)) self.cut_label.setMaximumSize(QSize(165, 25)) self.cut_layout.addWidget(self.cut_label) self.cut_entry = QLineEdit(self.input_frame) self.cut_entry.setObjectName(u"cut_entry") self.cut_entry.setMinimumSize(QSize(165, 25)) self.cut_entry.setMaximumSize(QSize(165, 25)) self.cut_layout.addWidget(self.cut_entry) self.verticalLayout_3.addLayout(self.cut_layout) # поле ввода длины подрезки торца заготовки self.trim_layout = QHBoxLayout() self.trim_layout.setSpacing(5) self.trim_layout.setObjectName(u"trim_layout") self.trim_label = QLabel(self.input_frame) self.trim_label.setObjectName(u"trim_label") self.trim_label.setMinimumSize(QSize(165, 25)) self.trim_label.setMaximumSize(QSize(165, 25)) self.trim_layout.addWidget(self.trim_label) self.trim_entry = QLineEdit(self.input_frame) self.trim_entry.setObjectName(u"trim_entry") self.trim_entry.setMinimumSize(QSize(165, 25)) self.trim_entry.setMaximumSize(QSize(165, 25)) self.trim_layout.addWidget(self.trim_entry) self.verticalLayout_3.addLayout(self.trim_layout) # таблица ввода длин и количеств элементов self.input_table = QTableWidget(self.input_frame) if (self.input_table.columnCount() < 2): self.input_table.setColumnCount(2) if (self.input_table.rowCount() < 7): self.input_table.setRowCount(7) self.input_table.setObjectName(u"input_table") self.input_table.setRowCount(7) self.input_table.setColumnCount(2) self.input_table.horizontalHeader().setMinimumSectionSize(33) self.input_table.horizontalHeader().setDefaultSectionSize(130) self.input_table.horizontalHeader().setProperty( "showSortIndicator", True, ) self.input_table.horizontalHeader().setStretchLastSection(True) self.input_table.verticalHeader().setMinimumSectionSize(30) self.input_table.verticalHeader().setDefaultSectionSize(30) self.verticalLayout_3.addWidget(self.input_table) self.horizontalLayout_3 = QHBoxLayout() self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") # кнопка "добавить строку в конец таблицы" self.add_str_button = QPushButton(self.input_frame) self.add_str_button.setObjectName(u"add_str_button") self.add_str_button.setMinimumSize(QSize(150, 35)) self.add_str_button.setMaximumSize(QSize(150, 35)) icon3 = QIcon() icon3.addFile( ADD_STR_ICON, QSize(), QIcon.Normal, QIcon.Off, ) self.add_str_button.setIcon(icon3) self.add_str_button.setIconSize(QSize(25, 25)) self.horizontalLayout_3.addWidget(self.add_str_button) # кнопка "удалить последнюю строку в таблице" self.remove_str_button = QPushButton(self.input_frame) self.remove_str_button.setObjectName(u"remove_str_button") self.remove_str_button.setMinimumSize(QSize(150, 35)) self.remove_str_button.setMaximumSize(QSize(150, 35)) icon4 = QIcon() icon4.addFile( DESTR_STR_ICON, QSize(), QIcon.Normal, QIcon.Off, ) self.remove_str_button.setIcon(icon4) self.remove_str_button.setIconSize(QSize(25, 25)) self.horizontalLayout_3.addWidget(self.remove_str_button) self.verticalLayout_3.addLayout(self.horizontalLayout_3) self.horizontalLayout_2.addWidget(self.input_frame) self.output_frame = QFrame(self.bottom_frame) self.output_frame.setObjectName(u"output_frame") self.output_frame.setFrameShape(QFrame.StyledPanel) self.output_frame.setFrameShadow(QFrame.Raised) self.verticalLayout_4 = QVBoxLayout(self.output_frame) self.verticalLayout_4.setObjectName(u"verticalLayout_4") # поле вывода результатов расчёта self.textBrowser = QTextBrowser(self.output_frame) self.textBrowser.setObjectName(u"textBrowser") self.verticalLayout_4.addWidget(self.textBrowser) self.horizontalLayout_4 = QHBoxLayout() self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") # строка состояния self.label_2 = QLabel(self.output_frame) self.label_2.setObjectName(u"label_2") self.label_2.setMinimumSize(QSize(25, 25)) self.label_2.setMaximumSize(QSize(16777215, 25)) self.horizontalLayout_4.addWidget(self.label_2) self.horizontalSpacer_2 = QSpacerItem( 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum, ) self.horizontalLayout_4.addItem(self.horizontalSpacer_2) # поле вывода версии программы self.label = QLabel(self.output_frame) self.label.setObjectName(u"label") self.label.setMinimumSize(QSize(0, 25)) self.label.setMaximumSize(QSize(16777215, 25)) self.horizontalLayout_4.addWidget(self.label) self.verticalLayout_4.addLayout(self.horizontalLayout_4) self.horizontalLayout_2.addWidget(self.output_frame) self.verticalLayout_2.addWidget(self.bottom_frame) self.verticalLayout.addWidget(self.main_frame) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QMetaObject.connectSlotsByName(MainWindow) # setupUi def retranslateUi(self, MainWindow): MainWindow.setWindowTitle( QCoreApplication.translate("MainWindow", u"simple linear nesting", None)) # if QT_CONFIG(tooltip) self.new_calc_btn.setToolTip( QCoreApplication.translate("MainWindow", u"Clear all fields, Ctrl+N", None)) # endif // QT_CONFIG(tooltip) self.new_calc_btn.setText( QCoreApplication.translate("MainWindow", u"new", None)) # if QT_CONFIG(shortcut) self.new_calc_btn.setShortcut( QCoreApplication.translate("MainWindow", u"Ctrl+N", None)) # endif // QT_CONFIG(shortcut) # if QT_CONFIG(tooltip) self.select_mode_btn.setToolTip( QCoreApplication.translate("MainWindow", u"Select calculation mode, Ctrl+M", None)) # endif // QT_CONFIG(tooltip) self.select_mode_btn.setText( QCoreApplication.translate("MainWindow", u"mode", None)) # if QT_CONFIG(shortcut) self.select_mode_btn.setShortcut( QCoreApplication.translate("MainWindow", u"Ctrl+M", None)) # endif // QT_CONFIG(shortcut) # if QT_CONFIG(tooltip) self.start_calc_btn.setToolTip( QCoreApplication.translate("MainWindow", u"Start calculation, Ctrl+R", None)) # endif // QT_CONFIG(tooltip) self.start_calc_btn.setText( QCoreApplication.translate("MainWindow", u"start", None)) # if QT_CONFIG(shortcut) self.start_calc_btn.setShortcut( QCoreApplication.translate("MainWindow", u"Ctrl+R", None)) # endif // QT_CONFIG(shortcut) self.length_label.setText( QCoreApplication.translate("MainWindow", u"workpiece length", None)) # if QT_CONFIG(tooltip) self.length_entry.setToolTip( QCoreApplication.translate("MainWindow", u"Enter the length of the workpiece", None)) # endif // QT_CONFIG(tooltip) self.waste_label.setText( QCoreApplication.translate("MainWindow", u"irreducible waste", None)) # if QT_CONFIG(tooltip) self.waste_entry.setToolTip( QCoreApplication.translate("MainWindow", u"Enter the length of the waste", None)) # endif // QT_CONFIG(tooltip) self.cut_label.setText( QCoreApplication.translate("MainWindow", u"cutting width", None)) # if QT_CONFIG(tooltip) self.cut_entry.setToolTip( QCoreApplication.translate("MainWindow", u"Enter the width of the cutting", None)) # endif // QT_CONFIG(tooltip) self.trim_label.setText( QCoreApplication.translate("MainWindow", u"trim the end", None)) # if QT_CONFIG(tooltip) self.trim_entry.setToolTip( QCoreApplication.translate( "MainWindow", u"Enter the length of the trimming end", None)) # endif // QT_CONFIG(tooltip) # if QT_CONFIG(tooltip) self.add_str_button.setToolTip( QCoreApplication.translate( "MainWindow", u"Add another row to the table, Ctrl++", None)) # endif // QT_CONFIG(tooltip) self.add_str_button.setText("") # if QT_CONFIG(shortcut) self.add_str_button.setShortcut( QCoreApplication.translate("MainWindow", u"Ctrl++", None)) # endif // QT_CONFIG(shortcut) # if QT_CONFIG(tooltip) self.remove_str_button.setToolTip( QCoreApplication.translate( "MainWindow", u"Delete the last row in the table, Ctrl+-", None)) # endif // QT_CONFIG(tooltip) self.remove_str_button.setText("") # if QT_CONFIG(shortcut) self.remove_str_button.setShortcut( QCoreApplication.translate("MainWindow", u"Ctrl+-", None)) # endif // QT_CONFIG(shortcut) self.label_2.setText("") self.label.setText( QCoreApplication.translate("MainWindow", u"v.0.0.0", None))
class ConfigurationDialog(QDialog): def __init__(self, node): super().__init__() self.node = node self.delete_popup = None self.layout = QGridLayout() self.table = QTableWidget(0, 2) self.node.configuration.parameter_change.connect( self.handle_configuration_change) self.table.cellChanged.connect(self.handle_cell_change) self.table.setHorizontalHeaderLabels(['Key', 'Value']) self.layout.addWidget(self.table, 0, 0, 1, 2) self.deleteButton = QPushButton('Remove Selected Row') self.deleteButton.clicked.connect(self.handle_delete_action) self.layout.addWidget(self.deleteButton, 1, 0) self.addButton = QPushButton('Add Configuration') self.addButton.clicked.connect(self.handle_add_action) self.layout.addWidget(self.addButton, 1, 1) self.setLayout(self.layout) ################################# # Add/Update/Get for table rows # ################################# def add_row(self, key, value): """ Adds a new row with key and value. :param key: Key for the new row :param value: Value for the new row :return: None """ row_index = self.table.rowCount() self.table.insertRow(row_index) self.update_row(row_index, key, value) def update_row(self, row_index, key, value): """ Updates a table row by index, with key and value. :param row_index: Index of the row to be updated :param key: Key for that row :param value: Value for that row :return: None """ # Disconnecting cellChanged event so we don't get a feedback loop self.table.cellChanged.disconnect() for column_index, cell_text in enumerate([key, value]): item = QTableWidgetItem() item.setText(str(cell_text)) if column_index != 1: # noinspection PyUnresolvedReferences item.setFlags(Qt.ItemIsEnabled) self.table.setItem(row_index, column_index, item) # Connecting the cellChanged event again self.table.cellChanged.connect(self.handle_cell_change) self.table.resizeColumnsToContents() def remove_row(self, row_index): row: Row = self.get_row_by_index(row_index) key = row[1][0] value = row[1][1] old_values = self.node.configuration[key] if old_values is None: old_values = [] elif not isinstance(old_values, list): old_values = [old_values] new_values = [] for old_value in old_values: if str(old_value) != str(value): new_values.append(old_value) if key in self.node.configuration: del self.node.configuration[key] self.node.configuration[key] = new_values # The event triggered by configuration values changing will update the table def get_rows_by_key(self, key: str) -> List[Row]: """ Returns the list of rows for a specific key. Since we can have multiple values for the same key we need to return a list of rows. :param key: The key we're looking for :return: The list of rows in the format: [ (row_index, [column0value, column1value, ...]), ... ] """ rows = [] for row_index in range(self.table.rowCount()): _key = self.table.item(row_index, 0).text() if self.table.item( row_index, 0) else '' if _key == key: rows.append((row_index, [ self.table.item(row_index, column_index).text() if self.table.item(row_index, column_index) else '' for column_index in range(self.table.columnCount()) ])) return rows def get_row_by_index(self, index: int) -> Optional[Row]: """ Returns a row by its index, or None if it doesn't exist. :param index: index of the row to retrieve :return: If the row doesn't exist, returns None If the row exists, returns a tuple with the row number and the list of columns Example: (1, ['server', '1']) """ for row_index in range(self.table.rowCount()): if row_index == index: return (row_index, [ self.table.item(row_index, column_index).text() if self.table.item(row_index, column_index) else '' for column_index in range(self.table.columnCount()) ]) return None ################## # Event Handlers # ################## def handle_configuration_change(self, _: str, key: str, values: List[str]): """ Handles the event of a configuration change and updates the table. There's a complication here because a single key can have multiple values, for parameters like 'addnode' or 'connect' on bitcoind. So we need to handle them all. :param _: The name of the node. (bitcoind, lnd, tor). Unused here. :param key: Which key was updated :param values: All values for that key. :return: None """ if not isinstance(values, list): values = [values] # Deleting extra existing rows old_rows: List[Row] = self.get_rows_by_key(key) delete_rows_count = len(old_rows) - len(values) if delete_rows_count > 0: # In this case there's more rows in the table than should exist for old_row in reversed(old_rows): if delete_rows_count == 0: break self.table.removeRow(old_row[0]) delete_rows_count -= 1 # Adding new rows add_rows_count = len(values) - len(old_rows) if add_rows_count > 0: # In this case there's less rows in the table than should exist for i in range(add_rows_count): new_value = values[len(old_rows) + i] self.add_row(key, new_value) # Updating rows update_values = values[0:min(len(values), len(old_rows))] for old_row_index, update_value in enumerate(update_values): row_index = old_rows[old_row_index][0] self.update_row(row_index, key, update_value) def handle_cell_change(self, row_index: int, column_index: int): """ Handles the event of a cell value change in the UI. Updates the configuration assuming that it is valid. Otherwise just keeps what was there before :param row_index: Index of the row that changed :param column_index: Index of the column that changed :return: None """ if column_index == 0: row: Row = self.get_row_by_index(row_index) key = row[1][0] if key == '': self.table.removeRow(row_index) elif column_index == 1: row: Row = self.get_row_by_index(row_index) new_value = row[1][1] key = row[1][0] rows = self.get_rows_by_key(key) new_values = sorted([row[1][1] for row in rows]) old_values = self.node.configuration[key] if old_values is None: old_values = [] elif not isinstance(old_values, list): old_values = [old_values] old_values.sort() if [str(e) for e in old_values] != [str(e) for e in new_values]: is_valid = self.node.configuration.is_valid_configuration( key, new_value) and key != '' if is_valid: if key in self.node.configuration: del self.node.configuration[key] self.node.configuration[key] = new_values else: if key == '': self.table.removeRow(row_index) else: for old_value in old_values: if old_value not in new_values: self.update_row(row_index, key, old_value) break def handle_confirm_deletion(self): self.node.configuration.parameter_change.disconnect() items = list(self.table.selectedItems()) for i, item in enumerate(items): if i == len(items) - 1: self.node.configuration.parameter_change.connect( self.handle_configuration_change) self.remove_row(item.row()) self.delete_popup = None def handle_delete_action(self): keys = [] for item in list(self.table.selectedItems()): row: Row = self.get_row_by_index(item.row()) keys.append(row[1][0]) if keys: self.delete_popup = QMessageBox() self.delete_popup.setWindowTitle('Confirm deletion') self.delete_popup.setText('Are you sure you want to delete ' + ', '.join(keys) + '?') self.delete_popup.show() self.delete_popup.buttonClicked.connect( self.handle_confirm_deletion) def handle_add_action(self): row_index = self.table.rowCount() self.table.insertRow(row_index)
class ExtraWindow(QtWidgets.QDialog): def __init__(self, parent): super().__init__(parent) self.setWindowTitle("customer - Table") self.setWindowIcon(QtGui.QIcon("assets/img/icons8-edit-file-64.png")) self.resize(662, 438) self.save_talble_changes = QPushButton("Save table changes", self) self.save_talble_changes.setGeometry(390, 290, 251, 31) self.add_table_main = QPushButton("Add Column ", self) self.add_table_main.setGeometry(390, 260, 251, 28) #self.add_table_main.clicked.connect(self.add_column) self.delet_table_column = QPushButton("Delet table column", self) self.delet_table_column.setGeometry(390, 330, 251, 31) self.not_null_box = QCheckBox("Not Null", self) self.not_null_box.setGeometry(220, 330, 81, 61) self.colm_name_label = QLabel("Column Name:", self) self.colm_name_label.setGeometry(10, 250, 111, 41) self.table_name_label_2 = QLabel("Table Name", self) self.table_name_label_2.setGeometry(10, 10, 91, 21) self.comn_name_line_edit = QLineEdit(self) self.comn_name_line_edit.setGeometry(110, 260, 151, 22) self.data_type_label = QLabel("Data Type", self) self.data_type_label.setGeometry(10, 300, 61, 16) self.data_type_line_edit = QLineEdit(self) self.data_type_line_edit.setGeometry(110, 300, 151, 22) self.table_name_line_edit = QLineEdit(self) self.table_name_line_edit.setGeometry(110, 10, 391, 22) self.foreign_key_box = QCheckBox("Foreign Key", self) self.foreign_key_box.setGeometry(120, 330, 91, 61) self.primary_key_box = QCheckBox("Primary Key", self) self.primary_key_box.setGeometry(20, 330, 111, 61) self.table_widget = QTableWidget(self) if (self.table_widget.columnCount() < 6): self.table_widget.setColumnCount(6) __qtablewidgetitem = QTableWidgetItem() self.table_widget.setHorizontalHeaderItem(0, __qtablewidgetitem) __qtablewidgetitem1 = QTableWidgetItem() self.table_widget.setHorizontalHeaderItem(1, __qtablewidgetitem1) __qtablewidgetitem2 = QTableWidgetItem() self.table_widget.setHorizontalHeaderItem(2, __qtablewidgetitem2) __qtablewidgetitem3 = QTableWidgetItem() self.table_widget.setHorizontalHeaderItem(3, __qtablewidgetitem3) __qtablewidgetitem4 = QTableWidgetItem() self.table_widget.setHorizontalHeaderItem(4, __qtablewidgetitem4) __qtablewidgetitem5 = QTableWidgetItem() self.table_widget.setHorizontalHeaderItem(5, __qtablewidgetitem5) self.table_widget.setGeometry(0, 40, 661, 201) self.table_widget.setColumnWidth(2, 85) self.table_widget.setColumnWidth(3, 85) self.table_widget.setColumnWidth(4, 85) self.frame = QFrame(self) self.frame.setGeometry(0, 390, 661, 51) self.frame.setStyleSheet(u"background-color: rgb(45,45,45);") self.frame.setFrameShape(QFrame.StyledPanel) self.frame.setFrameShadow(QFrame.Raised) self.cancel_button = QPushButton("Cancel button", self.frame) self.cancel_button.setGeometry(550, 10, 111, 28) self.cancel_button.setStyleSheet( u"background-color: rgb(255,255,255);") self.add_table_button = QPushButton("Add table", self.frame) self.add_table_button.setGeometry(442, 10, 101, 28) self.add_table_button.setStyleSheet( u"background-color: rgb(255,255,255);") self.tables() self.show() def tables(self): ''' self.setWindowTitle(QCoreApplication.translate("ExtraWindow", u"Dialog", None)) self.save_talble_changes.setText(QCoreApplication.translate("ExtraWindow", u"Save Table Changes", None)) self.add_table_main.setText(QCoreApplication.translate("ExtraWindow", u"Add Table", None)) self.delet_table_column.setText(QCoreApplication.translate("ExtraWindow", u"Delete Table Column", None)) self.not_null_box.setText(QCoreApplication.translate("ExtraWindow", u"Not null", None)) self.colm_name_label.setText(QCoreApplication.translate("ExtraWindow", u"Colomn Name", None)) self.table_name_label_2.setText(QCoreApplication.translate("ExtraWindow", u"Table Name", None)) self.data_type_label.setText(QCoreApplication.translate("ExtraWindow", u"Data Type", None)) self.foreign_key_box.setText(QCoreApplication.translate("ExtraWindow", u"Foreign key", None)) self.primary_key_box.setText(QCoreApplication.translate("ExtraWindow", u"Primary key", None)) ''' ___qtablewidgetitem = self.table_widget.horizontalHeaderItem(0) ___qtablewidgetitem.setText( QCoreApplication.translate("ExtraWindow", u"Column Name", None)) ___qtablewidgetitem1 = self.table_widget.horizontalHeaderItem(1) ___qtablewidgetitem1.setText( QCoreApplication.translate("ExtraWindow", u"Data Type", None)) ___qtablewidgetitem2 = self.table_widget.horizontalHeaderItem(2) ___qtablewidgetitem2.setText( QCoreApplication.translate("ExtraWindow", u"Primary key", None)) ___qtablewidgetitem3 = self.table_widget.horizontalHeaderItem(3) ___qtablewidgetitem3.setText( QCoreApplication.translate("ExtraWindow", u"Foreign key", None)) ___qtablewidgetitem4 = self.table_widget.horizontalHeaderItem(4) ___qtablewidgetitem4.setText( QCoreApplication.translate("ExtraWindow", u"Not Null", None)) ___qtablewidgetitem5 = self.table_widget.horizontalHeaderItem(5) ___qtablewidgetitem5.setText( QCoreApplication.translate("ExtraWindow", u"Default", None)) '''