Не работают сигналы, при исопльзовании QTabWidget

Странно работает QTabWidget, если не использовать его в классе MyTab, то будут пустые вкладки, если не использовать его в MainWindow.createCommonTab() то будет всё отрисовано но ничего не будет работать. Но если я использую QTabWidget и там и там, то все работает, но отрисовывается ещё по одной пустой вкладке с повторяющися названием.

Где найти золотую серидину?

main.py

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import random
import string
import traceback


def log_uncaught_exceptions(ex_cls, ex, tb):
    text = '{}: {}:\n'.format(ex_cls.__name__, ex)
    text += ''.join(traceback.format_tb(tb))

    print(text)
    QMessageBox.critical(None, 'Error', text)
    sys.exit(1)


sys.excepthook = log_uncaught_exceptions


def random_char(y):
    return ''.join(random.choice(string.ascii_letters) for x in range(y))


class MyTab(QWidget):
    about_time_update = pyqtSignal(str)

    def __init__(self, name, init, rangeDevic, deviceName, tabWidget):
        super().__init__()
        # Создание Вкладки
        self.tab = QWidget()
# Если избавиться от таб виджета то будут пустые вкладки             ---------------1
        self.tabWidget = tabWidget           
        self.tabWidget.addTab(self.tab, name)       
    # Всякое повторяющееся из вкладки во вкладку
        self.validator = QIntValidator(0, 3600000, self.tab)
        self.currentDisplayLable = QLabel('Текущее показание:', self.tab)
        self.init = init
        self.currentDisplayLineE = QLineEdit(''+self.init, self.tab)
        self.currentDisplayLineE.setReadOnly(True)
        self.timeToUpdateLable = QLabel('Период обновлений, мс:', self.tab)
        self.timeToUpdateLineE = QLineEdit('100', self.tab)
        self.timeToUpdateLineE.setValidator(self.validator)
        self.rangeDeviceLable = QLabel('Диапазон:', self.tab)
        self.rangeDeviceComboB = QComboBox(self.tab)
        self.rangeDevice = rangeDevic
        if type(self.rangeDevice) == str:
            self.rangeDeviceComboB.addItem(self.rangeDevice)
        else:
            self.rangeDeviceComboB.addItems(self.rangeDevice)
        self.lcdNumber = QLCDNumber(self.tab)

        self.widgetsData = QWidget()
        self.startPlotBtn = QPushButton('Старт', self.widgetsData)
        self.pausePlotBtn = QPushButton('Пауза', self.widgetsData)
        self.graphModeLabel = QLabel('Режим отображения графика', self.widgetsData)
        self.graphModeComboB = QComboBox(self.widgetsData)
        self.graphModeComboB.addItems(('Линия', 'Линия с точками', 'Точка'))
        self.linkModeLabel = QLabel('Режим связки', self.widgetsData)
        self.linkModeComboB = QComboBox(self.widgetsData)
        self.linkModeComboB.addItem('-')
        self.deviceName = deviceName
        self.linkModeComboB.addItem(self.deviceName)
        # Нужно для того чтобы плот не ел кнопки старта и паузы
        self.notEatPlot = QFrame(self.widgetsData)

    # Расположение
        self.commonTabGrib = QGridLayout(self.tab)

        self.commonTabGrib.addWidget(self.currentDisplayLable, 0, 0)
        self.commonTabGrib.addWidget(self.currentDisplayLineE, 0, 1)
        self.commonTabGrib.addWidget(self.timeToUpdateLable, 1, 0)
        self.commonTabGrib.addWidget(self.timeToUpdateLineE, 1, 1)
        self.commonTabGrib.addWidget(self.rangeDeviceLable, 2, 0)
        self.commonTabGrib.addWidget(self.rangeDeviceComboB, 2, 1)
        self.commonTabGrib.addWidget(self.lcdNumber, 3, 0, 2, -1)
        self.commonTabGrib.addWidget(self.widgetsData, 5, 0, -1, -1)

        self.plotGrid = QGridLayout(self.widgetsData)
        self.plotGrid.addWidget(self.startPlotBtn, 2, 0)
        self.plotGrid.addWidget(self.pausePlotBtn, 2, 1)
        self.plotGrid.addWidget(self.graphModeLabel, 3, 0)
        self.plotGrid.addWidget(self.graphModeComboB, 3, 1)
        self.plotGrid.addWidget(self.linkModeLabel, 4, 0)
        self.plotGrid.addWidget(self.linkModeComboB, 4, 1)

        self.startPlotBtn.pressed.connect(self.errrors)
        self.timeToUpdateLineE.textChanged.connect(self.timeUpdate)

    def timeUpdate(self):
        self.timeCurrentUpdate = self.timeToUpdateLineE.text()
        print('Text:', self.timeCurrentUpdate)
        self.about_time_update.emit(self.timeCurrentUpdate)

    def errrors(self):
        print(2)


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Intler")
        self.resize(600, 400)
        self.initUI()

    def initUI(self):
        #   Создание виджета для Вкладок
        self.tabWidget = QTabWidget()
        self.tabWidget.setDocumentMode(True)
#   Вкладка 1
        self.mainTab = QWidget()
        self.tabWidget.addTab(self.mainTab, 'Подключение:')

#   Кнопки
        self.connectPortBtn = QPushButton('Подключиться', self.mainTab)
#   Error Лайбл
        self.errorsLabel = QLabel('Ошибок не обнаружено', self.mainTab)
        self.errorsLabel.setWordWrap(True)
#   Расположение виджетов во вкладке 'Подключение:'
        self.mainTabVert = QVBoxLayout()
        self.tab1Grib = QGridLayout(self.mainTab)
        self.mainTabVert.addWidget(self.tabWidget)
        self.mainTabVert.addWidget(self.errorsLabel)
        self.tab1Grib.addWidget(self.connectPortBtn, 2, 3)
        self.setLayout(self.mainTabVert)
#   Подключение виджетов(кнопок и тд и тп)
        self.connectPortBtn.pressed.connect(self.connectToLab)

    def connectToLab(self):
        # Очистка вкладок
        while self.tabWidget.count() > 1:
            self.tabWidget.removeTab(1)
        i = 0
        while i != 3:
            self.createCommonTab(random_char(5), random_char(
                4), ('asd', 'fsa', 'gea'), random_char(2), self.tabWidget)
            i += 1

    def createCommonTab(self, name, init, rangeDevic, deviceName, tabWidget):
        tab = MyTab(name, init, rangeDevic, deviceName, tabWidget)
# Если избавиться тут то будет отрисовано всё, но ничего не будет работать ---------------2
        self.tabWidget.addTab(tab, name)

        tab.about_time_update.connect(self.on_about_tab_time_update)
        # self.tabWidget.addTab(tab, name)
        # tab.startPlotBtn.pressed.connect(self.errrors)

    def on_about_tab_time_update(self, time_update: str):
        tab: MyTab = self.sender()
        print(tab, time_update)

    def timeUpdate(self):
        self.timeCurrentUpdate = self.timeToUpdateLineE.text()
        print(self.timeCurrentUpdate)


# Запуск
if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec())

Ответы (1 шт):

Автор решения: gil9red

Проблемы и замечания:

  • У вас в конструкторе MyTab было создание вкладки от QWidget и добавление ее с таким же имененем
  • Не думаю, что нужно передавать виджет tabWidget в вкладки
  • Не думаю, что нужно передавать виджет tabWidget в методы (типа createCommonTab) самого MainWindow - он и так доступен
  • Не нужно виджетам указывать родителя, т.к. при использовании компоновщиков (Q*Layout) родитель и так будет указан. Поэтому убрал self.tab из конструкторов виджетов (хотя в вашем случае там был бы self, но и его можно не указывать)

Теперь работает:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import random
import string
import traceback


def log_uncaught_exceptions(ex_cls, ex, tb):
    text = '{}: {}:\n'.format(ex_cls.__name__, ex)
    text += ''.join(traceback.format_tb(tb))

    print(text)
    QMessageBox.critical(None, 'Error', text)
    sys.exit(1)


sys.excepthook = log_uncaught_exceptions


def random_char(y):
    return ''.join(random.choice(string.ascii_letters) for x in range(y))


class MyTab(QWidget):
    about_time_update = pyqtSignal(str)

    def __init__(self, name, init, rangeDevic, deviceName):
        super().__init__()

        self.setWindowTitle(name)

        # Всякое повторяющееся из вкладки во вкладку
        self.validator = QIntValidator(0, 3600000)
        self.currentDisplayLable = QLabel('Текущее показание:')
        self.init = init
        self.currentDisplayLineE = QLineEdit(''+self.init)
        self.currentDisplayLineE.setReadOnly(True)
        self.timeToUpdateLable = QLabel('Период обновлений, мс:')
        self.timeToUpdateLineE = QLineEdit('100')
        self.timeToUpdateLineE.setValidator(self.validator)
        self.rangeDeviceLable = QLabel('Диапазон:')
        self.rangeDeviceComboB = QComboBox()
        self.rangeDevice = rangeDevic
        if type(self.rangeDevice) == str:
            self.rangeDeviceComboB.addItem(self.rangeDevice)
        else:
            self.rangeDeviceComboB.addItems(self.rangeDevice)
        self.lcdNumber = QLCDNumber()

        self.widgetsData = QWidget()
        self.startPlotBtn = QPushButton('Старт', self.widgetsData)
        self.pausePlotBtn = QPushButton('Пауза', self.widgetsData)
        self.graphModeLabel = QLabel('Режим отображения графика', self.widgetsData)
        self.graphModeComboB = QComboBox(self.widgetsData)
        self.graphModeComboB.addItems(('Линия', 'Линия с точками', 'Точка'))
        self.linkModeLabel = QLabel('Режим связки', self.widgetsData)
        self.linkModeComboB = QComboBox(self.widgetsData)
        self.linkModeComboB.addItem('-')
        self.deviceName = deviceName
        self.linkModeComboB.addItem(self.deviceName)
        # Нужно для того чтобы плот не ел кнопки старта и паузы
        self.notEatPlot = QFrame(self.widgetsData)

    # Расположение
        self.commonTabGrib = QGridLayout(self)

        self.commonTabGrib.addWidget(self.currentDisplayLable, 0, 0)
        self.commonTabGrib.addWidget(self.currentDisplayLineE, 0, 1)
        self.commonTabGrib.addWidget(self.timeToUpdateLable, 1, 0)
        self.commonTabGrib.addWidget(self.timeToUpdateLineE, 1, 1)
        self.commonTabGrib.addWidget(self.rangeDeviceLable, 2, 0)
        self.commonTabGrib.addWidget(self.rangeDeviceComboB, 2, 1)
        self.commonTabGrib.addWidget(self.lcdNumber, 3, 0, 2, -1)
        self.commonTabGrib.addWidget(self.widgetsData, 5, 0, -1, -1)

        self.plotGrid = QGridLayout(self.widgetsData)
        self.plotGrid.addWidget(self.startPlotBtn, 2, 0)
        self.plotGrid.addWidget(self.pausePlotBtn, 2, 1)
        self.plotGrid.addWidget(self.graphModeLabel, 3, 0)
        self.plotGrid.addWidget(self.graphModeComboB, 3, 1)
        self.plotGrid.addWidget(self.linkModeLabel, 4, 0)
        self.plotGrid.addWidget(self.linkModeComboB, 4, 1)

        self.startPlotBtn.pressed.connect(self.errrors)
        self.timeToUpdateLineE.textChanged.connect(self.timeUpdate)

    def timeUpdate(self):
        self.timeCurrentUpdate = self.timeToUpdateLineE.text()
        print('Text:', self.timeCurrentUpdate)
        self.about_time_update.emit(self.timeCurrentUpdate)

    def errrors(self):
        print(2)


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Intler")
        self.resize(600, 400)
        self.initUI()

    def initUI(self):
        #   Создание виджета для Вкладок
        self.tabWidget = QTabWidget()
        self.tabWidget.setDocumentMode(True)
#   Вкладка 1
        self.mainTab = QWidget()
        self.tabWidget.addTab(self.mainTab, 'Подключение:')

#   Кнопки
        self.connectPortBtn = QPushButton('Подключиться', self.mainTab)
#   Error Лайбл
        self.errorsLabel = QLabel('Ошибок не обнаружено', self.mainTab)
        self.errorsLabel.setWordWrap(True)
#   Расположение виджетов во вкладке 'Подключение:'
        self.mainTabVert = QVBoxLayout()
        self.tab1Grib = QGridLayout(self.mainTab)
        self.mainTabVert.addWidget(self.tabWidget)
        self.mainTabVert.addWidget(self.errorsLabel)
        self.tab1Grib.addWidget(self.connectPortBtn, 2, 3)
        self.setLayout(self.mainTabVert)
#   Подключение виджетов(кнопок и тд и тп)
        self.connectPortBtn.clicked.connect(self.connectToLab)

    def connectToLab(self):
        # Очистка вкладок
        while self.tabWidget.count() > 1:
            self.tabWidget.removeTab(1)
        i = 0
        while i != 3:
            self.createCommonTab(random_char(5), random_char(4), ('asd', 'fsa', 'gea'), random_char(2))
            i += 1

    def createCommonTab(self, name, init, rangeDevic, deviceName):
        tab = MyTab(name, init, rangeDevic, deviceName)
# Если избавиться тут то будет отрисовано всё, но ничего не будет работать ---------------2
        self.tabWidget.addTab(tab, name)

        tab.about_time_update.connect(self.on_about_tab_time_update)
        # self.tabWidget.addTab(tab, name)
        # tab.startPlotBtn.pressed.connect(self.errrors)

    def on_about_tab_time_update(self, time_update: str):
        tab: MyTab = self.sender()
        print(tab, time_update)

    def timeUpdate(self):
        self.timeCurrentUpdate = self.timeToUpdateLineE.text()
        print(self.timeCurrentUpdate)


# Запуск
if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec())
→ Ссылка