Caeta
Caeta

Reputation: 461

closeTab function it closes more than one tab in PyQt5

I built a functionality for add new tabs in which only the new tabs has a close button, I mean the "Tab 1" and the "+" don't has the close button. So this it's the code:

tabs.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(628, 504)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
        self.tabWidget.setGeometry(QtCore.QRect(36, 34, 541, 396))
        self.tabWidget.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
        self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded)
        self.tabWidget.setTabsClosable(False)
        self.tabWidget.setObjectName("tabWidget")
        self.tab1 = QtWidgets.QWidget()
        self.tab1.setObjectName("tab1")
        self.tabWidget.addTab(self.tab1, "")
        self.tab2 = QtWidgets.QWidget()
        self.tab2.setObjectName("tab2")
        self.tabWidget.addTab(self.tab2, "")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 628, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab1), _translate("MainWindow", "Tab 1"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab2), _translate("MainWindow", "+"))

main.py

from tabs import Ui_MainWindow
from PyQt5 import QtCore, QtGui, QtWidgets

class Tab(QtWidgets.QMainWindow, Ui_MainWindow):
    tabs_list = []

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.tabWidget.tabBarClicked.connect(self.newTab)
        self.tabs_list.append(self.tab1)
        self.tabs_list.append(self.tab2)

    def newTab(self, event):
        if event == len(self.tabs_list) - 1:
            tab = QtWidgets.QWidget()
            self.tabWidget.addTab(tab, "+")
            self.tabs_list.append(tab)
            self.tabWidget.setTabText(len(self.tabs_list) - 2, "Tab " + str(len(self.tabs_list) - 1))
            self.setClosableTabs()

    def setClosableTabs(self):
        self.tabWidget.setTabsClosable(False)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.tabBar().setTabButton(0, QtWidgets.QTabBar.RightSide, None)
        self.tabWidget.tabBar().setTabButton(len(self.tabs_list) - 1, QtWidgets.QTabBar.RightSide, None)
        self.tabWidget.tabCloseRequested.connect(self.closeTab)
    
    def closeTab(self, event):
        print("close tab: ", event)
        self.tabWidget.removeTab(event)

enter image description here

enter image description here

When I tried to add the close functionality I ran into with the event that contains the index of the tab that I want to close its triggered as many times as number of tabs there are, so then its close more than only one tab. What I'm doing wrong?

Upvotes: 2

Views: 534

Answers (1)

musicamante
musicamante

Reputation: 48335

You are connecting the tabCloseRequested signal everytime you are setting the tabs closable. Everytime you connect a signal to a slot, the slot will be called when the signal is emitted: if you connect it twice, the slot will be called twice.

In your case, if you create two tabs, the closed signal will be connected twice, so if you then try to close the first tab, closeTab(0) will be called twice, resulting in closing both the first and the second (which will become the first after the previous one has been removed).

Remove the connection from setClosableTabs and put it in the __init__:

class Tab(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        # ...
        self.tabWidget.tabCloseRequested.connect(self.closeTab)

Note that your implementation has other issues too, with the most important being using an internal list for the tab widgets.

Qt already provides what you need: count() returns the amount of tabs, and widget(index) returns the individual widgets based on the index, in case you're going to need it.
Using any other method is usually discouraged, as it might lead to errors or unexpected behavior if they are not properly implemented, and that's exactly your case:

  1. you are not removing the tab from the list when it's closed (so the newTab() won't work, as it will return the wrong length of the list);
  2. you are using a class attribute for a list that should only contain objects that are members of the instance;

Imagine what would happen if you create two instances of the same Tab class: if you add a tab to the first window, then the second window will see that too in its tabs_list, since it's a class attribute that is shared among all instances.

This is a better and actually simpler implementation of what you're trying to do (note that you should remove the second "+" tab from your ui, as it's added programmatically):

class Tab(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.tabWidget.tabBarClicked.connect(self.newTab)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.tabCloseRequested.connect(self.closeTab)
        # add the "+" tab to the tabbar, not the tabwidget
        lastTab = self.tabWidget.tabBar().addTab('+')
        self.tabWidget.tabBar().setTabButton(lastTab, QtWidgets.QTabBar.RightSide, None)

    def newTab(self, event):
        if event == self.tabWidget.count() - 1:
            tab = QtWidgets.QWidget()
            tabName = "Tab {}".format(self.tabWidget.count())
            self.tabWidget.insertTab(self.tabWidget.count() - 1, tab, tabName)

Upvotes: 3

Related Questions