Как проверить заполнение вкладок QTabWidget циклом без визуального переключения вкладок
Ситуация следующая: в виджете QTabWidget динамически создаются вкладки, содержащие несколько полей QLineEdit. Вкладок может быть n-ое количество.
На каждой вкладке имеется одно поле QLineEdit обязательное для заполнения (self.tabWidget.currentWidget().lineEditType.text()), если это поле не заполнить, то кнопка self.buttonAdd будет недоступна для нажатия. Проверка заполнения полей во всех вкладках происходит циклом по индексу вкладок "на лету", т.е. при каждом изменении содержимого поля QLineEdit с применением метода QLineEdit.textChanged(). При этом после каждого изменения содержимого обязательного поля вызывается функция проверки заполнения вкладок def check_tabs(self), чтобы онлайн контролировать заполнение полей и активировать при надобности кнопку self.buttonAdd.
Если создать несколько вкладок (например 3) и удалить содержимое, например средней вкладки, то начав её заполнять, вызовется функция проверки и автоматически откроется последняя вкладка и так будет происходить постоянно при каждом вводе символа. Пользователь не сможет перезаполнить нужную ему вкладку без мучений.
Проблема заключается в том, что я никак не могу сделать так, чтобы либо вкладки не переключались, когда пользователь их редактирует, либо, чтобы цикл проверки запускался "под капотом" без видимого переключения проверяемых вкладок. И если какая-то из вкладок окажется не заполнена, то кнопка self.buttonAdd не активируется. Либо сделать так, чтобы после ввода первого символа далее проверка не запускалась. Метод QLineEdit.editingFinished() не подходит, т.к. он срабатывает после того как убран фокус с заполняемого поля.
Я пробовал уже делать с использованием словаря, куда записывал индексы вкладок и их текущее состояние (0 или 1), без использования цикла, но это приводило к тому, что каждая вкладка обрабатывалась независимо от остальных и, если я в одной вкладке очищу поле, кнопка станет неактивна, но зайдя в другую вкладку я изменю ее содержимое и кнопка снова станет активной, хотя другая вкладка так и останется пустой.
Может быть можно как-нибудь ходить по вкладкам (с помощью индексов например), чтобы GUI не видела этого (типа некоего бекэнда)?
Прошу помощи сообщества, спасибо.
Минимальный рабочий код:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *
class TabPage_SO(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.labelType = QLabel("№ типа", self)
self.lineEditType = QLineEdit(self)
self.lineEditType.setClearButtonEnabled(True)
self.labelYearOfIssue = QLabel("Год выпуска *", self)
self.spinBox = QSpinBox(self)
self.spinBox.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.spinBox.setAlignment(QtCore.Qt.AlignCenter)
self.spinBox.setMinimum(1917)
self.spinBox.setMaximum(2060)
self.spinBox.setProperty("value", 2020)
self.labelSerialNumber = QLabel("Заводской №", self)
self.lineEditSerialNumber = QLineEdit(self)
self.lineEditSerialNumber.setClearButtonEnabled(True)
self.labelSpecifications = QLabel("Характеристики", self)
self.lineEditSpecifications = QLineEdit(self)
self.lineEditSpecifications.setClearButtonEnabled(True)
grid = QGridLayout(self)
grid.addWidget(self.labelType, 0, 0)
grid.addWidget(self.labelYearOfIssue, 0, 1)
grid.addWidget(self.labelSerialNumber, 0, 2)
grid.addWidget(self.lineEditType, 1, 0)
grid.addWidget(self.spinBox, 1, 1)
grid.addWidget(self.lineEditSerialNumber, 1, 2)
grid.addWidget(self.labelSpecifications, 2, 0)
grid.addWidget(self.lineEditSpecifications, 3, 0, 1, 3)
grid.setRowStretch(4, 1)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.red_warning = "border-color: red; border-style: solid; border-width: 2px; font-weight: normal;"
self.tableWidget = QTableWidget(0, 4)
self.tableWidget.setHorizontalHeaderLabels(
["№ типа", "Год выпуска *", "Заводской №", "Характеристики"])
self.tableWidget.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
self.tableWidget.setSortingEnabled(True)
self.tableWidget.setAlternatingRowColors(True)
self.buttonAdd = QPushButton('Добавить из всех вкладок в таблицу')
self.buttonAdd.clicked.connect(self.addRowTable)
self.buttonDel = QPushButton('Удалить выбранную строку в таблице')
self.buttonDel.clicked.connect(self.delRowTable)
self.tabWidget = QTabWidget()
self.tabWidget.setTabsClosable(True)
count = self.tabWidget.count()
self.nb = QToolButton(text="Добавить", autoRaise=True)
self.nb.clicked.connect(self.new_tab)
self.tabWidget.insertTab(count, QWidget(), "")
self.tabWidget.tabBar().setTabButton(count, QTabBar.RightSide, self.nb)
self.new_tab()
self.tabWidget.tabCloseRequested.connect(self.closeTab_SO)
self.button_activate()
vbox = QGridLayout(self.centralWidget)
vbox.addWidget(self.tabWidget, 0, 0, 1, 2)
vbox.addWidget(self.tableWidget, 1, 0, 1, 2)
vbox.addWidget(self.buttonAdd, 2, 0)
vbox.addWidget(self.buttonDel, 2, 1)
def new_tab(self):
'''
Создание нового ТАБа
'''
index = self.tabWidget.count() - 1
self.tabWidget.insertTab(index, TabPage_SO(self), "Tab %d" % index)
self.tabWidget.setCurrentIndex(index)
self.nb.setEnabled(False)
self.count = 0
self.button_activate()
self.tabWidget.currentWidget().lineEditType.textChanged.connect(self.check_tabs)
self.tabWidget.currentWidget().lineEditSerialNumber.textChanged.connect(self.check_tabs)
self.tabWidget.currentWidget().lineEditSpecifications.textChanged.connect(self.check_tabs)
def closeTab_SO(self, currentIndex):
'''
Удаление ТАБов
'''
self.tabWidget.removeTab(currentIndex)
self.tabWidget.setCurrentIndex(self.tabWidget.count() - 2)
self.check_tabs()
def addRowTable(self):
for i in range(self.tabWidget.count()-1):
self.tabWidget.setCurrentIndex(i)
editType = self.tabWidget.currentWidget().lineEditType.text()
spinYearOfIssue = str(self.tabWidget.currentWidget().spinBox.value())
editSerialNumber = self.tabWidget.currentWidget().lineEditSerialNumber.text()
editSpecifications = self.tabWidget.currentWidget().lineEditSpecifications.text()
if not editType:
msg = QMessageBox.information(self, 'Внимание', 'Заполните поле!')
return
self.tableWidget.setSortingEnabled(False)
rows = self.tableWidget.rowCount()
self.tableWidget.insertRow(rows)
self.tableWidget.setItem(rows, 0, QTableWidgetItem(editType))
self.tableWidget.setItem(rows, 1, QTableWidgetItem(spinYearOfIssue))
self.tableWidget.setItem(rows, 2, QTableWidgetItem(editSerialNumber))
self.tableWidget.setItem(rows, 3, QTableWidgetItem(editSpecifications))
self.tableWidget.setSortingEnabled(True)
def delRowTable(self):
row = self.tableWidget.currentRow()
if row == -1:
msg = QMessageBox.information(self, 'Внимание', 'Выберите строку для удаления')
return
self.tableWidget.removeRow(row)
def check_tabs(self):
'''
Проверка заполнения поля lineEditType и проход по всем вкладкам для проверки заполнения
'''
for i in range(self.tabWidget.count()-1):
self.tabWidget.setCurrentIndex(i)
if self.tabWidget.currentWidget().lineEditType.text() == '' or self.tabWidget.currentWidget().lineEditType.text().isspace():
self.tabWidget.currentWidget().lineEditType.setStyleSheet(self.red_warning)
self.nb.setEnabled(False)
self.count = 0
self.button_activate()
return
else:
self.tabWidget.currentWidget().lineEditType.setStyleSheet('')
self.nb.setEnabled(True)
self.count = 1
self.button_activate()
def button_activate(self):
'''
Активация кнопки self.buttonAdd
'''
if self.count == 1:
self.buttonAdd.setEnabled(True)
else:
self.buttonAdd.setEnabled(False)
qss = """
QLabel {
font: 8pt "MS Shell Dlg 2";
}
QLineEdit {
font: 12pt "Calibri";
}
QSpinBox {
font: 12pt "Calibri";
}
"""
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
app.setStyleSheet(qss)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Ответы (1 шт):
Из выше сказанного я увидел вашу задачу так.
По сути я просто добавил таймер, который переодический проверет то, что вам надо. Интервал таймера (сейчас 1000 мс) установите по вашему разумению.
Я отметил для вас строки, в которые внес изменения.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *
class TabPage_SO(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.labelType = QLabel("№ типа", self)
self.lineEditType = QLineEdit(self)
self.lineEditType.setClearButtonEnabled(True)
self.labelYearOfIssue = QLabel("Год выпуска *", self)
self.spinBox = QSpinBox(self)
self.spinBox.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.spinBox.setAlignment(QtCore.Qt.AlignCenter)
self.spinBox.setMinimum(1917)
self.spinBox.setMaximum(2060)
self.spinBox.setProperty("value", 2020)
self.labelSerialNumber = QLabel("Заводской №", self)
self.lineEditSerialNumber = QLineEdit(self)
self.lineEditSerialNumber.setClearButtonEnabled(True)
self.labelSpecifications = QLabel("Характеристики", self)
self.lineEditSpecifications = QLineEdit(self)
self.lineEditSpecifications.setClearButtonEnabled(True)
grid = QGridLayout(self)
grid.addWidget(self.labelType, 0, 0)
grid.addWidget(self.labelYearOfIssue, 0, 1)
grid.addWidget(self.labelSerialNumber, 0, 2)
grid.addWidget(self.lineEditType, 1, 0)
grid.addWidget(self.spinBox, 1, 1)
grid.addWidget(self.lineEditSerialNumber, 1, 2)
grid.addWidget(self.labelSpecifications, 2, 0)
grid.addWidget(self.lineEditSpecifications, 3, 0, 1, 3)
grid.setRowStretch(4, 1)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.red_warning = "border-color: red; border-style: solid; border-width: 2px; font-weight: normal;"
self.tableWidget = QTableWidget(0, 4)
self.tableWidget.setHorizontalHeaderLabels(
["№ типа", "Год выпуска *", "Заводской №", "Характеристики"])
self.tableWidget.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
self.tableWidget.setSortingEnabled(True)
self.tableWidget.setAlternatingRowColors(True)
self.buttonAdd = QPushButton('Добавить из всех вкладок в таблицу')
self.buttonAdd.clicked.connect(self.addRowTable)
self.buttonDel = QPushButton('Удалить выбранную строку в таблице')
self.buttonDel.clicked.connect(self.delRowTable)
self.tabWidget = QTabWidget()
self.tabWidget.setTabsClosable(True)
count = self.tabWidget.count()
self.nb = QToolButton(text="Добавить", autoRaise=True)
self.nb.clicked.connect(self.new_tab)
self.tabWidget.insertTab(count, QWidget(), "")
self.tabWidget.tabBar().setTabButton(count, QTabBar.RightSide, self.nb)
self.new_tab()
self.tabWidget.tabCloseRequested.connect(self.closeTab_SO)
# - self.button_activate()
vbox = QGridLayout(self.centralWidget)
vbox.addWidget(self.tabWidget, 0, 0, 1, 2)
vbox.addWidget(self.tableWidget, 1, 0, 1, 2)
vbox.addWidget(self.buttonAdd, 2, 0)
vbox.addWidget(self.buttonDel, 2, 1)
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.timer = QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.check_tabs)
self.timer.start()
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def new_tab(self):
''' Создание нового ТАБа
'''
index = self.tabWidget.count() - 1
# self.tabWidget.insertTab(index, TabPage_SO(self), "Tab %d" % index)
tabPage_SO = TabPage_SO(self) # +
self.tabWidget.insertTab(index, tabPage_SO, "Tab %d" % index) # +
self.tabWidget.setCurrentIndex(index)
self.nb.setEnabled(False)
self.count = 0
self.button_activate(False) # +++ False
###
#- self.tabWidget.currentWidget().lineEditType.textChanged.connect(self.check_tabs)
#? self.tabWidget.currentWidget().lineEditSerialNumber.textChanged.connect(self.check_tabs)
#? self.tabWidget.currentWidget().lineEditSpecifications.textChanged.connect(self.check_tabs)
def closeTab_SO(self, currentIndex):
''' Удаление ТАБов
'''
self.tabWidget.removeTab(currentIndex)
self.tabWidget.setCurrentIndex(self.tabWidget.count() - 2)
#- self.check_tabs()
def addRowTable(self):
for i in range(self.tabWidget.count()-1):
self.tabWidget.setCurrentIndex(i)
editType = self.tabWidget.currentWidget().lineEditType.text()
spinYearOfIssue = str(self.tabWidget.currentWidget().spinBox.value())
editSerialNumber = self.tabWidget.currentWidget().lineEditSerialNumber.text()
editSpecifications = self.tabWidget.currentWidget().lineEditSpecifications.text()
if not editType:
msg = QMessageBox.information(self, 'Внимание', 'Заполните поле!')
return
self.tableWidget.setSortingEnabled(False)
rows = self.tableWidget.rowCount()
self.tableWidget.insertRow(rows)
self.tableWidget.setItem(rows, 0, QTableWidgetItem(editType))
self.tableWidget.setItem(rows, 1, QTableWidgetItem(spinYearOfIssue))
self.tableWidget.setItem(rows, 2, QTableWidgetItem(editSerialNumber))
self.tableWidget.setItem(rows, 3, QTableWidgetItem(editSpecifications))
self.tableWidget.setSortingEnabled(True)
def delRowTable(self):
row = self.tableWidget.currentRow()
if row == -1:
msg = QMessageBox.information(self, 'Внимание', 'Выберите строку для удаления')
return
self.tableWidget.removeRow(row)
# !!!
def check_tabs(self):
'''
Проверка заполнения поля lineEditType и проход по всем вкладкам для проверки заполнения
'''
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
flag = True
for i in range(self.tabWidget.count()-1):
line_edit_type = self.tabWidget.widget(i).lineEditType.text()
if not line_edit_type:
flag = False
self.nb.setEnabled(False)
break
if flag: self.nb.setEnabled(True)
else: self.nb.setEnabled(False)
self.button_activate(flag)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'''
for i in range(self.tabWidget.count()-1):
self.tabWidget.setCurrentIndex(i)
if self.tabWidget.currentWidget().lineEditType.text() == '' or self.tabWidget.currentWidget().lineEditType.text().isspace():
self.tabWidget.currentWidget().lineEditType.setStyleSheet(self.red_warning)
self.nb.setEnabled(False)
self.count = 0
self.button_activate()
return
else:
self.tabWidget.currentWidget().lineEditType.setStyleSheet('')
self.nb.setEnabled(True)
self.count = 1
self.button_activate()
'''
def button_activate(self, flag): # +++ flag
''' Активация кнопки self.buttonAdd
'''
if flag: # flag
self.buttonAdd.setEnabled(True) # показать
else:
self.buttonAdd.setEnabled(False)
qss = """
QLabel {
font: 8pt "MS Shell Dlg 2";
}
QLineEdit {
font: 12pt "Calibri";
}
QSpinBox {
font: 12pt "Calibri";
}
"""
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
app.setStyleSheet(qss)
window = MainWindow()
window.show()
sys.exit(app.exec_())
