Qt实战--主窗口布局

QMainWindow

MainWindow类我们一般选择直接继承自QMainWindow,因为QMainWindow已经向我们提供了一个常用的应用程序主窗口布局,包括QMenuBar菜单栏、QToolBar工具栏、QStatusBar状态栏、QDockWidget可停靠控件、以及需要自己定制的CentralWidget中央控件。这大大节省了我们布局主窗口的时间。

QMainWindow

构造函数

我们一般在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::addActionQToolbar::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

每个单元格都是一个视频控件HVideoWidgetHVideoWidgetHVideoTitlebarHVideoToolbarHVideoWnd组成,实现效果如下

HVideoWidget
HVideoWidget

HVideoWidget的具体接口和实现下节见

ithewei CSDN认证博客专家 c/c++ Qt libhv
编程之路,其路漫漫,吾将上下而求索
https://github.com/ithewei
https://hewei.blog.csdn.net
©️2020 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值