使用 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
这就是答案。 通过首先收集列表中的所有小部件,您可以避免在迭代布局时修改布局的结构。 这确保了每个小部件都得到正确处理,而不存在丢失或过早损坏的风险。
所以最终的测试代码是:
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...;-)