Qt实战--主窗口布局
QMainWindow
MainWindow
类我们一般选择直接继承自QMainWindow
,因为QMainWindow
已经向我们提供了一个常用的应用程序主窗口布局,包括QMenuBar
菜单栏、QToolBar
工具栏、QStatusBar
状态栏、QDockWidget
可停靠控件、以及需要自己定制的CentralWidget
*控件。这大大节省了我们布局主窗口的时间。
构造函数
我们一般在QWidget派生类的构造函数中构造界面,并建立固定的信号与槽连接,形式如下:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// member variable initialization here
// ...
initUI();
initConnect();
}
initUI
在主窗口initUI
中我们需要设定窗口大小、设置窗口图标、初始化菜单栏、工具栏、状态栏、还有不可缺少的一步是设置*控件
void MainWindow::initUI(){
setWindowIcon(QIcon(":/image/icon.png"));
setBaseSize(1200, 800);
initMenu();
center = new CentralWidget;
setCentralWidget(center);
statusBar()->showMessage(tr("Ready"));
}
initMenu
void MainWindow::initMenu(){
// Media
QMenu *mediaMenu = menuBar()->addMenu(tr("&Media"));
QToolBar *mediaToolbar = addToolBar(tr("&Media"));
toolbars.push_back(mediaToolbar);
QAction* actOpenFile = new QAction(QIcon(":/image/file.png"), tr(" Open File"));
actOpenFile->setShortcut(QKeySequence("Ctrl+F"));
connect(actOpenFile, &QAction::triggered, this, [=](){
onOpenMedia(MEDIA_TYPE_FILE);
});
mediaMenu->addAction(actOpenFile);
mediaToolbar->addAction(actOpenFile);
QAction* actOpenNetwork = new QAction(QIcon(":/image/network.png"), tr(" Open Network"));
actOpenNetwork->setShortcut(QKeySequence("Ctrl+N"));
connect(actOpenNetwork, &QAction::triggered, this, [=](){
onOpenMedia(MEDIA_TYPE_NETWORK);
});
mediaMenu->addAction(actOpenNetwork);
mediaToolbar->addAction(actOpenNetwork);
QAction* actOpenCapture = new QAction(QIcon(":/image/capture.png"), tr(" Open Capture"));
actOpenCapture->setShortcut(QKeySequence("Ctrl+C"));
connect(actOpenCapture, &QAction::triggered, this, [=](){
onOpenMedia(MEDIA_TYPE_CAPTURE);
});
mediaMenu->addAction(actOpenCapture);
mediaToolbar->addAction(actOpenCapture);
// ...
// Help
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(tr(" &About"), this, SLOT(about()));
}
initMenu
中通过menuBar()->addMenu
为菜单栏添加菜单,addToolBar
添加对应的工具栏,然后通过new QAction
来创建一个动作,QMenu::addAction
和QToolbar::addAction
添加QAction
CentralWidget
我们设计的*区域显示,初步是显示一个媒体播放列表HMediaList
(以H开头的都是我们自定义类,以和Qt中Q开头的类区别开)和一个多画面网格HMultiView
,使用QSplitter
作为可伸缩分隔板。
void CentralWidget::initUI(){
ml = new HMediaList;
mv = new HMultiView;
QSplitter *split = new QSplitter(Qt::Horizontal);
split->addWidget(ml);
split->addWidget(mv);
ml->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
ml->setMinimumWidth(300);
mv->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mv->setMinimumWidth(700);
split->setStretchFactor(0, MEDIA_LIST_FACTOR);
split->setStretchFactor(1, MULTI_VIEW_FACTOR);
QHBoxLayout *hbox = genHBoxLayout();
hbox->addWidget(split);
setLayout(hbox);
ml->setVisible(MEDIA_LIST_VISIBLE);
}
HMultiView
多画面网格,我们的需求是:
- *切换row*col风格(菜单栏选择风格)
- 扩展(全屏)某个单元格(鼠标双击)
- 交换两个单元格位置(鼠标左键拖动)
- 合并单元格(鼠标右键拖动画合并区域)
- 根据id获取单元格
- 根据位置获取单元格
- 获取空闲状态的单元格
- 保存当前布局,以便退出程序后,下次进来恢复布局
struct HWndInfo{
int id;
QRect rc;
bool visible;
};
struct HSaveLayout{
HLayout layout;
QVector<HWndInfo> views;
};
class HMultiView : public QWidget
{
Q_OBJECT
public:
enum Action{
STRETCH,
EXCHANGE,
MERGE,
};
explicit HMultiView(QWidget *parent = nullptr);
HVideoWidget* getPlayerByID(int playerid);
HVideoWidget* getPlayerByPos(QPoint pt);
HVideoWidget* getIdlePlayer();
signals:
public slots:
void setLayout(int row, int col);
void mergeCells(int lt, int rb);
void exchangeCells(HVideoWidget* player1, HVideoWidget* player2);
void stretch(QWidget* wdg);
void saveLayout();
void restoreLayout();
void play(HMedia& media);
protected:
void initUI();
void initConnect();
void relayout();
virtual void resizeEvent(QResizeEvent* e);
virtual void mousePressEvent(QMouseEvent *e);
virtual void mouseReleaseEvent(QMouseEvent *e);
virtual void mouseMoveEvent(QMouseEvent *e);
virtual void mouseDoubleClickEvent(QMouseEvent *e);
public:
HLayout layout;
QVector<QWidget*> views;
QLabel *labRect;
QLabel *labDrag;
HSaveLayout save_layout;
QPoint ptMousePress;
Action action;
};
根据需求我们定义出头文件,重载鼠标按下、移动、释放、双击事件,resize时也需要重新布局
为了完成合并单元格的需求,我们定义了一个逻辑类HLayout
,这个类记录了每个单元格所占行和列
#ifndef HLAYOUT_H
#define HLAYOUT_H
#include <map>
class HLayoutCell{
public:
HLayoutCell(){r1=r2=c1=c2=0;}
HLayoutCell(int r1, int r2, int c1, int c2){
this->r1 = r1;
this->r2 = r2;
this->c1 = c1;
this->c2 = c2;
}
int getRowspan() {return r2 - r1 + 1;}
int getColspan() {return c2 - c1 + 1;}
int getNums() {return getRowspan() * getColspan();}
bool contain(HLayoutCell cell){
if (cell.r1 >= r1 && cell.r2 <= r2 &&
cell.c1 >= c1 && cell.c2 <= c2)
return true;
return false;
}
int r1,r2,c1,c2;
};
class HLayout
{
public:
explicit HLayout();
void init(int row, int col);
bool getLayoutCell(int id, HLayoutCell& rst);
HLayoutCell merge(int lt, int rb);
public:
int row;
int col;
int num;
std::map<int, HLayoutCell> m_mapCells; // id => HLayoutCell
};
#endif // HLAYOUT_H
#include "hlayout.h"
#include "hdef.h"
HLayout::HLayout()
{
}
void HLayout::init(int row, int col){
this->row = row;
this->col = col;
num = row * col;
m_mapCells.clear();
for (int r = 1; r <= row; ++r){
for (int c = 1; c <= col; ++c){
int id = (r-1) * col + c;
m_mapCells[id] = HLayoutCell(r,r,c,c);
}
}
}
bool HLayout::getLayoutCell(int id, HLayoutCell& rst){
if (m_mapCells.find(id) != m_mapCells.end()){
rst = m_mapCells[id];
return true;
}
return false;
}
HLayoutCell HLayout::merge(int lt, int rb){
HLayoutCell cell_lt,cell_rb;
if (getLayoutCell(lt, cell_lt) && getLayoutCell(rb, cell_rb)){
int r1 = MIN(cell_lt.r1, cell_rb.r1);
int r2 = MAX(cell_lt.r2, cell_rb.r2);
int c1 = MIN(cell_lt.c1, cell_rb.c1);
int c2 = MAX(cell_lt.c2, cell_rb.c2);
HLayoutCell cell(r1, r2, c1, c2);
std::map<int, HLayoutCell>::iterator iter = m_mapCells.begin();
while (iter != m_mapCells.end()){
if (cell.contain(iter->second)){
iter = m_mapCells.erase(iter);
}else
++iter;
}
m_mapCells[lt] = cell;
return cell;
}
}
具体实现细节请专研源码,我就不细说了。
HVideoWidget
每个单元格都是一个视频控件HVideoWidget
,HVideoWidget
由HVideoTitlebar
、HVideoToolbar
和HVideoWnd
组成,实现效果如下
HVideoWidget的具体接口和实现下节见
推荐阅读
-
Python qt5编写第三个gui---由主窗口弹出子窗
-
Qt5 实现主窗口状态栏显示时间
-
Qt实战--主窗口布局
-
Qt5开发学习总结(四)——资源文件的使用与主窗口QMainWindow实现登录框
-
QT布局管理之QSplitter实现分割窗口
-
Qt布局管理: 分割窗口QSplitter类讲解(纯代码实现分割窗口)
-
QT布局管理(1)-分割窗口类QSplitter,可用鼠标任意拖动控件的大小
-
PyQt(Python+Qt)学习随笔:窗口部件大小策略sizePolicy与SizeConstraint布局大小约束的关系
-
qt 主窗口关闭时关闭子窗口
-
Python qt5编写第三个gui---由主窗口弹出子窗