护眼大路灯哪个牌子好用?热门护眼大路灯推荐,轻松选对不走弯路
2026/7/29 8:43:46
#ifndef CUSTOMDOCKWIDGET_H
#define CUSTOMDOCKWIDGET_H
#include <QDockWidget>
#include <QStringList>
class QStackedWidget;
class CustomDockWidget : public QDockWidget
{Q_OBJECT
public:
enum TabPosition {West = 0, // 左侧停靠
North = 1, // 顶部停靠
East = 2, // 右侧停靠
South = 3 // 底部停靠
};
Q_ENUM(TabPosition)
explicit CustomDockWidget(const QString &title, TabPosition pos = West, QWidget *parent = nullptr);
void addPage(QWidget *page, const QString &label);
void setCurrentIndex(int index);
int currentIndex() const;
protected:
void paintEvent(QPaintEvent *) override;
void mousePressEvent(QMouseEvent *) override;
private:
QRect tabRect(int i) const;
int tabAt(const QPoint &pos) const;
QStackedWidget *m_stack = nullptr;
QStringList m_labels;
int m_current = 0;
TabPosition m_pos = West;
static constexpr int m_tabBarThick = 22;
};
#endif
#include "CustomDockWidget.h"
#include <QStackedWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QHBoxLayout>
CustomDockWidget::CustomDockWidget(const QString &title, TabPosition pos, QWidget *parent)
: QDockWidget(title, parent), m_pos(pos)
{setObjectName(QStringLiteral("CustomDock_") + title);setFeatures(DockWidgetMovable | DockWidgetClosable | DockWidgetFloatable);
m_stack = new QStackedWidget();
auto *layout = new QHBoxLayout();
layout->setSpacing(0);
switch (m_pos) {case West: layout->setContentsMargins(m_tabBarThick, 0, 0, 0); break;
case East: layout->setContentsMargins(0, 0, m_tabBarThick, 0); break;
case North: layout->setContentsMargins(0, m_tabBarThick, 0, 0); break;
case South: layout->setContentsMargins(0, 0, 0, m_tabBarThick); break;
}
layout->addWidget(m_stack);
auto *container = new QWidget(this);
container->setLayout(layout);
setWidget(container);
}
void CustomDockWidget::addPage(QWidget *page, const QString &label)
{m_stack->addWidget(page);
m_labels << label;
update();
}
void CustomDockWidget::setCurrentIndex(int index)
{if (index >= 0 && index < m_stack->count()) {m_current = index;
m_stack->setCurrentIndex(index);
update();
}
}
int CustomDockWidget::currentIndex() const
{return m_current;
}
QRect CustomDockWidget::tabRect(int i) const
{QWidget *c = QDockWidget::widget();
int cx = c ? c->x() : 0;
int cy = c ? c->y() : 0;
int cw = c ? c->width() : 0;
int ch = c ? c->height() : 0;
QFont f = font(); f.setPointSize(9);
QFontMetrics fm(f);
if (m_pos == West || m_pos == East) {int y = cy;
for (int j = 0; j < i; ++j)
y += fm.horizontalAdvance(m_labels.value(j)) + 20;
int tabH = fm.horizontalAdvance(m_labels.value(i)) + 20;
if (m_pos == West)
return QRect(cx, y, m_tabBarThick, tabH);
else
return QRect(cx + cw - m_tabBarThick, y, m_tabBarThick, tabH);
} else {int x = cx;
for (int j = 0; j < i; ++j)
x += fm.horizontalAdvance(m_labels.value(j)) + 20;
int tabW = fm.horizontalAdvance(m_labels.value(i)) + 20;
if (m_pos == North)
return QRect(x, cy, tabW, m_tabBarThick);
else
return QRect(x, cy + ch - m_tabBarThick, tabW, m_tabBarThick);
}
}
int CustomDockWidget::tabAt(const QPoint &pos) const
{for (int i = 0; i < m_labels.size(); ++i)
if (tabRect(i).contains(pos)) return i;
return -1;
}
void CustomDockWidget::paintEvent(QPaintEvent *event)
{QDockWidget::paintEvent(event);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPalette pal = palette();
QColor borderColor(217, 217, 217);
for (int i = 0; i < m_labels.size(); ++i) {QRect r = tabRect(i);
bool sel = (i == m_current);
QRect drawR = r;
if (sel) {switch (m_pos) {case West: drawR = r.adjusted(0, 0, 2, 0); break;
case East: drawR = r.adjusted(-2, 0, 0, 0); break;
case North: drawR = r.adjusted(0, 0, 0, 2); break;
case South: drawR = r.adjusted(0, -2, 0, 0); break;
}
}
if (sel) {p.setBrush(pal.base());
p.setPen(Qt::NoPen);
p.drawRect(drawR);
} else {p.setBrush(pal.window());
p.setPen(QPen(borderColor, 1));
p.drawRect(drawR);
}
QFont f = font();
f.setPointSize(9);
p.setFont(f);
p.setPen(sel ? pal.windowText().color() : pal.buttonText().color());
if (m_pos == West) {p.save();
p.translate(r.center());
p.rotate(-90);
p.drawText(QRect(-r.height() / 2, -r.width() / 2,
r.height(), r.width()),
Qt::AlignCenter, m_labels[i]);
p.restore();
} else if (m_pos == East) {p.save();
p.translate(r.center());
p.rotate(90);
p.drawText(QRect(-r.height() / 2, -r.width() / 2,
r.height(), r.width()),
Qt::AlignCenter, m_labels[i]);
p.restore();
} else {p.drawText(r, Qt::AlignCenter, m_labels[i]);
}
}
}
void CustomDockWidget::mousePressEvent(QMouseEvent *event)
{int idx = tabAt(event->pos());
if (idx >= 0) {setCurrentIndex(idx);
return;
}
QDockWidget::mousePressEvent(event);
}
#include "MainWindow.h"
#include "CustomDockWidget.h"
#include <QDockWidget>
#include <QTreeWidget>
#include <QTextEdit>
#include <QToolBox>
#include <QStackedWidget>
#include <QPushButton>
#include <QButtonGroup>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QListWidget>
#include <QSplitter>
#include <QTabWidget>
#include <QLabel>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QStatusBar>
#include <QApplication>
#include <QStyle>
// ---------------------------------------------------------------------------
// Construction
// ---------------------------------------------------------------------------
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{setWindowTitle(QStringLiteral("QDockWidget 多面板示例"));resize(1200, 800);
createDockPanels();
// --- Central widget ---
auto *centralLabel = new QLabel(
QStringLiteral("<h2>欢迎使用 QDockWidget 示例</h2>""<p>左侧面板使用 CustomDockWidget(West 方向)</p>"
"<p>右侧面板使用 QTabWidget(East 方向)</p>"
"<p>您可以用 CustomDockWidget 指定停靠按钮到 "
"West / North / East / South 任意方向。</p>"),
this);
centralLabel->setAlignment(Qt::AlignCenter);
setCentralWidget(centralLabel);
// --- Menu ---
auto *viewMenu = menuBar()->addMenu(QStringLiteral("视图(&V)"));auto toggleAct = [&](QDockWidget *dw, const QString &text) {auto *act = dw->toggleViewAction();
act->setText(text);
viewMenu->addAction(act);
};
toggleAct(m_leftDock, QStringLiteral("左侧面板(&L)"));toggleAct(m_rightDock, QStringLiteral("右侧面板(&R)"));toggleAct(m_outputDock, QStringLiteral("输出窗口(&O)"));// --- Status bar ---
statusBar()->showMessage(QStringLiteral("就绪 | 拖动面板可重新布局"));}
// ---------------------------------------------------------------------------
// Dock panel factory
// ---------------------------------------------------------------------------
void MainWindow::createDockPanels()
{createLeftPanel();
createPropertiesDock();
createOutputDock();
createToolboxDock();
// --- Right: QDockWidget 内嵌 QTabWidget(停靠按钮在右侧) ---
m_rightDock = new QDockWidget(QStringLiteral("面板"), this);m_rightDock->setObjectName(QStringLiteral("RightPanelDock"));m_rightDock->setFeatures(QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetFloatable);
m_rightTab = new QTabWidget(m_rightDock);
m_rightTab->setTabPosition(QTabWidget::East);
m_rightTab->addTab(m_propTree, QStringLiteral("属性"));m_rightTab->addTab(m_toolBox, QStringLiteral("工具箱"));m_rightDock->setWidget(m_rightTab);
// --- 布局 ---
addDockWidget(Qt::LeftDockWidgetArea, m_leftDock);
addDockWidget(Qt::RightDockWidgetArea, m_rightDock);
addDockWidget(Qt::BottomDockWidgetArea, m_outputDock);
}
// ---------------------------------------------------------------------------
// ① 左侧面板:CustomDockWidget,按钮在左侧(West)
// ---------------------------------------------------------------------------
void MainWindow::createLeftPanel()
{m_fileTree = new QTreeWidget();
m_fileTree->setHeaderLabel(QStringLiteral("项目文件"));m_fileTree->setAnimated(true);
auto *src = new QTreeWidgetItem(m_fileTree, {QStringLiteral("src")});src->addChild(new QTreeWidgetItem({QStringLiteral("main.cpp")}));src->addChild(new QTreeWidgetItem({QStringLiteral("mainwindow.cpp")}));src->addChild(new QTreeWidgetItem({QStringLiteral("mainwindow.h")}));auto *resources = new QTreeWidgetItem(m_fileTree, {QStringLiteral("resources")});resources->addChild(new QTreeWidgetItem({QStringLiteral("icons")}));resources->addChild(new QTreeWidgetItem({QStringLiteral("styles.qss")}));m_fileTree->expandAll();
m_favList = new QListWidget();
m_favList->addItem(QStringLiteral("常用头文件"));m_favList->addItem(QStringLiteral("核心算法模块"));m_favList->addItem(QStringLiteral("配置文件"));m_favList->addItem(QStringLiteral("构建脚本"));m_favList->addItem(QStringLiteral("文档资源"));// CustomDockWidget 第二个参数指定停靠按钮方向:
// CustomDockWidget::West — 左侧(默认)
// CustomDockWidget::North — 顶部
// CustomDockWidget::East — 右侧
// CustomDockWidget::South — 底部
m_leftDock = new CustomDockWidget(
QStringLiteral("项目"),CustomDockWidget::West, // ← 停靠按钮在左侧
this);
m_leftDock->addPage(m_fileTree, QStringLiteral("文件浏览器"));m_leftDock->addPage(m_favList, QStringLiteral("收藏"));}
// ---------------------------------------------------------------------------
// ② 属性面板(嵌入右侧 QTabWidget)
// ---------------------------------------------------------------------------
void MainWindow::createPropertiesDock()
{m_propTree = new QTreeWidget();
m_propTree->setHeaderLabels({QStringLiteral("属性"), QStringLiteral("值")});m_propTree->setAlternatingRowColors(true);
auto addProp = [&](const QString &cat, const QString &key, const QString &val) {auto *item = new QTreeWidgetItem({key, val});if (!cat.isEmpty()) {auto *parent = new QTreeWidgetItem(m_propTree, {cat});parent->addChild(item);
parent->setExpanded(true);
} else {m_propTree->addTopLevelItem(item);
}
};
addProp(QString(), QStringLiteral("名称"), QStringLiteral("MainWindow"));addProp(QString(), QStringLiteral("类名"), QStringLiteral("QMainWindow"));addProp(QStringLiteral("几何"), QStringLiteral("位置"), QStringLiteral("(0, 0)"));addProp(QStringLiteral("几何"), QStringLiteral("尺寸"), QStringLiteral("1200 x 800"));addProp(QStringLiteral("样式"), QStringLiteral("主题"), QStringLiteral("Fusion"));}
// ---------------------------------------------------------------------------
// ③ 输出面板(底部)
// ---------------------------------------------------------------------------
void MainWindow::createOutputDock()
{m_outputDock = new QDockWidget(QStringLiteral("输出"), this);m_outputDock->setObjectName(QStringLiteral("OutputDock"));m_outputDock->setFeatures(QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetFloatable);
m_outputEdit = new QTextEdit(m_outputDock);
m_outputEdit->setReadOnly(true);
m_outputEdit->setFont(QFont(QStringLiteral("Consolas"), 10));m_outputEdit->append(QStringLiteral("[系统] 程序已启动"));m_outputEdit->append(QStringLiteral("[系统] 主窗口尺寸:1200 x 800"));m_outputEdit->append(QStringLiteral("[系统] DockWidget 初始化完成"));m_outputEdit->append(QStringLiteral("[信息] 您可以拖动面板重新布局"));m_outputDock->setWidget(m_outputEdit);
}
// ---------------------------------------------------------------------------
// ④ 工具箱内容(嵌入右侧 QTabWidget)
// ---------------------------------------------------------------------------
void MainWindow::createToolboxDock()
{m_toolBox = new QToolBox();
auto *page1 = new QLabel(QStringLiteral(" 选择工具\n 画笔\n 橡皮擦\n 填充\n 文本"));page1->setAlignment(Qt::AlignTop | Qt::AlignLeft);
page1->setMargin(12);
m_toolBox->addItem(page1, QIcon(), QStringLiteral("常用工具"));auto *page2 = new QLabel(QStringLiteral(" 矩形\n 椭圆\n 线条\n 贝塞尔曲线"));page2->setAlignment(Qt::AlignTop | Qt::AlignLeft);
page2->setMargin(12);
m_toolBox->addItem(page2, QIcon(), QStringLiteral("形状工具"));auto *page3 = new QLabel(QStringLiteral(" 取色器\n 标尺\n 放大镜"));page3->setAlignment(Qt::AlignTop | Qt::AlignLeft);
page3->setMargin(12);
m_toolBox->addItem(page3, QIcon(), QStringLiteral("辅助工具"));}