Qt中的主界面,通过双击将当前界面弹出,点击还原按钮弹出界面嵌回住界面中。程序中最多弹出两个界面。
主界面程序,通过创建四个分界面
#ifndef WIDGET_H
#define WIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include <QTabBar>
#include <QDesktopWidget>
#include <QLabel>
#include <QMessageBox>
#include "form1.h"
#include "form2.h"
#include "form3.h"
#include "form4.h"
#include "detachwindow.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
void closeEvent(QCloseEvent *event) override;
private slots:
void on_pushButton_1_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
private:
Ui::Widget *ui;
Form1 *m_form1;
Form2 *m_form2;
Form3 *m_form3;
Form4 *m_form4;
void detachForm(QWidget *form);
void restoreForm(QWidget *form);
QHash<QWidget*, DetachWindow*> m_detachWins; // Form → 独立窗口
QHash<QWidget*, QWidget*> m_placeholders; // Form → 占位页
struct PageInfo { int index = -1; QString title; };
QHash<QWidget*, PageInfo> m_pageInfos; // Form → 原 index + 标题
int m_maxDetach = 2; // 最多同时弹出数量
int m_offsetStep = 40; // 窗口错开步长
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
m_form1 = new Form1(); // 指定父对象
m_form2 = new Form2();
m_form3 = new Form3();
m_form4 = new Form4();
ui->tabWidget->insertTab(0, m_form1, tr("界面-1"));
ui->tabWidget->insertTab(1, m_form2, tr("界面-2"));
ui->tabWidget->insertTab(2, m_form3, tr("界面-3"));
ui->tabWidget->insertTab(3, m_form4, tr("界面-4"));
ui->tabWidget->tabBar()->hide();
// 给 tabWidget 安装事件过滤器,捕获双击
ui->tabWidget->installEventFilter(this);
// 给每个页面也装过滤器,这样点页面内部也能双击
m_form1->installEventFilter(this);
m_form2->installEventFilter(this);
m_form3->installEventFilter(this);
m_form4->installEventFilter(this);
}
Widget::~Widget()
{
delete ui;
}
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
bool isTarget = (watched == ui->tabWidget
|| watched == m_form1
|| watched == m_form2
|| watched == m_form3
|| watched == m_form4);
if (isTarget && event->type() == QEvent::MouseButtonDblClick)
{
QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (me->button() == Qt::LeftButton) {
QWidget *cur = ui->tabWidget->currentWidget();
if (!cur) return false;
// 当前页是占位页 → 忽略
if (m_placeholders.values().contains(cur))
return true;
// 当前页已弹出 → 忽略
if (m_detachWins.contains(cur))
return true;
detachForm(cur);
return true;
}
}
return QWidget::eventFilter(watched, event);
}
void Widget::closeEvent(QCloseEvent *event)
{
// 遍历所有独立窗口,强制关闭并清理
const QList<QWidget*> forms = m_detachWins.keys();
for (QWidget *form : forms) {
DetachWindow *win = m_detachWins.value(form);
if (!win) continue;
// 关键:断开还原信号,避免关闭时又触发 restoreForm
disconnect(win, nullptr, this, nullptr);
win->hide(); // hide 不触发 closeEvent,安全
win->deleteLater(); // 交给事件循环销毁
}
m_detachWins.clear();
m_placeholders.clear();
m_pageInfos.clear();
event->accept(); // 继续关闭主窗口
}
void Widget::on_pushButton_1_clicked()
{
ui->tabWidget->setCurrentIndex(0);
}
void Widget::on_pushButton_2_clicked()
{
ui->tabWidget->setCurrentIndex(1);
}
void Widget::on_pushButton_3_clicked()
{
ui->tabWidget->setCurrentIndex(2);
}
void Widget::on_pushButton_4_clicked()
{
ui->tabWidget->setCurrentIndex(3);
}
void Widget::detachForm(QWidget *form)
{
if (!form) return;
if (m_detachWins.contains(form)) return;
// 限制最多弹出数量
if (m_detachWins.size() >= m_maxDetach) {
QMessageBox::information(this,
tr("提示"),
tr("最多同时弹出 %1 个窗口").arg(m_maxDetach));
return;
}
int idx = ui->tabWidget->indexOf(form);
if (idx < 0) return;
// 1. 记录信息
PageInfo info;
info.index = idx;
info.title = ui->tabWidget->tabText(idx);
m_pageInfos.insert(form, info);
// 2. 创建占位页
QWidget *placeholder = new QWidget();
QVBoxLayout *phLayout = new QVBoxLayout(placeholder);
QLabel *label = new QLabel(tr("该界面已在独立窗口中显示"));
QPushButton *pushButton = new QPushButton(tr("还原"));
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("color: #888; font-size: 16px;");
phLayout->addWidget(label);
phLayout->addWidget(pushButton);
m_placeholders.insert(form, placeholder);
connect(pushButton, &QPushButton::clicked,
this, [this, form]() { restoreForm(form); });
// 3. 摘掉真实 Form,插入占位页
ui->tabWidget->removeTab(idx);
ui->tabWidget->insertTab(idx, placeholder, info.title);
ui->tabWidget->setCurrentIndex(idx);
// 4. 创建独立窗口
DetachWindow *win = new DetachWindow(nullptr, info.title);
m_detachWins.insert(form, win);
form->setParent(nullptr);
win->setContent(form);
// 位置错开:以主窗口为基准,按已弹出数量偏移
int n = m_detachWins.size() - 1; // 第几个(0 开始)
QPoint base = this->pos();
QSize sz = win->size();
int offsetX = n * m_offsetStep;
int offsetY = n * m_offsetStep;
// 防止跑出屏幕
QRect screen = QApplication::desktop()->availableGeometry(this);
int x = base.x() + 60 + offsetX;
int y = base.y() + 60 + offsetY;
if (x + sz.width() > screen.right()) x = screen.right() - sz.width() - 10;
if (y + sz.height() > screen.bottom()) y = screen.bottom() - sz.height() - 10;
win->move(x, y);
connect(win, &DetachWindow::restoreRequested,
this, [this, form]() { restoreForm(form); });
win->show();
win->raise();
win->activateWindow();
}
void Widget::restoreForm(QWidget *form)
{
if (!form) return;
if (!m_detachWins.contains(form)) return;
DetachWindow *win = m_detachWins.take(form);
QWidget *placeholder = m_placeholders.take(form);
PageInfo info = m_pageInfos.take(form);
// 1. 取回 Form
QWidget *content = win->takeContent();
if (!content) content = form;
// 2. 移除占位页
if (placeholder) {
int pIdx = ui->tabWidget->indexOf(placeholder);
if (pIdx >= 0)
ui->tabWidget->removeTab(pIdx);
placeholder->deleteLater();
}
// 3. 插回真实 Form
content->setParent(ui->tabWidget);
ui->tabWidget->insertTab(info.index, content, info.title);
ui->tabWidget->setCurrentIndex(info.index);
// 4. 清理独立窗口
win->hide();
win->deleteLater();
}
分界面处理主要函数
#ifndef DETACHWINDOW_H
#define DETACHWINDOW_H
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QCloseEvent>
class DetachWindow : public QWidget
{
Q_OBJECT
public:
explicit DetachWindow(QWidget *parent = nullptr, const QString &title = QString());
// 设置要显示的界面
void setContent(QWidget *content);
// 取出当前内容(还原时用)
QWidget *takeContent();
signals:
void restoreRequested(); // 点击"还原"时发出
protected:
void closeEvent(QCloseEvent *event) override; // 关闭自动还原
private:
QVBoxLayout *m_layout;
QPushButton *m_btnRestore;
QWidget *m_content = nullptr;
QString m_title ;
};
#endif // DETACHWINDOW_H
#include "detachwindow.h"
DetachWindow::DetachWindow(QWidget *parent, const QString &title)
: QWidget(parent)
, m_title(title)
{
setWindowTitle(m_title);
resize(600, 400);
setAttribute(Qt::WA_DeleteOnClose, false); // 自己管理生命周期
// 顶部工具条:还原按钮
m_btnRestore = new QPushButton(QStringLiteral("还原"), this);
QHBoxLayout *topBar = new QHBoxLayout;
topBar->addStretch();
topBar->addWidget(m_btnRestore);
// 内容区
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->addLayout(topBar);
connect(m_btnRestore, &QPushButton::clicked,
this, &DetachWindow::restoreRequested);
}
void DetachWindow::setContent(QWidget *content)
{
m_content = content;
if (m_content) {
m_content->setParent(this);
m_layout->addWidget(m_content);
m_content->show();
}
}
QWidget *DetachWindow::takeContent()
{
QWidget *c = m_content;
if (c) {
m_layout->removeWidget(c);
c->setParent(nullptr); // 先脱离
}
m_content = nullptr;
return c;
}
void DetachWindow::closeEvent(QCloseEvent *event)
{
emit restoreRequested(); // 通知主窗口还原
event->ignore(); // 由主窗口 hide + deleteLater 真正关闭
}
实现

已弹出界面会显示该界面已在独立窗口中显示。
这样可以实现多个屏幕时不同屏幕显示不同的界面