使用 PyQt5 分离选项卡 - 将所有小部件保留到分离的选项卡中

问题描述 投票:0回答:1

使用 PyQt 应用程序,我想提供可分离的选项卡,用户可以通过双击分离选项卡并通过关闭分离的选项卡将其重新附加到主窗口。

我已将问题最小化到以下脚本,该脚本几乎可以工作,除了当我在布局中有多个 QWidget 时,只有 1 on 2 被添加到分离布局中。这很令人费解。

这是代码:

import sys
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QTabWidget, QWidget, QVBoxLayout,
    QLabel, QStatusBar
)

# Variable to store detached windows
detached_windows = {}

def create_combined_tab():
    widget = QWidget()
    layout = QVBoxLayout()

    # Create two QLabel for demonstration
    label1 = QLabel("This is QLabel 1.")
    label1.setObjectName('label1')
    label2 = QLabel("This is QLabel 2.")
    label2.setObjectName('label2')
    label3 = QLabel("This is QLabel 3.")
    label3.setObjectName('label3')
    label4 = QLabel("This is QLabel 4.")
    label4.setObjectName('label4')
    label5 = QLabel("This is QLabel 5.")
    label5.setObjectName('label5')
    
    layout.addWidget(label1)
    layout.addWidget(label2)
    layout.addWidget(label3)
    layout.addWidget(label4)
    layout.addWidget(label5)

    widget.setLayout(layout)
    return widget

def detach_tab(tab_widget, index):
    """Detaches the tab and displays it in a new window."""
    tab_content = tab_widget.widget(index)
    tab_name = tab_widget.tabText(index)

    # Remove the tab from the QTabWidget
    tab_widget.removeTab(index)

    # Create a new window for the detached tab
    detached_window = QMainWindow()
    detached_window.setWindowTitle(tab_name)
    detached_window.setGeometry(200, 200, 800, 600)

    detached_content = QWidget()
    detached_layout = QVBoxLayout()
    detached_content.setLayout(detached_layout)

    # Move the content of the tab to the new window
    print(f"Before detaching: {tab_content.layout().count()} widgets")  # Debugging output

    for i in range(tab_content.layout().count()):
        item = tab_content.layout().itemAt(i)
        print(i, item)
        if item is not None and item.widget() is not None:
            widget = item.widget()
            detached_layout.addWidget(widget)  # Add the widget to the new layout

    print(f"After detaching: {detached_layout.count()} widgets in new layout")  # Debugging output

    detached_window.setCentralWidget(detached_content)
    detached_window.setStatusBar(QStatusBar())
    detached_window.statusBar().showMessage(f"{tab_name} - Ready")

    # Handle the close event of the detached window
    def on_close_event(event):
        tab_widget.addTab(detached_content, tab_name)  # Reattach the content
        tab_widget.setCurrentWidget(detached_content)  # Select the reattached tab
        event.accept()
        del detached_windows[tab_name]

    detached_window.closeEvent = on_close_event

    detached_window.show()  # Show the new window
    detached_windows[tab_name] = detached_window  # Keep track of the detached window

# Set up the application
app = QApplication(sys.argv)
main_window = QMainWindow()
main_window.setGeometry(200, 200, 800, 600)

tabs = QTabWidget(main_window)
tabs.setMovable(True)

# Create and add the combined tab
combined_tab = create_combined_tab()
tabs.addTab(combined_tab, "Tab - QLabels")

# Connect the double-click event to detach the tab
tabs.tabBarDoubleClicked.connect(lambda index: detach_tab(tabs, index))

main_window.setCentralWidget(tabs)
main_window.setStatusBar(QStatusBar())
main_window.statusBar().showMessage("Application ready")

main_window.show()  # Show the main window
sys.exit(app.exec_())  # Start the application event loop

python pyqt5
1个回答
0
投票

这就是答案。 通过首先收集列表中的所有小部件,您可以避免在迭代布局时修改布局的结构。 这确保了每个小部件都得到正确处理,而不存在丢失或过早损坏的风险。

所以最终的测试代码是:

import sys
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QTabWidget, QWidget, QVBoxLayout,
    QLabel, QStatusBar
)

# Variable to store detached windows
detached_windows = {}

def create_combined_tab():
    """Creates a tab with multiple QLabel."""
    widget = QWidget()
    layout = QVBoxLayout()

    # Create multiple QLabels for demonstration
    label1 = QLabel("This is QLabel 1.")
    label2 = QLabel("This is QLabel 2.")
    label3 = QLabel("This is QLabel 3.")
    label4 = QLabel("This is QLabel 4.")
    label5 = QLabel("This is QLabel 5.")
    
    layout.addWidget(label1)
    layout.addWidget(label2)
    layout.addWidget(label3)
    layout.addWidget(label4)
    layout.addWidget(label5)

    widget.setLayout(layout)
    return widget

def detach_tab(tab_widget, index):
    """Detaches the tab and displays it in a new window."""
    tab_content = tab_widget.widget(index)
    tab_name = tab_widget.tabText(index)

    # Remove the tab from the QTabWidget
    tab_widget.removeTab(index)

    # Create a new window for the detached tab
    detached_window = QMainWindow()
    detached_window.setWindowTitle(tab_name)
    detached_window.setGeometry(200, 200, 800, 600)

    detached_content = QWidget()
    detached_layout = QVBoxLayout()
    detached_content.setLayout(detached_layout)

    # Collect widgets to detach
    widgets_to_detach = []
    
    print(f"Before detaching: {tab_content.layout().count()} widgets")  # Debugging output

    for i in range(tab_content.layout().count()):
        item = tab_content.layout().itemAt(i)
        print(f"Checking item at index {i}: {item}")
        if item is not None and item.widget() is not None:
            widget = item.widget()
            widgets_to_detach.append(widget)  # Collect widget instead of adding directly

    # Now add collected widgets to the new layout
    for widget in widgets_to_detach:
        detached_layout.addWidget(widget)  # Add the widget to the new layout

    print(f"After detaching: {detached_layout.count()} widgets in new layout")  # Debugging output

    detached_window.setCentralWidget(detached_content)
    detached_window.setStatusBar(QStatusBar())
    detached_window.statusBar().showMessage(f"{tab_name} - Ready")

    # Handle the close event of the detached window
    def on_close_event(event):
        tab_widget.addTab(detached_content, tab_name)  # Reattach the content
        tab_widget.setCurrentWidget(detached_content)  # Select the reattached tab
        event.accept()
        del detached_windows[tab_name]

    detached_window.closeEvent = on_close_event

    detached_window.show()  # Show the new window
    detached_windows[tab_name] = detached_window  # Keep track of the detached window

# Set up the application
app = QApplication(sys.argv)
main_window = QMainWindow()
main_window.setGeometry(200, 200, 800, 600)

tabs = QTabWidget(main_window)
tabs.setMovable(True)

# Create and add the combined tab
combined_tab = create_combined_tab()
tabs.addTab(combined_tab, "Tab - QLabels")

# Connect the double-click event to detach the tab
tabs.tabBarDoubleClicked.connect(lambda index: detach_tab(tabs, index))

main_window.setCentralWidget(tabs)
main_window.setStatusBar(QStatusBar())
main_window.statusBar().showMessage("Application ready")

main_window.show()  # Show the main window
sys.exit(app.exec_())  # Start the application event loop

希望这会有所帮助。我在这个问题上花了好几个小时。即使使用 ChatGPT...;-)

© www.soinside.com 2019 - 2024. All rights reserved.