Adding new Tabs in TabWidget appears as a seperate window when tab is selected

Heads up! You've already completed this tutorial.

cookeh_a9 | 2020-12-05 00:13:58 UTC | #1

I've been working on a PyQT5 which has vertical tabs. However I am experiencing some odd behavior.

I have a class which inherits QApplication. When I create tabs within this class, behavior is as expected. However I want to add tabs via an event listener call. So within the class that inherits QApplication, I register a function which adds a tab.

When I add a tab via event listener call, the tab window goes outside the MainWindow. However if I just add it manually without event listener calls, the behavior is as expected.

Good and bad behaviors: behv1|690x346

My guess is that I am using an old state of QApplication or MainWindow. I've spent a while trying to debug this and I do not see anyone who has ran into tabs appearing outside issue.

Code is below. I have commented in caps-lock the events. This is a rather sized application, so posting all the code isn't really feasible. I am hoping to get any ideas from anyone. Appreciate it.

Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

python
class MainWindow(QApplication):

def __init__(self, argv):
    super().__init__(argv)
    self.logic = None
    self.logic_thread = None
    self.basic_routine_cb = None
    self.advanced_routine_cb = None
    self.routine_frequency_drop_down = None
    self.current_action_text = None
    self.bot_running_time_value = None
    self.total_rallies_joined_value = None
    self.time_until_next_routine_value = None
    self.routine_executed_at_value = None
    self.bubble_time_checked_value = None
    self.total_marches_drop_down = None
    self.help_dialog = None
    self.login = None
    self.emulator_port_value = None
    self.start_stop_button = None
    self.total_preset_rotation_drop_down = None
    self.consume_stamina_cb = None
    self.w = None
    self.win = MainApplicationWindow()
    self.initialize_user_interface()

def initialize_user_interface(self):
    QtWidgets.QApplication.setStyle(ProxyStyle())
    self.setStyle('Fusion')
    self.win.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
    self.win.setGeometry(0, 0, 650, 525)
    self.win.setFixedSize(650, 525)
    self.win.setWindowTitle("Some Title")

    self.w = TabWidget()

    self.login = LoginForm(self)

    tab1 = self.create_new_bot_tab()

    self.w.addTab(tab1, QtGui.QIcon("./forum/images/RedSquare.png"), "1")
    self.w.addTab(QWidget(), QtGui.QIcon("zoom.png"), "+")

    self.w.currentChanged.connect(self.add_new_tab) #changed!
    event_list.append(self.insert_new_bot_tab) # EVENT LISTENER ADDED HERE

    self.win.setCentralWidget(self.w)

    self.win.show()
    self.login.show()

    self.w.show()

    sys.exit(self.exec_())

@pyqtSlot()
def add_new_tab(self):
    try:
        if Client.socket_connection.connected is False:
            self.w.setCurrentIndex(self.w.currentIndex() - 1)
            self.login.show()
            return

        if self.w.tabText(self.w.currentIndex()) != '+':
            return

        Client.attempt_add_tab() # THIS CALLS A FUNCTION WHICH EVENTUALLY CALLS THE REGISTERED EVENT

        # IF I CALL self.insert_new_bot_tab() HERE RATHER THAN USING EVENT LISTENER TO CALL IT, BEHAVIOUR IS AS EXPECTED
    except Exception as e:
        print(str(e))


# THE REGISTERED EVENT HERE
def insert_new_bot_tab(self):
    tab2 = self.create_new_bot_tab()

    self.w.insertTab(self.w.currentIndex(), tab2, QtGui.QIcon("./forum/images/RedSquare.png"),
                     str(self.w.currentIndex() + 1))
    self.w.blockSignals(True)
    self.w.setCurrentIndex(self.w.currentIndex() - 1)
    self.w.blockSignals(False)

def create_new_bot_tab(self):
    wdg = CustomQWidget(self.win, self)
    layout = QtWidgets.QGridLayout(wdg)
    index = 0
    if self.w.currentIndex() > -1:
        index = self.w.currentIndex()
    tabs = QTabWidget()
    tab1 = StartMenuTab(self.login, self.w, index)
    tab2 = ProfileTab()
    tabs.resize(300, 200)

    # Add tabs
    tabs.addTab(tab1, "Start")
    tabs.addTab(tab2, "Profile")
    layout.addWidget(tabs)

    return wdg

CustomQWidget class:

python
class CustomQWidget(QWidget):

def __init__(self, win, win_instance):
    super().__init__(win)
    self.win_instance = win_instance

cookeh_a9 | 2020-12-05 06:40:39 UTC | #2

I'm using a hackish work around for now. If anyone has any idea I would really appreciate feedback!


martin | 2020-12-08 12:08:16 UTC | #3

Hi @cookeh_a9 welcome to the forum!

First up, it's a bit strange to inherit from QApplication, normally, you'd inherit from QMainWindow for your main window. You then create an instance of QApplication directly, create your window, and then start up the app event loop, e.g.

python
app = QApplication(sys.argv)
w = MainWindow()
app.exec_()

But I don't think that's the issue here. When you get windows popping out of the main window, it's usually an due to something going wrong with window parents -- any widget without a parent in Qt is a floating window. So if you create a QTabWidget but don't set a parent (either explicitly, or by adding it to a layout) it will be floating. The same happens if you remove the parent from a widget by setting parent to None.

Another way you can get caught out is by recreating a widget, e.g. if you have a tab widget as self.w and then recreate the self.w = QTabWidget again, the reference to the first tab widget is lost + it is destroyed. All widgets that had it as a parent (i.e. the tabs) are now parent-less and will free-float.

I'd start by checking the parent parameters you're passing to the various widgets when being created. Then double-check you're not recreating any widgets accidentally/unnecessarily.


The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Adding new Tabs in TabWidget appears as a seperate window when tab is selected was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.