Qt学习之界面UI编程应用

Qt作为c++的GUI编程框架,在Qt4时代,一直是传统的基于QtWidget的C++编程,而到了Qt5,为了适用当前的移动应用开发浪潮,Qt提供了,另一套界面框架QtQuick,它是基于QML语言(类似于js)的编程,就一个特点:快!。编码量大大减少。

这两套框架虽说用不同的语言开发,但底层还是C++。

基于QtQuick&QML的界面开发主要从以下方面学习:

  1. QML中的可视类型
  2. 在QML中响应用户输入
  3. QML动画
  4. 在QML中显示文本
  5. QML布局
  6. 样式和主题支持
  7. 在QML中集成JavaScript
  8. 如何进行扩展

基于QtWidget的界面开发主要从以下方面学习:

  1. 应用程序主窗口类
  2. 桌面集成类
  3. 对话框类
  4. 布局管理类
  5. 模型/视图编程类(重点)
  6. 富文本处理类
  7. 鼠标拖放处理类
  8. 国际化类

下面开始学习:

一、应用程序主窗口类

1、QAction类:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //获得此窗口的菜单栏
    QMenuBar * menuBar = this->menuBar();

    //直接添加到菜单栏上 返回添加的实例指针
    QAction *action1 = menuBar->addAction("action");
    connect(action1,&QAction::triggered,[]{
        qDebug() << "action1 triggered";
    });
    connect(action1,&QAction::hovered,[]{
        qDebug() << "hovered";
    });
    //实例化然后添加
    QAction *action2 = new QAction("action2");
    menuBar->addAction(action2);
    connect(action2,&QAction::triggered,[]{
        qDebug() << "action2 triggered";
    });

    //添加到菜单上
    QMenu *menu = new QMenu("menu");
    menuBar->addMenu(menu);
    QAction * action3 = menu->addAction("action3");

    //还可以向QAction上添加一些附加的信息
    QPixmap pixmap(QSize(20,20));
    pixmap.fill(QColor(Qt::red));
    action3->setIcon(QIcon(pixmap));
    action3->setShortcut(QKeySequence::Open);
    connect(action3,&QAction::triggered,[]{
        qDebug() << "open";
    });

    //也可以添加到toolbar上
    QToolBar *toolbar = addToolBar("toolBar");
    toolbar->addAction("action4");
}

2、QActionGroup类(凡是放进这个组中的action,便可相互互斥)

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(400,300);
    //先放个label进去
    QLabel *label = new QLabel;
    label->setText("hello world!");
    setCentralWidget(label);

    QMenuBar *bar = menuBar();
    QMenu *menu = new QMenu("格式");
    bar->addMenu(menu);

    //3个action 分别表示文字的三个方向 靠左 靠右 居中
    QAction *leftAction = new QAction("靠左对齐");
    leftAction->setCheckable(true);
    leftAction->setChecked(true);
    QAction *rightAction = new QAction("靠右对齐");
    rightAction->setCheckable(true);
    QAction *centerAction = new QAction("居中对齐");
    centerAction->setCheckable(true);

    menu->addAction(leftAction);
    menu->addAction(rightAction);
    menu->addAction(centerAction);

    //放进actiongroup中 这3个action便是相互互斥的了
    QActionGroup *group = new QActionGroup(this);
    group->addAction(leftAction);
    group->addAction(rightAction);
    group->addAction(centerAction);

    connect(leftAction,&QAction::triggered,[=]{
        label->setText("leftAction");
        label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
    });

    connect(rightAction,&QAction::triggered,[=]{
        label->setText("rightAction");
        label->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
    });

    connect(centerAction,&QAction::triggered,[=]{
        label->setText("centerAction");
        label->setAlignment(Qt::AlignCenter);
    });
}

3、QWidgetAction:派生自QAction,由于QAction一直是作为一个Item在使用,在定制性上比较弱,所以QT从QAction派生了一个QWidgetAction类来解决action的可定制功能。可以向QWidgetAction中放入我们自己定义的组件作为action显示

MyWindow::MyWindow(QWidget *parent) : QMainWindow(parent)
{
    resize(300,200);
    QMenu *menu = menuBar()->addMenu("我是菜单");

    //下面放两个action,一个是qt默认样式的,一个是自定义样式的
    QIcon icon(":/icon.jpg");
    QAction *action = new QAction("我是默认样式的");
    action->setIcon(icon);
    menu->addAction(action);

    QWidgetAction *action1 = new QWidgetAction(0);

    QLabel * label = new QLabel;
    label->setPixmap(QPixmap(":/icon.jpg"));
    action1->setDefaultWidget(label);

    connect(action1,&QWidgetAction::triggered,[]{
        qDebug() << "action1 triggered";
    });

    menu->addAction(action1);

}

4、QDockWidget类:可以停靠和浮动在qmainwindow中的组件,相当于有多个可展示内容的界面用于和用户交互

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle(tr("windows"));  //设置主窗口的标题栏文字
    QTextEdit *te = new QTextEdit(this);  //定义一个QTextEdit对象作为主窗口
    te->setText(tr("Main Window"));
    te->setAlignment(Qt::AlignCenter);
    setCentralWidget(te);     //将此编辑框设为主窗口的中央窗体
    //停靠窗口1
    QDockWidget *dock = new QDockWidget(tr("DockWindow1"), this);
    dock->setFeatures(QDockWidget::DockWidgetMovable);  //可移动
    dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
    //可以在主窗口的左/右边停靠
    QTextEdit *te1 =new QTextEdit();
    te1->setText(tr("Window1,The dock widget can be moved between docks by the user" ""));
    dock->setWidget(te1);
    addDockWidget(Qt::RightDockWidgetArea,dock);

    //停靠窗口2
    dock=new QDockWidget(tr("DockWindow2"),this);
    dock->setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable); //可关闭、可浮动
    //  QDockWidget::NoDockWidgetFeatures   //不可以关闭,不可以移动,不可以浮动
    dock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
    //顶端停靠,底部停靠
    QTextEdit *te2 =new QTextEdit();
    te2->setText(tr("Window2,The dock widget can be detached from the main window,""and floated as an independent window, and can be closed"));
    dock->setWidget(te2);
    addDockWidget(Qt::RightDockWidgetArea,dock);

    //停靠窗口3
    dock=new QDockWidget(tr("DockWindow3"),this);
    dock->setFeatures(QDockWidget::AllDockWidgetFeatures);     //全部特性
    QTextEdit *te3 =new QTextEdit();
    te3->setText(tr("Window3,The dock widget can be closed, moved, and floated"));
    dock->setWidget(te3);
    addDockWidget(Qt::RightDockWidgetArea,dock);
}

5、QMainWindow类:是一个包含菜单栏、工具栏、状态栏的窗口部件,一般的桌面程序都是以该组件为显示窗口的

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //mainwindow主要有 标题栏 菜单栏 工具栏 停靠浮动窗口 中间部件 状态栏 下面一一创建
    //标题栏 显示标题
    setWindowTitle("MainWindow");
    //也可以通过设置把标题栏去掉
//    setWindowFlags(Qt::FramelessWindowHint);
    //菜单栏
    menuBar()->addMenu("menu");
    menuBar()->addAction("action1");
    menuBar()->setStatusTip("菜单栏");//用于状态栏显示

    //工具栏
    QToolBar * toolBar = addToolBar("工具栏");
    toolBar->addAction("action2")->setStatusTip("action2");
    toolBar->addAction("action3");
    toolBar->addAction("action4");
    toolBar->setStatusTip("工具栏");

    //浮动窗口
    QDockWidget *dock1 = new QDockWidget;
    QWidget *mywid1 = new QWidget();


    QPushButton *btn_Play = new QPushButton("Play", mywid1);
    btn_Play->setFixedSize(40, 30);

    QPushButton *btn_Pause = new QPushButton("Pause", mywid1);
    btn_Pause->setFixedSize(40, 30);
    QPushButton *btn_Stop = new QPushButton("Stop", mywid1);
    btn_Stop->setFixedSize(40, 30);
    QHBoxLayout *HorizontalLayout = new QHBoxLayout;
    HorizontalLayout->addWidget(btn_Play);
    HorizontalLayout->addWidget(btn_Pause);
    HorizontalLayout->addWidget(btn_Stop);
    QLabel *qlabel = new QLabel("hello");
    QPixmap pixmap(btn_Pause->size());
    pixmap.fill(Qt::blue);
    qlabel->setPixmap(pixmap);
    HorizontalLayout->addWidget(qlabel);
    mywid1->setLayout(HorizontalLayout);
    dock1->setWidget(mywid1);
    addDockWidget(Qt::LeftDockWidgetArea,dock1);


    QDockWidget *dock2 = new QDockWidget("99999",this);
    dock2->setFeatures(QDockWidget::AllDockWidgetFeatures); //全部特性
    QTextEdit *te3 =new QTextEdit();
    te3->setText(tr("Window3,The dock widget can be closed, moved, and floated"));
    dock2->setWidget(te3);
    addDockWidget(Qt::RightDockWidgetArea,dock2);

    //中间部件
    setCentralWidget(new QTextEdit("在这里编辑..."));

    //状态栏
    statusBar();

}

6、QMdiArea类: 常用于QMainwindow中,作为多个子窗口的父窗口,可以执行用户的多个界面窗口的显示及操作,类似于word中的多个文档操作

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(300,400);
    QMdiArea *mdiarea = new QMdiArea;
    QMdiSubWindow *sub1 = new QMdiSubWindow;
    sub1->setWindowTitle("子窗口1");
    sub1->resize(200,200);
    sub1->setWidget(new QLabel("sub1"));
    mdiarea->addSubWindow(sub1);

    QMdiSubWindow *sub2 = new QMdiSubWindow;
    sub2->setWindowTitle("子窗口2");
    sub2->resize(200,200);
    sub2->setWidget(new QLabel("sub2"));
    mdiarea->addSubWindow(sub2);

//    mdiarea->setViewMode(QMdiArea::TabbedView);
//    mdiarea->setTabShape(QTabWidget::Rounded);
//    mdiarea->setTabsClosable(true);
    mdiarea->setActivationOrder(QMdiArea::CreationOrder);
    mdiarea->setBackground(QBrush(Qt::red));
    setCentralWidget(mdiarea);
}

7、QMdiSunWindow类 :MDI窗口的子窗口。用法 同上。

8、QMenu类:菜单类。在该类下可以存放多个QAction类,并且也支持嵌套QMenu。下面的例子是Qt自带的,可以说是很全了,现把源码贴出来

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

protected:
#ifdef QT_NO_CONTEXTMENU
   void contextMenuEvent(QContextMenuEvent *event);
#endif

private slots:
   void newFile();
   void open();
   void save();
   void print();
   void undo();
   void redo();
   void cut();
   void copy();
   void paste();
   void bold();
   void italic();
   void leftAlign();
   void rightAlign();
   void justify();
   void center();
   void setLineSpacing();
   void setParagraphSpacing();
   void about();
   void aboutQt();

private:
   void createActions();
   void createMenus();

   QMenu *fileMenu;
   QMenu *editMenu;
   QMenu *formatMenu;
   QMenu *helpMenu;
   QActionGroup *alignmentGroup;
   QAction *newAct;
   QAction *openAct;
   QAction *saveAct;
   QAction *printAct;
   QAction *exitAct;
   QAction *undoAct;
   QAction *redoAct;
   QAction *cutAct;
   QAction *copyAct;
   QAction *pasteAct;
   QAction *boldAct;
   QAction *italicAct;
   QAction *leftAlignAct;
   QAction *rightAlignAct;
   QAction *justifyAct;
   QAction *centerAct;
   QAction *setLineSpacingAct;
   QAction *setParagraphSpacingAct;
   QAction *aboutAct;
   QAction *aboutQtAct;
   QLabel *infoLabel;
};
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QWidget *widget = new QWidget;
    setCentralWidget(widget);

    QWidget *topFiller = new QWidget;
    topFiller->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

    infoLabel = new QLabel("<i>Choose a menu option,"
                           "or right-click to"
                           " invoke a context menu</i>");
    infoLabel->setFrameStyle(QFrame::StyledPanel|QFrame::Sunken);
    infoLabel->setAlignment(Qt::AlignCenter);
    infoLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);//可以让文本被选中 及 复制

    QWidget *bottomFiller = new QWidget;
    bottomFiller->setSizePolicy(QSizePolicy::Expanding,
                                QSizePolicy::Expanding);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->setMargin(10);
    layout->addWidget(topFiller);
    layout->addWidget(infoLabel);
    layout->addWidget(bottomFiller);
    widget->setLayout(layout);

    createActions();
    createMenus();

    QString message = tr("A context menu is available by right-clicking");
    statusBar()->showMessage(message);

    setWindowTitle(tr("Menus"));
    setMinimumSize(160,160);
    resize(480,320);
}

#ifdef QT_NO_CONTEXTMENU
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu menu(this);
    menu.addAction(cutAct);
    menu.addAction(copyAct);
    menu.addAction(pasteAct);
    menu.exec(event->globalPos());
}
#endif

MainWindow::~MainWindow()
{

}

void MainWindow::newFile()
{
    infoLabel->setText(tr("Invoke <b>File|New</b>"));
}

void MainWindow::open()
{
    infoLabel->setText(tr("Invoke <b>File|Open</b>"));
}

void MainWindow::save()
{
    infoLabel->setText(tr("Invoke <b>File|Save</b>"));
}

void MainWindow::print()
{
    infoLabel->setText(tr("Invoke <b>File|Print</b>"));
}

void MainWindow::undo()
{
    infoLabel->setText(tr("Invoke <b>Edit|Undo</b>"));
}

void MainWindow::redo()
{
    infoLabel->setText(tr("Invoke <b>Edit|Redo</b>"));
}

void MainWindow::cut()
{
    infoLabel->setText(tr("Invoke <b>Edit|Cut</b>"));
}

void MainWindow::copy()
{
    infoLabel->setText(tr("Invoke <b>Edit|Copy</b>"));
}
void MainWindow::paste()
{
    infoLabel->setText(tr("Invoked <b>Edit|Paste</b>"));
}
void MainWindow::bold()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|Bold</b>"));
}
void MainWindow::italic()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|Italic</b>"));
}
void MainWindow::leftAlign()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|LeftAlign</b>"));
}
void MainWindow::rightAlign()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|RightAlign</b>"));
}
void MainWindow::justify()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|Justify</b>"));
}
void MainWindow::center()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|Center</b>"));
}
void MainWindow::setLineSpacing()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|Set Line Spacing</b>"));
}
void MainWindow::setParagraphSpacing()
{
    infoLabel->setText(tr("Invoked <b>Edit|Format|Set Paragraph Spacing</b>"));
}
void MainWindow::about()
{
    infoLabel->setText(tr("Invoked <b>Help|About</b>"));
    QMessageBox::about(this,
                       tr("About Menu"),
                       tr("The <b>Menu</b> example shows how to create "
                          "menu-bar menus and context menus."));
}
void MainWindow::aboutQt()
{
    infoLabel->setText(tr("Invoked <b>About Qt</b>"));
}

void MainWindow::createActions()
{
    newAct = new QAction(tr("&New"),this);
    newAct->setShortcuts(QKeySequence::New);
    newAct->setStatusTip(tr("Create a new file"));
    connect(newAct,&QAction::triggered,this,&MainWindow::newFile);

    openAct = new QAction(tr("&Open"),this);
    openAct->setShortcuts(QKeySequence::Open);
    openAct->setStatusTip(tr("Open a existing file"));
    connect(openAct,&QAction::triggered,this,&MainWindow::open);

    saveAct = new QAction(tr("&Save"),this);
    saveAct->setShortcuts(QKeySequence::Save);
    saveAct->setStatusTip(tr("Save the document to disk"));
    connect(saveAct,&QAction::triggered,this,&MainWindow::save);

    printAct = new QAction(tr("&Print..."),this);
    printAct->setShortcuts(QKeySequence::Print);
    printAct->setStatusTip(tr("Print the document"));
    connect(printAct,&QAction::triggered,this,&MainWindow::print);

    exitAct = new QAction(tr("&Exit"),this);
    exitAct->setShortcuts(QKeySequence::Quit);
    exitAct->setStatusTip(tr("Exit the application"));
    connect(exitAct,&QAction::triggered,this,&MainWindow::close);

    undoAct = new QAction(tr("&Undo"),this);
    undoAct->setShortcuts(QKeySequence::Undo);
    undoAct->setStatusTip(tr("Undo the last operation"));
    connect(undoAct,&QAction::triggered,this,&MainWindow::undo);

    redoAct = new QAction(tr("&Redo"),this);
    redoAct->setShortcuts(QKeySequence::Redo);
    redoAct->setStatusTip(tr("Redo the last opeartion"));
    connect(redoAct,&QAction::triggered,this,&MainWindow::redo);

    cutAct = new QAction(tr("&Cut"),this);
    cutAct->setShortcuts(QKeySequence::Cut);
    cutAct->setStatusTip(tr("Cut the current selection's contents to the "
                            "clipboard"));
    connect(cutAct,&QAction::triggered,this,&MainWindow::cut);

    copyAct = new QAction(tr("&Copy"),this);
    copyAct->setShortcuts(QKeySequence::Copy);
    copyAct->setStatusTip(tr("Copy the current selection's contents "
                             "to the clipboard"));
    connect(copyAct,&QAction::triggered,this,&MainWindow::copy);

    pasteAct = new QAction(tr("&Paste"),this);
    pasteAct->setShortcuts(QKeySequence::Paste);
    pasteAct->setStatusTip(tr("Paste the clipboard's contents info "
                              "the current selection"));
    connect(pasteAct,&QAction::triggered,this,&MainWindow::paste);

    boldAct = new QAction(tr("&Bold"),this);
    boldAct->setShortcuts(QKeySequence::Bold);
    boldAct->setStatusTip(tr("Make the text bold"));
    connect(boldAct,&QAction::triggered,this,&MainWindow::bold);

    QFont boldFont = boldAct->font();
    boldFont.setBold(true);
    boldAct->setFont(boldFont);

    italicAct = new QAction(tr("&Italic"),this);
    italicAct->setShortcuts(QKeySequence::Italic);
    italicAct->setStatusTip("Make the text italic");
    connect(italicAct,&QAction::triggered,this,&MainWindow::italic);

    QFont italicFont = italicAct->font();
    italicFont.setItalic(true);
    italicAct->setFont(italicFont);

    setLineSpacingAct = new QAction(tr("set &Line Spacing..."),this);
    setLineSpacingAct->setStatusTip(tr("Change the gap between the lines of a paragraph"));
    connect(setLineSpacingAct,&QAction::triggered,this,&MainWindow::setLineSpacing);

    setParagraphSpacingAct = new QAction(tr("set &Paragraph Spacing..."));
    setParagraphSpacingAct->setStatusTip(tr("Change the gap between paragraphs"));
    connect(setParagraphSpacingAct,&QAction::triggered,this,&MainWindow::setParagraphSpacing);

    aboutAct = new QAction(tr("&About"),this);
    aboutAct->setStatusTip(tr("Show the application's About box"));
    connect(aboutAct,&QAction::triggered,this,&MainWindow::about);

    aboutQtAct = new QAction(tr("About &Qt"),this);
    aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
    connect(aboutQtAct,&QAction::triggered,qApp,QApplication::aboutQt);
    connect(aboutQtAct,&QAction::triggered,this,&MainWindow::aboutQt);

    leftAlignAct = new QAction(tr("&Left Align"),this);
    leftAlignAct->setCheckable(true);
    leftAlignAct->setShortcut(tr("Ctrl+L"));
    leftAlignAct->setStatusTip(tr("Left align the selected text"));
    connect(leftAlignAct,&QAction::triggered,this,&MainWindow::leftAlign);

    rightAlignAct = new QAction(tr("&Right Align"),this);
    rightAlignAct->setCheckable(true);
    rightAlignAct->setShortcut(tr("Ctrl+R"));
    rightAlignAct->setStatusTip(tr("Right align the selected text"));
    connect(rightAlignAct,&QAction::triggered,this,&MainWindow::rightAlign);

    justifyAct = new QAction(tr("&Justify"),this);
    justifyAct->setCheckable(true);
    justifyAct->setShortcut(tr("Ctrl+J"));
    justifyAct->setStatusTip(tr("Justify align the selected text"));
    connect(justifyAct,&QAction::triggered,this,&MainWindow::justify);

    centerAct = new QAction(tr("&Center"),this);
    centerAct->setCheckable(true);
    centerAct->setShortcut(tr("Ctrl+E"));
    centerAct->setStatusTip(tr("Center align the selected text"));
    connect(centerAct,&QAction::triggered,this,&MainWindow::center);

    alignmentGroup = new QActionGroup(this);
    alignmentGroup->addAction(leftAlignAct);
    alignmentGroup->addAction(rightAlignAct);
    alignmentGroup->addAction(justifyAct);
    alignmentGroup->addAction(centerAct);
    leftAlignAct->setChecked(true);

}
void MainWindow::createMenus()
{
    fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(newAct);
    fileMenu->addAction(openAct);
    fileMenu->addAction(saveAct);
    fileMenu->addAction(printAct);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);

    editMenu = menuBar()->addMenu(tr("&Edit"));
    editMenu->addAction(undoAct);
    editMenu->addAction(redoAct);
    editMenu->addSeparator();
    editMenu->addAction(cutAct);
    editMenu->addAction(copyAct);
    editMenu->addAction(pasteAct);
    editMenu->addSeparator();

    helpMenu = menuBar()->addMenu(tr("&Help"));
    helpMenu->addAction(aboutAct);
    helpMenu->addAction(aboutQtAct);


    formatMenu = menuBar()->addMenu(tr("&Format"));
    formatMenu->addAction(boldAct);
    formatMenu->addAction(italicAct);
    formatMenu->addSeparator()->setText(tr("Alignment"));
    formatMenu->addAction(leftAlignAct);
    formatMenu->addAction(rightAlignAct);
    formatMenu->addAction(justifyAct);
    formatMenu->addAction(centerAct);
    formatMenu->addSeparator();
    formatMenu->addAction(setLineSpacingAct);
    formatMenu->addAction(setParagraphSpacingAct);
}

9、QMenuBar类:菜单栏类,可以向其中添加qmenu和qaction,通常在qmainwindow中使用

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //两种添加方式
    //1、调用函数自动添加
    QMenuBar * bar1 = menuBar();
    bar1->addMenu(tr("自动添加的"));

    //2、先创建一个QMenuBar 再放进mainwindow中去 这回覆盖之前已经创建好的
    QMenuBar * bar2 = new QMenuBar;
    bar2->addMenu(tr("手动添加"));
    setMenuBar(bar2);
}

10、QSizeGrip类:一般位于顶级窗口(QMainWindow或QDialog)的右下角,它是QWidget的派生类,你可以放置到另一个QWidget的任何位置,通过它可以改变它所在顶级窗口的大小。如果要自己控制改变大小,则需要继承他然后实现他的虚函数即可

11、QStatusBar类: 状态栏用于显示各个界面上各种部件的提示信息,各个部件也自带了函数来设置状态栏要显示的内容。上面的例子都可以看到状态栏的用法。

12、QToolBar类: 窗口的工具栏可以放action和widget,这也是很常见的部件

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(300,300);
    //放置两个工具栏 并设置初始放置的位置
    QToolBar * bar1 = new QToolBar("toolBar1");
    addToolBar(Qt::TopToolBarArea,bar1);
    QToolBar * bar2 = new QToolBar("toolBar2");
    addToolBar(Qt::BottomToolBarArea, bar2);

    //设置是否可以移动
    bar1->setMovable(true);
    bar2->setMovable(true);
    //是否可以悬浮
    bar2->setFloatable(false);
    //设置所能移动的区域
    bar1->setAllowedAreas(Qt::AllToolBarAreas);
    bar2->setAllowedAreas(Qt::LeftToolBarArea|Qt::RightToolBarArea);

    //工具栏上添加action

    //设置工具栏上按钮显示风格
    bar1->setToolButtonStyle(Qt::ToolButtonIconOnly);
//    bar1->setToolButtonStyle(Qt::ToolButtonTextOnly);
//    bar1->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
//    bar1->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
//    bar1->setToolButtonStyle(Qt::ToolButtonFollowStyle);
    bar1->setIconSize(QSize(50,50));//置工具栏的图标最大尺寸
    QPixmap pixmap(QSize(30,30));
    pixmap.fill(Qt::red);
    bar1->addAction(QIcon(pixmap),"保存");

    bar1->addAction("新增");

    bar2->addAction("删除");
    bar2->addAction("修改");

    //还可以添加除qaction‘外的部件
    QComboBox *box1 = new QComboBox;
    box1->addItems(QStringList()<<"111"<<"222"<<"333"<<"444"<<"555");
    bar1->addWidget(box1);

    QComboBox *box2 = new QComboBox;
    box2->addItems(QStringList()<<"111"<<"222"<<"333"<<"444"<<"555");
    bar2->addWidget(box2);

}

二、桌面集成类(这个应该比较有意思)

1、QDesktopServices类:访问通用桌面服务的方法.3个静态函数,可直接使用,不用实例化类

1) openUrl(const QUrl &url):提供的url是由文件路径的构成的url。功能是:使用外部程序来打开文件

//下面两个方法是用来定制化处理openUrl的,意思是不使用系统提供的方法来处理,而是使用自己的方式来处理openUrl

2) setUrlHandler(const QString &scheme, QObject *receiver, const char *method)

3) unsetUrlHandler(const QString &scheme)

以下实例:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(300,300);
    //先注册自己的定制handler
    QDesktopServices::setUrlHandler("http",this,"openUrl");
    QDesktopServices::setUrlHandler("file",this,"openUrl");

    QPushButton *http = new QPushButton(this);
    http->setText(tr("打开http"));
    http->setGeometry(100,50,100,30);
    connect(http,&QPushButton::clicked,[]{
        QDesktopServices::openUrl(QUrl("http://www.baidu.com"));
    });

    QPushButton *file = new QPushButton(this);
    file->setText(tr("打开file"));
    file->setGeometry(100,150,100,30);
    connect(file,&QPushButton::clicked,[]{
        QDesktopServices::openUrl(QUrl("file:///F:/QtPractice/DesktopServices"));
    });
}

void MainWindow::openUrl(const QUrl &url)
{
    //获得url的头部
    QString scheme = url.scheme();
    if(scheme == "http"){
        QMessageBox::information(this,"提示",QString("这是一个网址需要进行处理:%1").arg(url.url()));
    }else if(scheme == "file"){
        QMessageBox::information(this,"提示",QString("这是文件路径需要进行处理%1").arg(url.url()));
    }
}

例子摘自:https://www.cnblogs.com/itrena/p/5938298.html

2、QDesktopWidget类:这个类提供关于用户桌面的信息,例如它的总大小、屏幕数量、每个屏幕的几何形状,以及它们是被配置为单独的桌面还是单个虚拟桌面。实例一般通过QApplication::desktop()来获取

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,300);
    //获得实例
    QDesktopWidget * desktop = QApplication::desktop();
    //函数使用
    //获得可用的屏幕大小 不包括dock、bar所占用的空间
    QRect availab = desktop->availableGeometry();
    qDebug() << "可用的屏幕大小:" << availab;

    qDebug() << "是否是虚拟桌面=" << desktop->isVirtualDesktop();

    qDebug() << "当前屏幕的索引:" << desktop->primaryScreen() << " 可用的屏幕大小:"
             << desktop->availableGeometry(desktop->primaryScreen());

    qDebug() << "屏幕数量:" << desktop->screenCount();

    //获得整个屏幕,包括dock bar
    QRect fullScreen = desktop->screenGeometry();
    qDebug() << "整个屏幕大小:" << fullScreen;

    //截屏操作
    QScreen *screen = QApplication::primaryScreen();
    QPixmap pixmap = screen->grabWindow(0);
    pixmap.save("F:/a.jpg");

}

3、QSystemTrayIcon类:为系统托盘中的应用程序提供了一个图标

MyWindow::MyWindow(QWidget *parent) : QWidget(parent)
{
    resize(300,200);
    QComboBox *comboBox = new QComboBox(this);

    comboBox->addItem(QIcon(":/images/bad.png"),"Bad");
    comboBox->addItem(QIcon(":/images/heart.png"),"Heart");
    comboBox->addItem(QIcon(":/images/trash.png"),"Trash");
    comboBox->setGeometry(100,50,100,20);

    QSystemTrayIcon *trayIcon = new QSystemTrayIcon(this);
    trayIcon->setIcon(comboBox->itemIcon(0));
    trayIcon->show();

    connect(comboBox,QOverload<int>::of(&QComboBox::currentIndexChanged),
            [=](int index){
        setWindowIcon(comboBox->itemIcon(index));
        trayIcon->setIcon(comboBox->itemIcon(index));
    });

    //还可以向托盘中添加菜单
    QMenu *menu = new QMenu;
    QPixmap pixmap(30,30);
    pixmap.fill(Qt::red);
    menu->addAction(pixmap,"退出");
    pixmap.fill(Qt::yellow);
    menu->addAction(pixmap,"显示最大化");
    pixmap.fill(Qt::blue);
    menu->addAction(pixmap,"显示正常大小");
    trayIcon->setContextMenu(menu);

    //还可以处理对托盘图标的点击事件
    connect(trayIcon,&QSystemTrayIcon::activated,
            [=](QSystemTrayIcon::ActivationReason reason){
        switch (reason) {
        case QSystemTrayIcon::Trigger:
        case QSystemTrayIcon::DoubleClick:
            QMessageBox::information(this,"ActivationReason","Trigger/DoubleClick");
            break;
        case QSystemTrayIcon::MiddleClick:
            QMessageBox::information(this,"ActivationReason","MiddleClick");
            break;
        default:
            break;
        }
    });

    //托盘还可以弹出消息
    trayIcon->showMessage("showMessage","我是弹出的消息内容",QSystemTrayIcon::Information,
                          1000);//这个事件表示显示多长时间(会受平台的影响,有时候会无效)
    //还可以处理对弹出的消息进行点击时的处理
    connect(trayIcon,&QSystemTrayIcon::messageClicked,[=]{
        QMessageBox::information(this,"messageClicked","对弹出的消息进行点击了");
    });
}

三、对话框类:对话框可以是模态的,在这种情况下,用户需要提供必要的信息,然后才能在主窗口中继续工作,或者是非模态的。非模型对话框不会阻止用户与应用程序中的任何其他窗口进行交互;Qt为文件、字体、颜色选择等提供了一组现成的对话框。

1、QColorDialog类:Qt已经封装的很好了直接用

ColorDialog::ColorDialog(QWidget *parent) : QWidget(parent)
{
    resize(200,100);
    QPushButton *btn = new QPushButton(this);
    btn->setText("选择颜色");

    connect(btn,&QPushButton::clicked,[=]{
        //两种方式打开颜色对话框

        //第一种 实例化一个标准颜色对话框,这个是和平台无关的界面
        QColorDialog *colorDialog = new QColorDialog(this);
        colorDialog->show();

        connect(colorDialog,&QColorDialog::colorSelected,
                [=](const QColor &color){
            qDebug() << color.name();
        });

        //第二种是调用静态函数直接打开对话框选择颜色,这个是和平台有关
//        QColor color = QColorDialog::getColor();
//        qDebug() << color.name();
    });
}

2、QFileDialog类:Qt已经封装的很好了直接用

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(200,100);
    QPushButton *btn = new QPushButton("选择文件",this);
    connect(btn,&QPushButton::clicked,[=]{
        //两种打开方式
        //1、通过静态函数
        //返回用户选择的现有目录的绝对路径。
//        QString path = QFileDialog::getExistingDirectory(this,"getExistingDirectory","F:/",QFileDialog::ShowDirsOnly);
//        qDebug() << "getExistingDirectory:" << path;
        //返回用户选择的现有目录的URL。由于是url 所以支持访问远程的路径
//        QUrl dirUrl = QUrl::fromLocalFile("F:/");
//        qDebug() << dirUrl.url();
//        QUrl url = QFileDialog::getExistingDirectoryUrl(this,"getExistingDirectoryUrl",dirUrl,QFileDialog::ShowDirsOnly);
//        qDebug() << url.url();
        //返回用户选择的现有的单个文件的绝对路径
//        QString path = QFileDialog::getOpenFileName(this,"getOpenFileName","F:/",
//                "Image (*.png *.xpm *.jpg);;Text files (*.txt *.xlsx *.xls);;XML files (*.xml)");
//        qDebug() << path;
        //返回用户选择的现有的多个文件的绝对路径
//        QStringList paths = QFileDialog::getOpenFileNames(this,"getOpenFileNames","f:/",
//                     "Text files (*.txt *.xlsx)");
//        qDebug() << paths;
        //返回用户选择的现有的单个文件的Url
//        QUrl dirUrl = QUrl::fromLocalFile("F:/");
//        QUrl url = QFileDialog::getOpenFileUrl(this,"getOpenFileUrl",dirUrl,"");
//        qDebug() << url.url();
        //返回用户选择的现有的多个文件的Url
//        QUrl dirUrl = QUrl::fromLocalFile("f:/");
//        QList<QUrl> urls = QFileDialog::getOpenFileUrls(this,"getOpenFileUrls",dirUrl,"");
//        foreach (QUrl url, urls) {
//            qDebug() << url.url();
//        }
        //返回用户选择的单个文件的绝对路径 这个文件可以不存在,得到路径后就可以向这个文件中写入数据
//        QString path = QFileDialog::getSaveFileName(this, tr("getSaveFileName"),
//                                   "f:/",tr("Images (*.png *.xpm *.jpg)"));
//        qDebug() << path;
//        QFile file(path);
//        if(file.open(QIODevice::WriteOnly)){
//            file.write("hello world!");
//            file.close();
//        }else{
//            qDebug() << "文件打开失败!";
//        }
        //返回用户选择的单个文件的绝对路径 这个文件可以不存在,得到路径后转换成正常的路径就可以向这个文件中写入数据
//        QUrl url = QFileDialog::getSaveFileUrl(this,tr("getSaveFileUrl"),QUrl::fromLocalFile("f:/"));
//        QString path = url.toLocalFile();
//        qDebug() << path;
//        QFile file(path);
//        if(file.open(QIODevice::WriteOnly)){
//            file.write("hello world!");
//            file.close();
//        }else{
//            qDebug() << "文件打开失败!";
//        }
        //2、通过实例化对象
        QFileDialog *fileDialog = new QFileDialog(this);
        //先设置下这个文件对话框要返回什么
//        fileDialog->setFileMode(QFileDialog::AnyFile);//任何一个文件, 包括不存在的
        fileDialog->setFileMode(QFileDialog::ExistingFile);//单个存在的文件
//        fileDialog->setFileMode(QFileDialog::ExistingFiles);//多个存在的文件

//        fileDialog->setFileMode(QFileDialog::Directory);//单个文件夹

//        fileDialog->setOptions(QFileDialog::ShowDirsOnly);//貌似没用

        //设置展示的样式 -- 貌似没用
//        fileDialog->setViewMode(QFileDialog::Detail);
//        fileDialog->setViewMode(QFileDialog::List);

        fileDialog->setAcceptMode(QFileDialog::AcceptOpen);//qt默认
//        fileDialog->setAcceptMode(QFileDialog::AcceptSave);

        fileDialog->show();

        connect(fileDialog,&QFileDialog::fileSelected,
                [](const QString &file){
            qDebug() << file;
        });

        connect(fileDialog,&QFileDialog::filesSelected,
                [](const QStringList &files){
            qDebug() << files;
        });

    });
}

3、QFontDialog类:字体对话框类

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(200,100);

    QLabel *label = new QLabel("Hello World");
    label->setAlignment(Qt::AlignCenter);
    setCentralWidget(label);


    QPushButton *btn = new QPushButton("选择字体",this);
    connect(btn,&QPushButton::clicked,[=]{
        bool ok = false;
        QFont font = QFontDialog::getFont(&ok,label->font(),this,"getFont");
        if(ok){
            label->setFont(font);
        }else{
            qDebug() << "未选取颜色";
        }
    });
}

4、QInputDialog类:简单方便的对话框,从用户获取输入的值,可以作为弹出框使用

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(300,200);
    QButtonGroup *btnGroup = new QButtonGroup(this);

    QGroupBox *box = new QGroupBox("测试输入框");
    QVBoxLayout *layout = new QVBoxLayout;

    QPushButton *doubleBtn = new QPushButton("获取double值:");
    QPushButton *intBtn = new QPushButton("获取int值:");
    QPushButton *itemBtn = new QPushButton("获取item值:");
    QPushButton *multiLineTextBtn = new QPushButton("获取multiLineText值:");
    QPushButton *textBtn = new QPushButton("获取text:");

    btnGroup->addButton(doubleBtn,Double);
    btnGroup->addButton(intBtn,Int);
    btnGroup->addButton(itemBtn,Item);
    btnGroup->addButton(multiLineTextBtn,MultiLineText);
    btnGroup->addButton(textBtn,Text);

    layout->addWidget(doubleBtn);
    layout->addWidget(intBtn);
    layout->addWidget(itemBtn);
    layout->addWidget(multiLineTextBtn);
    layout->addWidget(textBtn);
    box->setLayout(layout);


    connect(btnGroup,QOverload<int>::of(&QButtonGroup::buttonClicked),
            [=](int id){
        bool ok = false;
        switch (id) {
        case Double:
        {
            qDebug() << "Double";
            double value = QInputDialog::getDouble(this,"getDouble","请输入:",88,0,100,
                                                   3,//支持的小数点的位数
                                                   &ok);
            if(ok){
                doubleBtn->setText(doubleBtn->text()+ QString("\n%1").arg(value));
            }
        }
            break;
        case Int:
        {
            qDebug() << "Int";
            int value = QInputDialog::getInt(this,"getInt","请输入:",88,0,100,5,&ok);
            if(ok)
                intBtn->setText(intBtn->text()+QString("\n%1").arg(value));
        }
            break;
        case Item:
        {
            qDebug() << "Item";
            QString value = QInputDialog::getItem(this,"getItem","请输入:",QStringList()<<"111"<<"222"<<"333",1,true,&ok,
                                                  Qt::WindowFlags(),
                                                  Qt::ImhNone);//最后一个属性是对输入键盘的显示的控制,貌似没用
            if(ok)
                itemBtn->setText(itemBtn->text()+"\n"+value);
        }
            break;
        case MultiLineText:
        {
            qDebug() << "MultiLineText";
            QString value = QInputDialog::getMultiLineText(this,"getMultiLineText","请输入:","11223344",&ok);
            if(ok)
                multiLineTextBtn->setText(multiLineTextBtn->text()+"\n"+value);
        }
            break;
        case Text:
        {
            qDebug() << "Text";
            QString value = QInputDialog::getText(this,"getText","请输入:",
                                                  QLineEdit::Password,//设置文本编辑的显示方式 显示为密码形式
                                                  "11223344",&ok);
            if(ok)
                textBtn->setText(textBtn->text()+"\n"+value);
        }
            break;
        default:
            break;
        }
    });

    setCentralWidget(box);
}

5、QMessageBox类:弹出的消息提示框

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(300,200);
    QButtonGroup *btnGroup = new QButtonGroup(this);

    QGroupBox *box = new QGroupBox("消息框");
    QVBoxLayout *layout = new QVBoxLayout;

    QPushButton *btn1 = new QPushButton("about:");
    QPushButton *btn2 = new QPushButton("aboutQt:");
    QPushButton *btn3 = new QPushButton("critical:");
    QPushButton *btn4 = new QPushButton("information:");
    QPushButton *btn5 = new QPushButton("question:");
    QPushButton *btn6 = new QPushButton("warning:");

    btnGroup->addButton(btn1,about);
    btnGroup->addButton(btn2,aboutQt);
    btnGroup->addButton(btn3,critical);
    btnGroup->addButton(btn4,information);
    btnGroup->addButton(btn5,question);
    btnGroup->addButton(btn6,warning);

    layout->addWidget(btn1);
    layout->addWidget(btn2);
    layout->addWidget(btn3);
    layout->addWidget(btn4);
    layout->addWidget(btn5);
    layout->addWidget(btn6);

    box->setLayout(layout);


    connect(btnGroup,QOverload<int>::of(&QButtonGroup::buttonClicked),
            [=](int id){
        switch (id) {
        case about:
            QMessageBox::about(this,"about","关于");
            break;
        case aboutQt:
            QMessageBox::aboutQt(this,"aboutQt");
            break;
        case critical:
//            QMessageBox::critical(this,"critical","严重",QMessageBox::Ok,QMessageBox::Cancel,QMessageBox::Yes);
            QMessageBox::critical(this,"critical","严重",tr("确定"),tr("取消"));
            break;
        case information:
//            QMessageBox::information(this,"information","信息",QMessageBox::Ok,QMessageBox::Cancel);
            QMessageBox::information(this,"information","信息",tr("确定"),tr("取消"));
            break;
        case question:
//            QMessageBox::question(this,"question","问题",QMessageBox::Ok,QMessageBox::Cancel);
            QMessageBox::question(this,"question","问题",tr("确定"),tr("取消"));
            break;
        case warning:
//            QMessageBox::warning(this,"warning","警告",QMessageBox::Ok,QMessageBox::Cancel);
            QMessageBox::warning(this,"warning","警告",tr("确定"),tr("取消"));
            break;
        default:
            break;
        }
    });

    setCentralWidget(box);
}

6、QProgressDialog类:进度对话框,比如进行耗时操作的时候可以弹出来提示用户,有两种使用QProgressDialog的方式:模态和非模态。注意:setValue和Value不要随便调用,会导致对话框无法正常关闭

Widget::Widget(QWidget *parent)
    : QWidget(parent),progress(nullptr),timer(nullptr),steps(0)
{
    resize(300,200);
    QVBoxLayout *layout = new QVBoxLayout;

    QPushButton *btn = new QPushButton("下载(模态)");
    btn->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);//固定长宽

    connect(btn,&QPushButton::clicked,[=]{
        QProgressDialog progress("拷贝文件...", "取消", 0, 500000, this);
        progress.setWindowModality(Qt::WindowModal);
        progress.setAutoReset(false);
        progress.show();
        QApplication::processEvents();

        for (int i = 0; i < 500000; i++) {
            progress.setValue(i);

            if (progress.wasCanceled())
                break;
            //... copy one file
        }
        progress.setValue(500000);
    });

    QPushButton *btn1 = new QPushButton("下载(非模态)");
    btn1->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);//固定长宽

    connect(btn1,&QPushButton::clicked,[=]{
        progress = new QProgressDialog("拷贝文件...", "取消", 0, 5000);
        progress->setWindowModality(Qt::NonModal);

        timer = new QTimer(this);
        connect(timer,&QTimer::timeout,this,&Widget::timeOut);

        progress->show();
        QApplication::processEvents();
        timer->start(2);

        connect(progress,&QProgressDialog::canceled,this,&Widget::canceled);
    });

    layout->addWidget(btn,Qt::AlignHCenter);
    layout->setSpacing(20);
    layout->addWidget(btn1,Qt::AlignHCenter);

    setLayout(layout);
}

Widget::~Widget()
{

}

void Widget::canceled()
{
    if(timer != nullptr){
        timer->stop();
        delete timer;
        timer = nullptr;
    }
    steps = 0;
}

void Widget::timeOut()
{
    progress->setValue(steps);
    steps++;
    if (steps > progress->maximum())
        canceled();
}

7、QDialog类:对话框窗口的基类,一般继承它实现自己的自定义对话框

FindDialog::FindDialog(QWidget *parent)
    :QDialog(parent)
{
    //一个输入框
    label = new QLabel(tr("Find &What:"));
    lineEdit = new QLineEdit;
    label->setBuddy(lineEdit);//当用户按下此标签指示的快捷键时,键盘焦点转移到标签的buddy小部件
    //即  当按下Alt+W时 焦点会到编辑框里

    //查询的条件
    caseCheckBox = new QCheckBox(tr("Match &case"));
    fromStartCheckBox = new QCheckBox(tr("Search from &start"));
    fromStartCheckBox->setChecked(true);

    //查询点击按钮
    findButton = new QPushButton(tr("&Find"));
    findButton->setDefault(true);//默认获取ennter键的响应

    closeButton = new QPushButton(tr("&Close"));

    //更多按钮用来显示extension
    moreButton = new QPushButton(tr("&More"));
    moreButton->setCheckable(true);
    moreButton->setAutoDefault(false);//此属性保存按钮是否是自动默认按钮

    extension = new QWidget;//扩展界面

    wholeWordsCheckBox = new QCheckBox(tr("&Whole words"));
    backwardCheckBox = new QCheckBox(tr("Search &backward"));
    searchSelectionCheckBox = new QCheckBox(tr("Search se&lection"));


    buttonBox = new QDialogButtonBox(Qt::Vertical);//垂直方向
    //QDialogButtonBox允许开发人员向其添加按钮,
    //并将自动为用户的桌面环境使用适当的布局水平方向上的按钮框
    //添加按钮
    buttonBox->addButton(findButton,QDialogButtonBox::ActionRole);
    buttonBox->addButton(closeButton,QDialogButtonBox::ActionRole);
    buttonBox->addButton(moreButton,QDialogButtonBox::ActionRole);

    connect(moreButton,&QPushButton::toggled,extension,&QWidget::setVisible);
    //布局扩展界面
    QVBoxLayout *extensionLayout = new QVBoxLayout;
    extensionLayout->setMargin(0);//设置布局与外部的间距
    extensionLayout->addWidget(wholeWordsCheckBox);
    extensionLayout->addWidget(backwardCheckBox);
    extensionLayout->addWidget(searchSelectionCheckBox);
    extension->setLayout(extensionLayout);

    //编辑框在左上角
    QHBoxLayout *topLeftLayout = new QHBoxLayout;
    topLeftLayout->addWidget(label);
    topLeftLayout->addWidget(lineEdit);

    //左边的布局
    QVBoxLayout *leftLayout = new QVBoxLayout;
    leftLayout->addLayout(topLeftLayout);
    leftLayout->addWidget(caseCheckBox);
    leftLayout->addWidget(fromStartCheckBox);

    //整体进行网格布局
    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->setSizeConstraint(QLayout::SetFixedSize);//布局为固定大小,不会调整
    mainLayout->addLayout(leftLayout,0,0);//0行0列
    mainLayout->addWidget(buttonBox,0,1);//0行1列
    mainLayout->addWidget(extension,1,0,1,2);//第1行占2列
    mainLayout->setRowStretch(2,1);//底下留点空
    //拉伸系数与网格中的其他行相关。具有较高拉伸系数的行占用更多的可用空间。

    setLayout(mainLayout);

    setWindowTitle(tr("Extension"));
    extension->hide();
}

8、QDialogButtonBox类:理解这个类的关键在于addButton(const QString &text, ButtonRole role),这里面role代表这个添加的这个button属于什么角色,那么这个类中的每个按钮都有自己的角色,那么在通过button(StandardButton which) const就可以返回我所需要的button了。

借鉴别人的理解:这个控件代表一个包含很多按钮的盒子。今天我在读代码的时候,发现别人在实现的时候使用了这个控件。我一开始非常不理解,感觉每一个按钮都单独的使用多好,这样就可以单独控制每一个按钮,特别是我需要实现的功能就是需要首先得到其中一个按钮的指针。最后,通过看Qt说明文档,我发现可以通过方法button(StandardButton)来返回对应的QPushButton,其中标准按钮可以参考说明文档,我使用的是QDialogButtonBox::Ok。在得到这个按钮后,我就可以随意操作它了。 后来我又思考了一下,有时候,当按钮比较多的时候,分组确实是一个很好地方法来进行管理,以后多多使用。

四、布局管理类:所有QWidget子类都可以使用布局来管理它们的子类。函数的作用是::setLayout()将布局应用于小部件。当以这种方式在小部件上设置布局时,它将负责以下任务:放置子部件、感知系统默认的大小、感知系统最小大小、调整布局、实时根据内容的变化而变化(满足的条件:子部件的字体大小、文本或其他内容发生变化,子部件隐藏或者显示,移除了子部件)。

1、QGraphicsAnchor:表示QGraphicsAnchorLayout中的两个项目之间的锚,主要是结合QGraphicsAnchorLayout进行使用

2、QGraphicsAnchorLayout:QGraphicsAnchorLayout类提供了一种布局,其中可以将widget锚定在图形视图中。锚布局允许开发人员指定小部件之间以及与布局本身的相对位置。该规范是通过调用addAnchor()、addAnchors()或addCornerAnchors()在布局中添加锚来制定的。要使用这个类,必须结合:

QGraphicsView :负责显示QGraphicsScene

QGraphicsScene :表示一个场景,在这个场景里我们可以放置我们显示的部件,但是它本身是无法显示这些部件的,要显示需要找QGraphicsView 。

QGraphicsItem及其子类QGraphicsEllipseItem、QGraphicsLineItem、QGraphicsPathItem、QGraphicsPixmapItem、QGraphicsPolygonItem、QGraphicsRectItem、QGraphicsSimpleTextItem、QGraphicsTextItem以及自己继承定义的:这些部件就是放进QGraphicsScene的部件

QGraphicsProxyWidget:如果想用Qt传统的widget怎么办,这个类就是用来帮助在QGraphicsScene嵌入widget的,方便好用。

QGraphicsWidget:支持setLayout

#include "mainwindow.h"
#include <QApplication>
#include <QGraphicsAnchorLayout>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QGraphicsProxyWidget>
#include <QPushButton>
#include <QGraphicsEllipseItem>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
//    MainWindow w;
//    w.show();

    QGraphicsView view;
    view.resize(300,200);
    QGraphicsScene scene;
    QGraphicsWidget *ws = new QGraphicsWidget;
    QGraphicsAnchorLayout *layout = new QGraphicsAnchorLayout;
    layout->setSpacing(0);

    QGraphicsProxyWidget *w1 = new QGraphicsProxyWidget;
    w1->setWidget(new QPushButton("传统部件1"));
    QGraphicsProxyWidget *w2 = new QGraphicsProxyWidget;
    w2->setWidget(new QPushButton("传统部件2"));
    QGraphicsProxyWidget *w3 = new QGraphicsProxyWidget;
    w3->setWidget(new QPushButton("传统部件3"));

    layout->addCornerAnchors(w1, Qt::TopLeftCorner, layout, Qt::TopLeftCorner);
    layout->addAnchor(w2, Qt::AnchorLeft, w1, Qt::AnchorRight);
    layout->addAnchor(w2, Qt::AnchorTop, w1, Qt::AnchorBottom);
    layout->addAnchor(w3,Qt::AnchorHorizontalCenter,w1,Qt::AnchorHorizontalCenter);
    layout->addAnchor(w3,Qt::AnchorTop,w1,Qt::AnchorBottom);

    ws->setLayout(layout);
    scene.addItem(ws);

//    QGraphicsEllipseItem *ellipseItem = new QGraphicsEllipseItem;
//    ellipseItem->setRect(QRect(0,0,100,100));
//    scene.addItem(ellipseItem);

//    QGraphicsSimpleTextItem *simpleTextItem = new QGraphicsSimpleTextItem("hello Qt!");
//    scene.addItem(simpleTextItem);

    view.setScene(&scene);
    view.show();

    return a.exec();
}

3、QBoxLayout:水平布局和垂直布局的基类4、QHBoxLayout:水平布局5、QVBoxLayout:垂直布局

三种填充布局的方法:

addWidget:向布局中添加部件,并设置拉伸因子,及对齐方式

addSpacing:填充间距;这是您用来创建漂亮而宽敞的对话框的功能之一

addStretch:增加一个可伸缩的间距,通过设置拉伸因子实现

addLayout:嵌套添加布局

setContentsMargins:设置小部件每一侧外部边框的宽度。这是沿QBoxLayout的四个边的每个保留空间的宽度

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,200);
    //创建三个组选框
    QGroupBox *groupBox1 = new QGroupBox("QBoxLayout");
    QBoxLayout *baseLayout = new QBoxLayout(QBoxLayout::LeftToRight);
    for(int i = 0; i < 5; ++i){
        baseLayout->addWidget(new QPushButton(QString("btn%1").arg(i)),i);
    }
    QPushButton *changeDirection = new QPushButton("changeDirection");
    connect(changeDirection,&QPushButton::clicked,[=]{
        if(baseLayout->direction() == QBoxLayout::RightToLeft)
            baseLayout->setDirection(QBoxLayout::LeftToRight);
        else
            baseLayout->setDirection(QBoxLayout::RightToLeft);
    });
    baseLayout->addWidget(changeDirection);
    groupBox1->setLayout(baseLayout);

    QGroupBox *groupBox2 = new QGroupBox("QVBoxLayout");
    QVBoxLayout *verLayout = new QVBoxLayout;
    for(int i = 0; i < 2; ++i){
        verLayout->addStretch();
        verLayout->addWidget(new QPushButton(QString("btn%1").arg(i)));
    }
    QPushButton *hideBtn = new QPushButton("点击隐藏");
    hideBtn->setCheckable(true);
    QPushButton *testBtn = new QPushButton("测试按钮");
    connect(hideBtn,&QPushButton::toggled,testBtn,&QPushButton::setVisible);
    verLayout->addWidget(hideBtn);
    verLayout->insertWidget(2,testBtn);//注意 这个index是从1开始的
    groupBox2->setLayout(verLayout);

    QGroupBox *groupBox3 = new QGroupBox("QHBoxLayout");
    QHBoxLayout *horLayout = new QHBoxLayout;
    for(int i = 0; i < 6; ++i){
        horLayout->addSpacing(50);
        horLayout->addWidget(new QPushButton(QString("btn%1").arg(i)));
    }
    QPushButton *removeBtn = new QPushButton("点击删除我");
    connect(removeBtn,&QPushButton::clicked,[=]{
        horLayout->removeWidget(removeBtn);//只是从布局中删除,内存并未删除 而且会一直显示 除非调用hide隐藏掉
        removeBtn->hide();
    });
    horLayout->addWidget(removeBtn);
    groupBox3->setLayout(horLayout);
    //垂直从上到下布局
    QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom);
    //拉伸比例1:2:3
    mainLayout->addWidget(groupBox1,1);
    mainLayout->addWidget(groupBox2,2);
    mainLayout->addWidget(groupBox3,3);
    setLayout(mainLayout);
}

6、QFormLayout:表单布局,一组2个部件的组合,类似于QQ登录时界面

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QFormLayout *layout = new QFormLayout;
    layout->addRow("编辑:",new QLineEdit("请输入..."));
    layout->addRow("选择1:",new QCheckBox("是否"));
    layout->addRow("选择2:",new QSpinBox);

    //重新设置表单样式
    layout->setRowWrapPolicy(QFormLayout::DontWrapRows);//不换行排列
    layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);//保持默认尺寸
    layout->setFormAlignment(Qt::AlignLeft|Qt::AlignVCenter);//始终靠左水平居中
    layout->setLabelAlignment(Qt::AlignRight);//文本靠右居中

    layout->setHorizontalSpacing(50);//水平方向上间距
    layout->setVerticalSpacing(100);//垂直方向上的间距

    layout->insertRow(1,new QLineEdit("新插入的"));//从0开始的
    setLayout(layout);
}

7、QGridLayout:网格布局

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    createMenu();
    createHorizontalGroupBox();
    createGridGroupBox();
    createFormGroupBox();

    //多行编辑
    bigEditor = new QTextEdit;
    bigEditor->setPlainText(tr("This widget takes up all the remaining space "
                               "in the top-level layout."));

    //对话框按钮组合
    buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|
                                     QDialogButtonBox::Cancel);

    connect(buttonBox,&QDialogButtonBox::accepted,[=]{
        qDebug() << "QDialogButtonBox accepted";
    });
    connect(buttonBox,&QDialogButtonBox::rejected,[=]{
        qDebug() << "QDialogButtonBox rejected";
    });

    //最后全部放入一个垂直布局
    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->setMenuBar(menuBar);
    mainLayout->addWidget(horizontalGroupBox);
    mainLayout->addWidget(gridGroupBox);
    mainLayout->addWidget(formGroupBox);
    mainLayout->addWidget(bigEditor);
    mainLayout->addWidget(buttonBox,0,Qt::AlignCenter);
    setLayout(mainLayout);
}

Widget::~Widget()
{

}

void Widget::createMenu()
{
    //创建文件菜单 里面添加一个退出的动作,关联主窗口的退出
    menuBar = new QMenuBar;
    fileMenu = new QMenu(tr("&File"),this);
    exitAction = fileMenu->addAction(tr("E&xit"));
    menuBar->addMenu(fileMenu);
    connect(exitAction,&QAction::triggered,this,&Widget::close);
}

void Widget::createHorizontalGroupBox()
{
    //创建一个组选框
    horizontalGroupBox = new QGroupBox(tr("Horizontal layout"));
    QHBoxLayout *layout = new QHBoxLayout;//水平布局

    //向水平布局中添加按钮
    for(int i = 0; i < NumButtons; ++i){
        buttons[i] = new QPushButton(tr("Button %1").arg(i+1));
        layout->addWidget(buttons[i]);
    }
    //组选框放入布局
    horizontalGroupBox->setLayout(layout);
}

void Widget::createGridGroupBox()
{
    //创建组选框
    gridGroupBox = new QGroupBox(tr("Grid layout"));
    QGridLayout *layout = new QGridLayout;//网格布局

    for(int i = 0; i < NumGridRows; ++i){
        //一个文本
        labels[i] = new QLabel(tr("line %1:").arg(i+1));
        //一个编辑框
        lineEdits[i] = new QLineEdit;
        //从第一行开始放
        //文本放第0列
        layout->addWidget(labels[i],i+1,0);
        //编辑框放第1列
        layout->addWidget(lineEdits[i],i+1,1);
    }
    //一个多行编辑框
    smallEditor = new QTextEdit;
    smallEditor->setPlainText(tr("This widget takes up about two thirds of the "
                                 "grid layout."));
    //放在第1行第2列,占据4行1列的空间
    layout->addWidget(smallEditor,0,2,4,1);
    //设置某列的拉伸系数
    layout->setColumnStretch(1,10);
    layout->setColumnStretch(2,20);

    gridGroupBox->setLayout(layout);
}

void Widget::createFormGroupBox()
{
    //创建组选框
    formGroupBox = new QGroupBox(tr("Form layout"));
    //表单布局
    QFormLayout *layout = new QFormLayout;
    //添加每一行 和QGridLayout比确实方便快捷
    layout->addRow(new QLabel(tr("Line 1:")),new QLineEdit);
    layout->addRow(new QLabel(tr("Line 2, long text:")),new QComboBox);
    layout->addRow(new QLabel(tr("Line 3:")),new QSpinBox);
    formGroupBox->setLayout(layout);
}

8、QLayout:常用布局类的基类,一般用来继承它实现自定义布局。

要创建自己的布局管理器,可以实现addItem()、sizeHint()、setGeometry()、itemAt()和takeAt()函数。您还应该实现minimumSize(),以确保如果空间太少,布局不会被调整为零大小。为了支持高度依赖于宽度的子控件,实现hasHeightForWidth()和heightForWidth()。下面是qt自带的example,我没看太明白

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,200);
    FlowLayout *flowLayout = new FlowLayout;
    flowLayout->addWidget(new QPushButton(tr("Short")));
    flowLayout->addWidget(new QPushButton(tr("Longer")));
    flowLayout->addWidget(new QPushButton(tr("Different text")));
    flowLayout->addWidget(new QPushButton(tr("More text")));
    flowLayout->addWidget(new QPushButton(tr("Even longer button text")));
    setLayout(flowLayout);
    setWindowTitle(tr("Flow Layout"));
}
class FlowLayout : public QLayout
{
public:
    explicit FlowLayout(QWidget *parent,int margin = -1,int hSpacing = -1,int vSpacing = -1);
    explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
    ~FlowLayout();

    int horizontalSpacing()const;
    int verticalSpacing()const;

    //实现虚函数
    void addItem(QLayoutItem * item) override;
    Qt::Orientations expandingDirections() const override;
    bool hasHeightForWidth() const override;
    int heightForWidth(int width) const override;
    int count() const override;
    QLayoutItem *itemAt(int index) const override;
    QSize minimumSize() const override;
    void setGeometry(const QRect & rect) override;
    QSize sizeHint() const override;
    QLayoutItem *takeAt(int index) override;

private:
    int doLayout(const QRect &rect,bool testOnly) const;
    int smartSpacing(QStyle::PixelMetric pm) const;

    QList<QLayoutItem*> itemList;
    int m_hSapce;
    int m_vSpace;
};
FlowLayout::FlowLayout(QWidget *parent,int margin,int hSpacing,int vSpacing)
    :QLayout(parent),m_hSapce(hSpacing),m_vSpace(vSpacing)
{
    //设置布局与外部空间的间距
    setContentsMargins(margin,margin,margin,margin);
}

FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
    :m_hSapce(hSpacing),m_vSpace(vSpacing)
{
    setContentsMargins(margin,margin,margin,margin);
}

FlowLayout::~FlowLayout()
{
    while (itemList.size() > 0) {
        delete itemList.takeFirst();
    }
}

int FlowLayout::horizontalSpacing()const
{
    if(m_hSapce >= 0)
        return m_hSapce;
    else
        return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);//返回自动计算的间距大小
}

int FlowLayout::verticalSpacing()const
{
    if(m_vSpace >= 0)
        return m_vSpace;
    else
        return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}

void FlowLayout::addItem(QLayoutItem * item)
{
    itemList << item;
}

Qt::Orientations FlowLayout::expandingDirections() const
{
    return 0;
}
bool FlowLayout::hasHeightForWidth() const
{
    return true;//要求布局的首选高度取决于其宽度
}
int FlowLayout::heightForWidth(int width) const
{
    //根据宽度来计算高度
    int height = doLayout(QRect(0,0,width,0),true);
    return height;
}
int FlowLayout::count() const
{
    return itemList.count();
}
QLayoutItem *FlowLayout::itemAt(int index) const
{
    if(index >= 0 && index < itemList.size())
        return itemList.value(index);
    else
        return nullptr;
}
QSize FlowLayout::minimumSize() const
{
    //获得布局中所有子控件中的最大的最小尺寸的值
    QSize size;
    QLayoutItem *item;
    foreach (item, itemList) {
        size = size.expandedTo(item->minimumSize());
    }
    size += QSize(2*margin(),2*margin());
    return size;
}
void FlowLayout::setGeometry(const QRect & rect)
{
    QLayout::setGeometry(rect);
    doLayout(rect,false);
}
QSize FlowLayout::sizeHint() const
{
    return minimumSize();
}
QLayoutItem *FlowLayout::takeAt(int index)
{
    if(index >= 0 && index < itemList.size())
        return itemList.takeAt(index);
    else
        return nullptr;
}

int FlowLayout::doLayout(const QRect &rect,bool testOnly) const
{
    //这里是实现flow布局的核心
    //如果horizontalspaces()或verticalspaces()不返回默认值,
    //doLayout()将处理布局。
    //它使用getContentsMargins()来计算布局项的可用区域
    //然后根据当前样式为布局中的每个小部件设置适当的间距
    /*
     * 然后,通过将项目的宽度和行高添加到初始的x和y坐标,
     * 计算布局中每个项目的位置。这样我们就可以知道下一个项目是否适合当前行,
     * 或者是否必须向下移动到下一个项目。我们还可以根据小部件的高度找到当前行的高度。
     */

    int leftMargin,topMargin,rightMargin,bottomMargin;
    //获得布局与外部空间的间距
    getContentsMargins(&leftMargin,&topMargin,&rightMargin,&bottomMargin);

    //将rect调整下,加上外部的间距大小
    QRect effctiveRect = rect.adjusted(+leftMargin,+topMargin,-rightMargin,-bottomMargin);
    //得到调整后的x,y坐标
    int x = effctiveRect.x();
    int y = effctiveRect.y();
    int lineHeight = 0;

    QLayoutItem *item;
    foreach (item, itemList) {
        QWidget *wid = item->widget();
        int spaceX = horizontalSpacing();
        if(spaceX == -1){
            /*
             * 返回布局中控件l1和控件2之间应该使用的间距。
             * 方向指定控件是并排放置还是垂直堆叠。
             * 选项参数可用于传递关于父小部件的额外信息。
             * 小部件参数是可选的,如果选项为0也可以使用。
             */
            spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton,
                                                 QSizePolicy::PushButton,
                                                 Qt::Horizontal);
        }
        int spaceY = verticalSpacing();
        if(spaceY == -1){
            spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton,
                                                 QSizePolicy::PushButton,
                                                 Qt::Vertical);
        }
        //获得下一个子部件的x坐标
        int nextX = x+item->sizeHint().width()+ spaceX;
        if(nextX-spaceY > effctiveRect.right() && lineHeight > 0){
            //如果超出了可用区域的范围即右上角x坐标值 并且总行高>0
            //计算出下一行的坐标值
            x = effctiveRect.x();
            y = y + lineHeight + spaceY;
            nextX = x + item->sizeHint().width() + spaceY;
            lineHeight = 0;
        }
        if(!testOnly)//设置该item的布局
            item->setGeometry(QRect(QPoint(x,y),item->sizeHint()));

        //为下一行计算准备数据
        x = nextX;
        lineHeight = qMax(lineHeight,item->sizeHint().height());
    }
    //最后返回该布局的高度
    return y + lineHeight - rect.y() + bottomMargin;
}
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
    QObject *parent = this->parent();
    if(!parent)
        return -1;
    else if(parent->isWidgetType()){
        QWidget *pw = static_cast<QWidget*>(parent);
        return pw->style()->pixelMetric(pm,0,pw);//当父控件是QWidget时,顶级布局的默认间距将通过查询样式来确定
    }else{
        return static_cast<QLayout*>(parent)->spacing();//当父节点是QLayout时,子控件的默认间距将通过查询父节点布局的间距来确定
    }
}

9、QLayoutItem:QLayout操作的抽象项,这是自定义布局所使用的.

10、QSpacerItem:这个类主要用来创建一个空白项目,以使布局看起来更合理

11、QWidgetItem:其主要作用是可以根据QWidget部件产生一个可由布局管理的QWidgetItem对象

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    CustomLayout *layout = new CustomLayout;

    QPushButton *pb=new QPushButton("AAA");     QPushButton *pb1=new QPushButton("BBB");
    QPushButton *pb2=new QPushButton("CCC");    QPushButton *pb3=new QPushButton("DDD");
    QPushButton *pb4=new QPushButton("DDD");    QPushButton *pb5=new QPushButton("DDD");
    QPushButton *pb6=new QPushButton("DDD");    QPushButton *pb7=new QPushButton("DDD");

    layout->addMyWidget(pb);
    layout->addMyWidget(pb1);
    layout->addMyWidget(pb2);
    layout->addMyWidget(pb3);
    layout->addMyWidget(pb4);
    layout->addMyWidget(pb5);
    layout->addMyWidget(pb6);
    layout->addMyWidget(pb7);

    setLayout(layout);
}
CustomLayout::CustomLayout(QWidget *parent)
    :QLayout(parent)
{

}

CustomLayout::~CustomLayout()
{
    while (list.size() > 0) {
        delete list.takeFirst();
    }
}

void CustomLayout::addItem(QLayoutItem *item)
{
    list << item;
}

QSize CustomLayout::sizeHint() const
{
    return QSize(77,22);
}
int CustomLayout::count() const
{
    return list.size();
}
QLayoutItem *CustomLayout::itemAt(int index) const
{
    return list.value(index);
}
QLayoutItem *CustomLayout::takeAt(int index)
{
    if(index >= 0 && index < list.size())
        return list.takeAt(index);
    else
        return nullptr;
}

void CustomLayout::setGeometry(const QRect &rect)
{
    //布置布局中的子项目
    QSize s=parentWidget()->size();  		//获取布局所在父部件的大小
    int w=sizeHint().width();
    int h=sizeHint().height();
    int x=0; int y=0;//部件左上角的坐标。
    for(int i=0;i<list.size();i++){
        list.at(i)->setGeometry(QRect(x,y,w,h));
        x=x+w; //第二个项目的水平坐标向后移第一个部件的宽度
        if(x+w>s.width())/*如果新添加的项目占据的位置超过了父部件的大小,则该部件添加到下一行的开头*/
        {
            y=y+h;
            x=0;
        }
    }
}

void CustomLayout::addMyWidget(QWidget *w)
{
    addItem(new QWidgetItem(w));
}

12、QSizePolicy:大小策略,QSizePolicy类是描述水平和垂直调整策略的布局属性。

https://blog.csdn.net/liang19890820/article/details/51986284

这篇文章讲的很明了,我补充个例子就好。

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,200);
    QVBoxLayout *layout = new QVBoxLayout;
    QPushButton *btn = new QPushButton("");

    //获取默认的
    QSizePolicy sizePolicy = btn->sizePolicy();
    /*
     * QSizePolicy(
     * horizontalPolicy = QSizePolicy::Policy(Minimum),表示水平方向上有最小的尺寸,尺寸可以扩展放大
     * verticalPolicy = QSizePolicy::Policy(Fixed)) 表示垂直方向上是固定的值
     */
    qDebug() << sizePolicy;
    //修改按钮的大小策略
//    sizePolicy.setHorizontalPolicy(QSizePolicy::Fixed);//固定
//    sizePolicy.setVerticalPolicy(QSizePolicy::Minimum);//支持扩展 有最小尺寸
    sizePolicy.setHorizontalPolicy(QSizePolicy::Preferred);
    sizePolicy.setVerticalPolicy(QSizePolicy::Minimum);

    sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);//还是有最小大小的也就是说始终存在最小大小忽略不掉
    sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);


    btn->setSizePolicy(sizePolicy);

    layout->addWidget(btn);
    setLayout(layout);
}

13、QStackedLayout:有栈特性的布局,一次只能展示一个子项目

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,200);
    QComboBox *box = new QComboBox;
    QStackedLayout *stackLayout = new QStackedLayout;
    connect(box,QOverload<int>::of(&QComboBox::activated),stackLayout,&QStackedLayout::setCurrentIndex);
//    stackLayout->setStackingMode(QStackedLayout::StackAll);//设置所以的子项目都是显示的
    for(int i = 0; i < 10; ++i){
        QLabel *label = new QLabel(QString("第%1页").arg(i+1),this);
        label->setAlignment(Qt::AlignCenter);
        QPalette palette;
        palette.setColor(QPalette::Background,QColor(220,220,(i+1)*25));
        label->setAutoFillBackground(true);
        label->setPalette(palette);
        stackLayout->addWidget(label);

        box->addItem(label->text());
    }
    QPushButton *nextBtn = new QPushButton("下一页");
    connect(nextBtn,&QPushButton::clicked,[=]{
        if(stackLayout->currentIndex() < stackLayout->count()-1){
            stackLayout->setCurrentIndex(stackLayout->currentIndex()+1);
        }else{
            nextBtn->setText("已经是最后一页了");
        }
    });
    QPushButton *backBtn = new QPushButton("返回");
    connect(backBtn,&QPushButton::clicked,[=]{
        if(stackLayout->currentIndex() > 0)
            stackLayout->setCurrentIndex(stackLayout->currentIndex()-1);
        else
            backBtn->setText("已经是第一页了");
    });

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(box,0,Qt::AlignHCenter);
    mainLayout->addLayout(stackLayout);
    mainLayout->addWidget(nextBtn,0,Qt::AlignRight);
    mainLayout->addWidget(backBtn,0,Qt::AlignLeft);
    setLayout(mainLayout);
}

14、QButtonGroup:提供了一个容器来组织按钮部件组,例子在QMessageBox类中可见

15、QGroupBox:常用方法 在上面的例子中均可见到

16、QStackedWidget:这是一个方便类,基于QStackedLayout的功能支持栈界面

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget w;
    w.resize(300,200);
    QVBoxLayout *layout = new QVBoxLayout;
    QPalette palette;
    QWidget *firstPageWidget = new QWidget;
    palette.setColor(QPalette::Background,Qt::red);
    firstPageWidget->setAutoFillBackground(true);
    firstPageWidget->setPalette(palette);
    QWidget *secondPageWidget = new QWidget;
    palette.setColor(QPalette::Background,Qt::blue);
    secondPageWidget->setAutoFillBackground(true);
    secondPageWidget->setPalette(palette);
    QWidget *thirdPageWidget = new QWidget;
    palette.setColor(QPalette::Background,Qt::yellow);
    thirdPageWidget->setAutoFillBackground(true);
    thirdPageWidget->setPalette(palette);

    QStackedWidget *stackedWidget = new QStackedWidget;
    stackedWidget->addWidget(firstPageWidget);
    stackedWidget->addWidget(secondPageWidget);
    stackedWidget->addWidget(thirdPageWidget);

    layout->addWidget(stackedWidget);

    QPushButton *btn = new QPushButton("next");
    btn->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
    QObject::connect(btn,&QPushButton::clicked,[=]{
        if(stackedWidget->currentIndex() < stackedWidget->count()-1)
            stackedWidget->setCurrentIndex(stackedWidget->currentIndex()+1);
        else
            stackedWidget->setCurrentIndex(0);
    });

    layout->addWidget(btn,0,Qt::AlignCenter);
    w.setLayout(layout);

    w.show();
    return a.exec();
}

五、模型/视图编程类(重点):需要单独的一篇文章来学习。这里暂时先放下,后续补上链接

六、富文本处理类:新类以QTextDocument类为中心,而不是以原始文本信息为中心。这使开发人员能够创建和修改结构化的富文本文档,而无需准备中间标记格式的内容。文档中的信息可以通过两个互补的接口访问:一个基于指针的接口用于编辑,一个只读的分层接口提供了文档结构的高级概述。基于指针的界面的主要优点是,可以使用模拟用户与编辑器交互的操作来编辑文本,而不会丢失文档的底层结构。只读层次结构接口在执行搜索和文档导出等操作时非常有用。

1、富文本文档结构说明,概述QTextDocument中不同类型的元素,并描述它们如何在文档结构中排列

2、QTextCursor接口解释了如何使用基于指针的接口编辑富文本文档

3、文档布局简要说明了文档布局的作用

4、常见富文本编辑任务检查一些涉及到读取或操作富文本文档的常见任务

5、高级富文本处理

6、支持的HTML4子集列出了QTextDocument支持的HTML标记

7、Qt提供了一个广泛的类集合,用于解析、呈现、操作和编辑富文本。

七、鼠标拖放处理类

1、QDrag:支持基于mime的拖放数据传输

2、QDragEnterEvent:提供了一个事件,当拖放操作进入该事件时,该事件被发送到小部件

3、QDragLeaveEvent:提供了一个事件,当拖放操作离开小部件时,该事件被发送到小部件。

4、QDragMoveEvent:提供了一个在拖放操作进行时发送的事件

5、QDropEvent:提供一个事件,当拖放操作完成时发送该事件

static QLabel *createDragLabel(const QString text,QWidget *parent)
{
    QLabel *label = new QLabel(text,parent);
    //设置文本的样式
    label->setAutoFillBackground(true);
    //边框形状
    label->setFrameShape(QFrame::Panel);
    //边框阴影
    label->setFrameShadow(QFrame::Raised);
    return label;
}

static QString hotSpotMimeDataKey()
{
    return QStringLiteral("application/x-hotspot");
}

DragWidget::DragWidget(QWidget *parent)
    : QWidget(parent)
{
    QFile dictionaryFile(QStringLiteral(":/words.txt"));
    dictionaryFile.open(QIODevice::ReadOnly);
    QTextStream inputStream(&dictionaryFile);

    int x = 5;
    int y = 5;

    while (!inputStream.atEnd()) {
        QString word;
        inputStream >> word;
        if(!word.isEmpty()){
            QLabel *wordLabel = createDragLabel(word,this);
            wordLabel->move(x,y);
            wordLabel->show();
            wordLabel->setAttribute(Qt::WA_DeleteOnClose);
            x += wordLabel->width() + 2;
            if(x >= 245){
                x = 5;
                y += wordLabel->height() + 2;
            }
        }
    }

    //设置支持拖拽
    setAcceptDrops(true);
    setMinimumSize(400,qMax(200,y));
    setWindowTitle(tr("Draggable Text"));
}

DragWidget::~DragWidget()
{

}

void DragWidget::mousePressEvent(QMouseEvent *event)
{
    QLabel *child = qobject_cast<QLabel*>(childAt(event->pos()));
    if(!child)
        return;
    //获得鼠标点击的位置与文本左上角的距离.实际上就是鼠标在这个label中的位置
    QPoint hotSpot = event->pos() - child->pos();
//    qDebug() << "hotSpot" << hotSpot;

    QMimeData *mimeData = new QMimeData;
    mimeData->setText(child->text());
    mimeData->setData(hotSpotMimeDataKey(),
                      QByteArray::number(hotSpot.x()) + ' ' + QByteArray::number(hotSpot.y()));
    //返回窗口的物理像素和与设备无关的像素之间的比率。此值依赖于窗口所在的屏幕,并且在移动窗口时可能会更改。
    qreal dpr = this->window()->windowHandle()->devicePixelRatio();
    //直接将文本绘制在QPixmap上
    QPixmap pixmap(child->size()*dpr);
    pixmap.setDevicePixelRatio(dpr);
    child->render(&pixmap);

    QDrag *drag = new QDrag(this);
    drag->setMimeData(mimeData);
    drag->setPixmap(pixmap);
    drag->setHotSpot(hotSpot);

    Qt::DropAction dropAction = drag->exec(Qt::MoveAction|Qt::CopyAction,Qt::CopyAction);
    if(dropAction == Qt::MoveAction)
        child->close();
}

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasText()){
        if(event->source() == this){
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }else{
            event->acceptProposedAction();
        }
    }else{
        event->ignore();
    }
}

void DragWidget::dropEvent(QDropEvent *event)
{
    if(event->mimeData()->hasText()){
        const QMimeData * mime = event->mimeData();
        QStringList pieces = mime->text().split(QRegularExpression(QStringLiteral("\\s+")),
                                                QString::SkipEmptyParts);
        //获得当前放置的坐标
        QPoint position = event->pos();
        qDebug() << event->pos();
        //获得鼠标在label中的位置
        QPoint hotSpot;
        QByteArrayList hotSpotPos = mime->data(hotSpotMimeDataKey()).split(' ');
        if(hotSpotPos.size() == 2){
            hotSpot.setX(hotSpotPos.first().toInt());
            hotSpot.setY(hotSpotPos.last().toInt());
        }
        //重新创建文本显示控件
        foreach (const QString &piece, pieces) {
            QLabel *newLabel = createDragLabel(piece,this);
            //移动到新的位置
            newLabel->move(position-hotSpot);
            newLabel->show();
            newLabel->setAttribute(Qt::WA_DeleteOnClose);
            position += QPoint(newLabel->width(),0);
        }
        if(event->source() == this){
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }else{
            event->acceptProposedAction();
        }
    }else{
        event->ignore();
    }

    //查找隐藏的 把它删除掉
    foreach (QWidget *widget, this->findChildren<QWidget *>()) {
        if(!widget->isVisible())
            widget->deleteLater();
    }
}
DragWidget::DragWidget(QWidget *parent)
    : QFrame(parent)
{
    setMinimumSize(200,200);
    setFrameStyle(QFrame::Sunken|QFrame::StyledPanel);
    setAcceptDrops(true);

    QLabel *boatIcon = new QLabel(this);
    boatIcon->setPixmap(QPixmap(":/boat.png"));
    boatIcon->move(10,10);
    boatIcon->show();
    boatIcon->setAttribute(Qt::WA_DeleteOnClose);

    QLabel *carIcon = new QLabel(this);
    carIcon->setPixmap(QPixmap(":/car.png"));
    carIcon->move(100,10);
    carIcon->show();
    carIcon->setAttribute(Qt::WA_DeleteOnClose);

    QLabel *houseIcon = new QLabel(this);
    houseIcon->setPixmap(QPixmap(":/house.png"));
    houseIcon->move(10,80);
    houseIcon->show();
    houseIcon->setAttribute(Qt::WA_DeleteOnClose);

}

DragWidget::~DragWidget()
{

}

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasFormat("application/x-dnditemdata")){
        if(event->source() == this){
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }else{
            event->acceptProposedAction();
        }
    }else{
        event->ignore();
    }
}

void DragWidget::dragMoveEvent(QDragMoveEvent *event)
{
    if(event->mimeData()->hasFormat("application/x-dnditemdata")){
        if(event->source() == this){
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }else{
            event->acceptProposedAction();
        }
    }else{
        event->ignore();
    }
}

void DragWidget::dropEvent(QDropEvent *event)
{
    if(event->mimeData()->hasFormat("application/x-dnditemdata")){
        //获取数据
        QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");
        QDataStream dataStream(&itemData,QIODevice::ReadOnly);

        QPixmap pixmap;
        QPoint offset;
        dataStream >> pixmap >> offset;

        QLabel *newIcon = new QLabel(this);
        newIcon->setPixmap(pixmap);
        newIcon->move(event->pos()-offset);
        newIcon->show();
        newIcon->setAttribute(Qt::WA_DeleteOnClose);
        if(event->source() == this){
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }else{
            event->acceptProposedAction();
        }
    }else{
        event->ignore();
    }
}

void DragWidget::mousePressEvent(QMouseEvent *event)
{
    QLabel *child = static_cast<QLabel*>(childAt(event->pos()));
    if(!child)
        return;
    QPixmap pixmap = *child->pixmap();

    QByteArray itemData;
    QDataStream dataStream(&itemData,QIODevice::WriteOnly);
    dataStream << pixmap << QPoint(event->pos()-child->pos());

    QMimeData *mimeData = new QMimeData;
    mimeData->setData("application/x-dnditemdata",itemData);

    QDrag *drag = new QDrag(this);
    drag->setMimeData(mimeData);
    drag->setPixmap(pixmap);
    drag->setHotSpot(event->pos()-child->pos());

    QPixmap tempPixmap = pixmap;
    QPainter painter;
    painter.begin(&tempPixmap);
    painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127));
    painter.end();

    Qt::DropAction action = drag->exec(Qt::CopyAction|Qt::MoveAction,Qt::CopyAction);
    if(action == Qt::MoveAction){
        child->close();
    }else{
        child->show();
        child->setPixmap(pixmap);
    }
}
DropArea::DropArea(QWidget *parent)
    :QLabel(parent)
{
    setMinimumSize(200,200);
    setFrameStyle(QFrame::Sunken|QFrame::StyledPanel);
    setAlignment(Qt::AlignCenter);
    setAcceptDrops(true);
    setAutoFillBackground(true);
    clear();
}

void DropArea::clear()
{
    setText(tr("<drop content here>"));
    setBackgroundRole(QPalette::Dark);

    emit changed();
}

void DropArea::dragEnterEvent(QDragEnterEvent *event)
{
    setText(tr("<drop content>"));
    setBackgroundRole(QPalette::Highlight);
    event->acceptProposedAction();

    emit changed(event->mimeData());
}

void DropArea::dragMoveEvent(QDragMoveEvent *event)
{
    event->acceptProposedAction();
}

void DropArea::dragLeaveEvent(QDragLeaveEvent *event)
{
    clear();
    event->accept();
}

void DropArea::dropEvent(QDropEvent *event)
{
    const QMimeData *mimeData = event->mimeData();
    if(mimeData->hasImage()){
        setPixmap((qvariant_cast<QPixmap>)(mimeData->imageData()));
    }else if(mimeData->hasHtml()){
        setText(mimeData->html());
        setTextFormat(Qt::RichText);
    }else if(mimeData->hasText()){
        setText(mimeData->text());
        setTextFormat(Qt::PlainText);
    }else if(mimeData->hasUrls()){
        QList<QUrl> urlList = mimeData->urls();
        QString text;
        for(int i = 0; i < urlList.size() && i < 32; ++i){
            text += urlList.at(i).path() + QLatin1Char('\n');
        }
        setText(text);
    }else{
        setText(tr("Cannot display data"));
    }

    setBackgroundRole(QPalette::Dark);
    event->acceptProposedAction();
}
DropSiteWindow::DropSiteWindow(QWidget *parent)
    : QWidget(parent)
{
    abstractLabel = new QLabel(tr("This example accepts drags from other "
                                  "applications and displays the MIME "
                                  "types "
                                  "provided by the drag object."));
    abstractLabel->setWordWrap(true);
    //调整窗口大小以适用该文本
    abstractLabel->adjustSize();

    dropArea = new DropArea;
    //检测到新的拖放 则更新表内容
    connect(dropArea,&DropArea::changed,this,&DropSiteWindow::updateFormatsTable);

    //表头
    QStringList labels;
    labels << tr("Format") << tr("Content");

    //创建表格控件
    formatsTable = new QTableWidget;
    formatsTable->setColumnCount(2);
    formatsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);//不支持编辑
    formatsTable->setHorizontalHeaderLabels(labels);//设置表头
    formatsTable->horizontalHeader()->setStretchLastSection(true);//最后一列自动拉伸

    //按钮
    clearButton = new QPushButton(tr("Clear"));
    quitButton = new QPushButton(tr("Quit"));

    buttonBox = new QDialogButtonBox;
    buttonBox->addButton(clearButton,QDialogButtonBox::ActionRole);
    buttonBox->addButton(quitButton,QDialogButtonBox::RejectRole);

    connect(quitButton,&QPushButton::pressed,this,&DropSiteWindow::close);//关闭窗口
    connect(clearButton,&QPushButton::pressed,dropArea,&DropArea::clear);//清空拖放的内容

    //设置布局
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(abstractLabel);
    mainLayout->addWidget(dropArea);
    mainLayout->addWidget(formatsTable);
    mainLayout->addWidget(buttonBox);

    setWindowTitle(tr("Drop Site"));
    setMinimumSize(350, 500);
}

void DropSiteWindow::updateFormatsTable(const QMimeData *mimeData)
{
    //清空表
    formatsTable->setRowCount(0);
    if(!mimeData)
        return;
    //开始遍历保存的格式并获取数据
    foreach (QString format, mimeData->formats()) {
        QTableWidgetItem *formatItem = new QTableWidgetItem(format);
        formatItem->setFlags(Qt::ItemIsEnabled);
        formatItem->setTextAlignment(Qt::AlignTop|Qt::AlignLeft);

        QString text;
        if(format == QStringLiteral("text/plain")){
            text = mimeData->text().simplified();
        }else if(format == QStringLiteral("text/html")){
            text = mimeData->html().simplified();
        }else if(format == QStringLiteral("text/uri-list")){
            QList<QUrl> urlList = mimeData->urls();
            for (int i = 0; i < urlList.size() && i < 32; ++i)
                text.append(urlList.at(i).toString() + QLatin1Char(' '));
        }else {
            QByteArray data = mimeData->data(format);
            for (int i = 0; i < data.size() && i < 32; ++i)
                text.append(QStringLiteral("%1 ").arg(uchar(data[i]), 2, 16, QLatin1Char('0')).toUpper());
        }

        int row = formatsTable->rowCount();
        formatsTable->insertRow(row);
        formatsTable->setItem(row,0,formatItem);
        formatsTable->setItem(row,1,new QTableWidgetItem(text));
    }
    formatsTable->resizeColumnToContents(0);//第1列自动适用内容
}

八、国际化类

应用程序的国际化和本地化是使应用程序适应目标市场的不同语言、地区差异和技术需求的过程。国际化意味着设计一个软件应用程序,使其能够在不进行工程更改的情况下适应各种语言和地区。本地化意味着通过添加特定于地区或语言的组件(如日期、时间和数字格式)和翻译文本来适应国际化软件。

1、QTextCodec:文本编码之间的转换

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
//    MainWindow w;
//    w.show();

    QString str = QObject::tr("我是gb2312");//这个在Qt中默认编码是utf-8
    //创建一个gb2312编码实例
    QTextCodec *code = QTextCodec::codecForName("gb2312");
    //把字符串转为gb2312表示的二进制编码
    QByteArray convert = code->fromUnicode(str);
    qDebug() << convert;

    //再使用该实例将二进制编码转为utf-8
    QString newStr = code->toUnicode(convert);
    qDebug() << newStr;


    //把Unicode编码转为指定的编码
    QTextCodec *big5 = QTextCodec::codecForName("Big5");
    QTextEncoder enCode(big5);
    convert = enCode.fromUnicode(QString("輕裝上陣"));
    qDebug() << convert;

    //把二进制编码转为Unicode编码
    QTextDecoder deCode(big5);
    qDebug() << deCode.toUnicode(convert);

    return a.exec();
}

2、QTextDecoder:文本解码器使用特定的编解码器将文本从已编码的文本格式转换为Unicode。

3、QTextEncoder:文本编码器使用特定的编解码器将文本从Unicode转换为经过编码的文本格式。

4、QTranslator:为文本输出提供国际化支持。

5、QCollator:根据本地化的排序算法对字符串进行比较

6、QCollatorSortKey:总是由QCollator::sortKey()创建,用于快速字符串排序,例如在排序多个字符串时。

7、QLocale:在其构造函数中使用语言/国家对进行初始化,并提供类似于QString中的数字到字符串和字符串到数字的转换函数。

猜你喜欢

转载自blog.csdn.net/wei375653972/article/details/85111315