Qt實戰--主窗口布局
阿新 • • 發佈:2019-01-24
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的具體介面和實現下節見