[Qt]窗口

文章摘于 爱编程的大丙

文章目录

  • 1. 基础窗口类
    • 1.1 QWidget
      • 1.1.1 设置父对象
      • 1.1.2 窗口位置
      • 1.1.3 窗口尺寸
      • 1.1.4 窗口标题和图标
      • 1.1.5 信号
      • 1.1.6 槽函数
    • 1.2 QDialog
      • 1.2.1 常用API
      • 1.2.2 常用使用方法
    • 1.3 QDialog的子类
      • 1.3.1 QMessageBox
        • 1.3.1.1 API - 静态函数
        • 1.3.1.2 测试代码
      • 1.3.2 QFileDialog
        • 1.3.2.1 API - 静态函数
        • 1.3.2.2 测试代码
      • 1.3.3 QFontDialog
        • 1.3.3.1 QFont 字体类
        • 1.3.3.2 QFontDialog类的静态API
        • 1.3.3.3 测试代码
      • 1.3.4 QColorDialog
        • 1.3.4.1 颜色类 QColor
        • 1.3.4.2 静态API函数
        • 1.3.4.3 测试代码
      • 1.3.5 QInputDialog
        • 1.3.5.1 API - 静态函数
        • 1.3.5.2 测试代码
      • 1.3.6 QProgressDialog
        • 1.3.6.1 常用API
        • 1.3.6.2 测试代码
    • 1.4 QMainWindow
      • 1.4.1 菜单栏
      • 1.4.2 工具栏
        • 1.4.2.1 添加工具按钮
        • 1.4.2.2 工具栏的属性设置
      • 1.4.3 状态栏
      • 1.4.4 停靠窗口
    • 1.5 资源文件 .qrc
    • 1.6 在Qt窗口中添加右键菜单
      • 1.6.1 基于鼠标事件实现
        • 1.6.1.1 实现思路
        • 1.6.1.2 代码实现
      • 1.6.2 基于窗口的菜单策略实现
        • 1.6.2.1 Qt::DefaultContextMenu
        • 1.6.2.2 Qt::ActionsContextMenu
        • 1.6.2.3 Qt::CustomContextMenu
  • 2. 窗口布局
    • 2.1 布局的样式
    • 2.2 在UI窗口中设置布局
      • 2.2.1 方式1
      • 2.2.2 方式2
      • 2.2.3 弹簧的使用
      • 2.2.4 布局属性设置
      • 2.2.5 布局的注意事项
    • 2.3 通过API设置布局
      • 2.3.1 QLayout
      • 2.3.2 QHBoxLayout
      • 2.3.3 QVBoxLayout
      • 2.3.4 QGridLayout


1. 基础窗口类

1.1 QWidget

QWidget类是所有窗口类的父类(控件类是也属于窗口类), 并且QWidget类的父类的QObject,
也就意味着所有的窗口类对象只要指定了父对象, 都可以实现内存资源的自动回收。
关于这个窗口类的属性介绍, 参考容器控件之QWidget。

[Qt]窗口_第1张图片

1.1.1 设置父对象

// 构造函数
QWidget::QWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());

// 公共成员函数
// 给当前窗口设置父对象
void QWidget::setParent(QWidget *parent);
void QWidget::setParent(QWidget *parent, Qt::WindowFlags f);
// 获取当前窗口的父对象, 没有父对象返回 nullptr
QWidget *QWidget::parentWidget() const;

1.1.2 窗口位置

//------------- 窗口位置 -------------
// 得到相对于当前窗口父窗口的几何信息, 边框也被计算在内
QRect QWidget::frameGeometry() const;
// 得到相对于当前窗口父窗口的几何信息, 不包括边框
const QRect &geometry() const;
// 设置当前窗口的几何信息(位置和尺寸信息), 不包括边框
void setGeometry(int x, int y, int w, int h);
void setGeometry(const QRect &);
    
// 移动窗口, 重新设置窗口的位置
void move(int x, int y);
void move(const QPoint &);

窗口位置设定和位置获取的测试代码如下:

// 获取当前窗口的位置信息
void MainWindow::on_positionBtn_clicked()
{
    QRect rect = this->frameGeometry();
    qDebug() << "左上角: " << rect.topLeft()
             << "右上角: " << rect.topRight()
             << "左下角: " << rect.bottomLeft()
             << "右下角: " << rect.bottomRight()
             << "宽度: " << rect.width()
             << "高度: " << rect.height();
}

// 重新设置当前窗口的位置以及宽度, 高度
void MainWindow::on_geometryBtn_clicked()
{
    int x = 100 + rand() % 500;
    int y = 100 + rand() % 500;
    int width = this->width() + 10;
    int height = this->height() + 10;
    setGeometry(x, y, width, height);
}

// 通过 move() 方法移动窗口
void MainWindow::on_moveBtn_clicked()
{
    QRect rect = this->frameGeometry();
    move(rect.topLeft() + QPoint(10, 20));
}

1.1.3 窗口尺寸

//------------- 窗口尺寸 -------------
// 获取当前窗口的尺寸信息
QSize size() const
// 重新设置窗口的尺寸信息
void resize(int w, int h);
void resize(const QSize &);
// 获取当前窗口的最大尺寸信息
QSize maximumSize() const;
// 获取当前窗口的最小尺寸信息
QSize minimumSize() const;
// 设置当前窗口固定的尺寸信息
void QWidget::setFixedSize(const QSize &s);
void QWidget::setFixedSize(int w, int h);
// 设置当前窗口的最大尺寸信息
void setMaximumSize(const QSize &);
void setMaximumSize(int maxw, int maxh);
// 设置当前窗口的最小尺寸信息
void setMinimumSize(const QSize &);
void setMinimumSize(int minw, int minh);


// 获取当前窗口的高度    
int height() const;
// 获取当前窗口的最小高度
int minimumHeight() const;
// 获取当前窗口的最大高度
int maximumHeight() const;
// 给窗口设置固定的高度
void QWidget::setFixedHeight(int h);
// 给窗口设置最大高度
void setMaximumHeight(int maxh);
// 给窗口设置最小高度
void setMinimumHeight(int minh);

// 获取当前窗口的宽度
int width() const;
// 获取当前窗口的最小宽度
int minimumWidth() const;
// 获取当前窗口的最大宽度
int maximumWidth() const;
// 给窗口设置固定宽度
void QWidget::setFixedWidth(int w);
// 给窗口设置最大宽度
void setMaximumWidth(int maxw);
// 给窗口设置最小宽度
void setMinimumWidth(int minw);

1.1.4 窗口标题和图标

//------------- 窗口图标 -------------
// 得到当前窗口的图标
QIcon windowIcon() const;
// 构造图标对象, 参数为图片的路径
QIcon::QIcon(const QString &fileName);
// 设置当前窗口的图标
void setWindowIcon(const QIcon &icon);

//------------- 窗口标题 -------------
// 得到当前窗口的标题
QString windowTitle() const;
// 设置当前窗口的标题
void setWindowTitle(const QString &);

1.1.5 信号

// QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy);
// 窗口的右键菜单策略 contextMenuPolicy() 参数设置为 Qt::CustomContextMenu, 按下鼠标右键发射该信号
[signal] void QWidget::customContextMenuRequested(const QPoint &pos);
// 窗口图标发生变化, 发射此信号
[signal] void QWidget::windowIconChanged(const QIcon &icon);
// 窗口标题发生变化, 发射此信号
[signal] void QWidget::windowTitleChanged(const QString &title);

基于窗口策略实现右键菜单具体操作参考 Qt右键菜单的添加和使用(本章第六部分)


1.1.6 槽函数

//------------- 窗口显示 -------------
// 关闭当前窗口
[slot] bool QWidget::close();
// 隐藏当前窗口
[slot] void QWidget::hide();
// 显示当前创建以及其子窗口
[slot] void QWidget::show();
// 全屏显示当前窗口, 只对windows有效
[slot] void QWidget::showFullScreen();
// 窗口最大化显示, 只对windows有效
[slot] void QWidget::showMaximized();
// 窗口最小化显示, 只对windows有效
[slot] void QWidget::showMinimized();
// 将窗口回复为最大化/最小化之前的状态, 只对windows有效
[slot] void QWidget::showNormal();

//------------- 窗口状态 -------------
// 判断窗口是否可用
bool QWidget::isEnabled() const; // 非槽函数
// 设置窗口是否可用, 不可用窗口无法接收和处理窗口事件
// 参数true->可用, false->不可用
[slot] void QWidget::setEnabled(bool);
// 设置窗口是否可用, 不可用窗口无法接收和处理窗口事件
// 参数true->不可用, false->可用
[slot] void QWidget::setDisabled(bool disable);
// 设置窗口是否可见, 参数为true->可见, false->不可见
[slot] virtual void QWidget::setVisible(bool visible);

1.2 QDialog

1.2.1 常用API

对话框类是QWidget类的子类, 处理继承自父类的属性之外, 还有一些自己所特有的属性

// 构造函数
QDialog::QDialog(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());

// 模态显示窗口
[virtual slot] int QDialog::exec();
// 隐藏模态窗口, 并且解除模态窗口的阻塞, 将 exec() 的返回值设置为 QDialog::Accepted
[virtual slot] void QDialog::accept();
// 隐藏模态窗口, 并且解除模态窗口的阻塞, 将 exec() 的返回值设置为 QDialog::Rejected
[virtual slot] void QDialog::reject();
// 关闭对话框并将其结果代码设置为r。finished()信号将发出r;
// 如果r是QDialog::Accepted 或 QDialog::Rejected,则还将分别发出accept()或Rejected()信号。
[virtual slot] void QDialog::done(int r);

[signal] void QDialog::accepted();
[signal] void QDialog::rejected();
[signal] void QDialog::finished(int result);

1.2.2 常用使用方法

场景介绍:

  1. 有两个窗口, 主窗口和一个对话框子窗口
  2. 对话框窗口先显示, 根据用户操作选择是否显示主窗口
  • 关于对话框窗口类的操作
// 对话框窗口中三个普通按钮按下之后对应的槽函数
void MyDialog::on_acceptBtn_clicked()
{
    this->accept();  // exec()函数返回值为QDialog::Accepted
}

void MyDialog::on_rejectBtn_clicked()
{
    this->reject();  // exec()函数返回值为QDialog::Rejected
}

void MyDialog::on_donBtn_clicked()
{
    // exec()函数返回值为 done() 的参数, 并根据参数发射出对应的信号
    this->done(666);   
}
  • 根据用户针对对话框窗口的按钮操作, 进行相应的逻辑处理。
// 创建对话框对象
MyDialog dlg;    
int ret = dlg.exec();
if(ret == QDialog::Accepted)
{
    qDebug() << "accept button clicked...";
    // 显示主窗口
    MainWindow* w = new MainWindow;
    w->show();
}
else if(ret == QDialog::Rejected)
{
    qDebug() << "reject button clicked...";
    // 不显示主窗口
    ......
    ......
}
else
{
    // ret == 666
    qDebug() << "done button clicked...";
    // 根据需求进行逻辑处理
    ......
    ......
}

1.3 QDialog的子类

1.3.1 QMessageBox

QMessageBox 对话框类是 QDialog 类的子类
通过这个类可显示一些简单的提示框,用于展示警告、错误、问题等信息。
关于这个类只需掌握一些静态方法即可。

1.3.1.1 API - 静态函数

// 显示一个模态对话框, 将参数 text 的信息展示到窗口中
[static] void QMessageBox::about(QWidget *parent, const QString &title, const QString &text);

/*
参数:
- parent: 对话框窗口的父窗口
- title: 对话框窗口的标题
- text: 对话框窗口中显示的提示信息
- buttons: 对话框窗口中显示的按钮(一个或多个)
- defaultButton
    1. defaultButton指定按下Enter键时使用的按钮。
    2. defaultButton必须引用在参数 buttons 中给定的按钮。
    3. 如果defaultButton是QMessageBox::NoButton, QMessageBox会自动选择一个合适的默认值。
*/
// 显示一个信息模态对话框
[static] QMessageBox::StandardButton QMessageBox::information(
           QWidget *parent, const QString &title, 
           const QString &text, 
           QMessageBox::StandardButtons buttons = Ok,
           QMessageBox::StandardButton defaultButton = NoButton);

// 显示一个错误模态对话框
[static] QMessageBox::StandardButton QMessageBox::critical(
           QWidget *parent, const QString &title, 
           const QString &text, 
           QMessageBox::StandardButtons buttons = Ok,
           QMessageBox::StandardButton defaultButton = NoButton);

// 显示一个问题模态对话框
[static] QMessageBox::StandardButton QMessageBox::question(
           QWidget *parent, const QString &title, 
           const QString &text, 
           QMessageBox::StandardButtons buttons = StandardButtons(Yes | No), 
           QMessageBox::StandardButton defaultButton = NoButton);

// 显示一个警告模态对话框
[static] QMessageBox::StandardButton QMessageBox::warning(
           QWidget *parent, const QString &title, 
           const QString &text, 
           QMessageBox::StandardButtons buttons = Ok,
           QMessageBox::StandardButton defaultButton = NoButton);

1.3.1.2 测试代码

  • 测试代码片段
void MainWindow::on_msgbox_clicked()
{
    QMessageBox::about(this, "about",  "这是一个简单的消息提示框!!!");
    QMessageBox::critical(this, "critical", "这是一个错误对话框-critical...");
    int ret = QMessageBox::question(this, "question", 
             "你要保存修改的文件内容吗???",                                 
              QMessageBox::Save|QMessageBox::Cancel, 
              QMessageBox::Cancel);
    if(ret == QMessageBox::Save)
    {
        QMessageBox::information(this, "information", "恭喜你保存成功了, o(* ̄︶ ̄*)o!!!");
    }
    else if(ret == QMessageBox::Cancel)
    {
        QMessageBox::warning(this, "warning", "你放弃了保存, ┭┮﹏┭┮ !!!");
    }
}
  • 得到的对话框窗口效果如下图:
    [Qt]窗口_第2张图片

1.3.2 QFileDialog

QFileDialog 对话框类是 QDialog 类的子类
通过这个类可以选择要打开/保存的文件或者目录。
关于这个类只需掌握一些静态方法的使用即可。

1.3.2.1 API - 静态函数

/*
通用参数:
  - parent: 当前对话框窗口的父对象也就是父窗口
  - caption: 当前对话框窗口的标题
  - dir: 当前对话框窗口打开的默认目录
  - options: 当前对话框窗口的一些可选项,枚举类型, 一般不需要进行设置, 使用默认值即可
  - filter: 过滤器, 在对话框中只显示满足条件的文件, 可以指定多个过滤器, 使用 ;; 分隔
    - 样式举例: 
	- Images (*.png *.jpg)
	- Images (*.png *.jpg);;Text files (*.txt)
  - selectedFilter: 如果指定了多个过滤器, 通过该参数指定默认使用哪一个, 不指定默认使用第一个过滤器
*/
// 打开一个目录, 得到这个目录的绝对路径
[static] QString QFileDialog::getExistingDirectory(
                  QWidget *parent = nullptr, 
                  const QString &caption = QString(), 
                  const QString &dir = QString(), 
                  QFileDialog::Options options = ShowDirsOnly);

// 打开一个文件, 得到这个文件的绝对路径
[static] QString QFileDialog::getOpenFileName(
    	          QWidget *parent = nullptr, 
    		  const QString &caption = QString(), 
                  const QString &dir = QString(), 
                  const QString &filter = QString(), 
                  QString *selectedFilter = nullptr, 
                  QFileDialog::Options options = Options());

// 打开多个文件, 得到这多个文件的绝对路径
[static] QStringList QFileDialog::getOpenFileNames(
    	          QWidget *parent = nullptr, 
                  const QString &caption = QString(), 
                  const QString &dir = QString(), 
                  const QString &filter = QString(), 
                  QString *selectedFilter = nullptr, 
                  QFileDialog::Options options = Options());

// 打开一个目录, 使用这个目录来保存指定的文件
[static] QString QFileDialog::getSaveFileName(
    		  QWidget *parent = nullptr, 
                  const QString &caption = QString(), 
                  const QString &dir = QString(), 
                  const QString &filter = QString(), 
                  QString *selectedFilter = nullptr, 
                  QFileDialog::Options options = Options());

1.3.2.2 测试代码

  • 打开一个已存在的本地目录
void MainWindow::on_filedlg_clicked()
{
    QString dirName = QFileDialog::getExistingDirectory(this, "打开目录", "e:\\temp");
    QMessageBox::information(this, "打开目录", "您选择的目录是: " + dirName);
}
  • 对话框效果如下:
    [Qt]窗口_第3张图片

  • 打开一个本地文件

void MainWindow::on_filedlg_clicked()
{
    QString arg("Text files (*.txt)");
    QString fileName = QFileDialog::getOpenFileName(
              this, "Open File", "e:\\temp",
              "Images (*.png *.jpg);;Text files (*.txt)", &arg);
    QMessageBox::information(this, "打开文件", "您选择的文件是: " + fileName);
}
  • 对话框效果如下:

[Qt]窗口_第4张图片

  • 打开多个本地文件
void MainWindow::on_filedlg_clicked()
{
    QStringList fileNames = QFileDialog::getOpenFileNames(
              this, "Open File", "e:\\temp",
              "Images (*.png *.jpg);;Text files (*.txt)");
    QString names;
    for(int i=0; i<fileNames.size(); ++i)
    {
        names += fileNames.at(i) + " ";
    }
    QMessageBox::information(this, "打开文件(s)", "您选择的文件是: " + names);
}
  • 对话框效果如下:

[Qt]窗口_第5张图片

  • 打开保存文件对话框
void MainWindow::on_filedlg_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, "保存文件", "e:\\temp");
    QMessageBox::information(this, "保存文件", "您指定的保存数据的文件是: " + fileName);
}

对话框效果如下:

[Qt]窗口_第6张图片


1.3.3 QFontDialog

QFontDialog类是QDialog的子类
通过这个类我们可以得到一个进行字体属性设置的对话框窗口
和前边介绍的对话框类一样,只需调用这个类的静态成员函数就可以得到想要的窗口了。

1.3.3.1 QFont 字体类

关于字体的属性信息, 在QT框架中被封装到了一个叫QFont的类中

// 构造函数
  QFont::QFont();
  /*
  参数:
    - family: 本地字库中的字体名, 通过 office 等文件软件可以查看
    - pointSize: 字体的字号
    - weight: 字体的粗细, 有效范围为 0 ~ 99
    - italic: 字体是否倾斜显示, 默认不倾斜
  */
  QFont::QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false);
  
  // 设置字体
  void QFont::setFamily(const QString &family);
  // 根据字号设置字体大小
  void QFont::setPointSize(int pointSize);
  // 根据像素设置字体大小
  void QFont::setPixelSize(int pixelSize);
  // 设置字体的粗细程度, 有效范围: 0 ~ 99
  void QFont::setWeight(int weight);
  // 设置字体是否加粗显示
  void QFont::setBold(bool enable);
  // 设置字体是否要倾斜显示
  void QFont::setItalic(bool enable);
  
  // 获取字体相关属性(一般规律: 去掉设置函数的 set 就是获取相关属性对应的函数名)
  QString QFont::family() const;
  bool QFont::italic() const;
  int QFont::pixelSize() const;
  int QFont::pointSize() const;
  bool QFont::bold() const;
  int QFont::weight() const;

如果一个QFont对象被创建, 并且进行了初始化, 我们可以将这个属性设置给某个窗口, 或者设置给当前应用程序对象。

// QWidget 类
// 得到当前窗口使用的字体
const QWidget::QFont& font() const;
// 给当前窗口设置字体, 只对当前窗口类生效
void QWidget::setFont(const QFont &);

// QApplication 类
// 得到当前应用程序对象使用的字体
[static] QFont QApplication::font();
// 给当前应用程序对象设置字体, 作用于当前应用程序的所有窗口
[static] void QApplication::setFont(const QFont &font, const char *className = nullptr);

1.3.3.2 QFontDialog类的静态API

/*
参数:
  - ok: 传出参数, 用于判断是否获得了有效字体信息, 指定一个布尔类型变量地址
  - initial: 字体对话框中默认选中并显示该字体信息, 用于对话框的初始化
  - parent: 字体对话框窗口的父对象
  - title: 字体对话框的窗口标题
  - options: 字体对话框选项, 使用默认属性即可, 一般不设置
*/
  [static] QFont QFontDialog::getFont(
		bool *ok, const QFont &initial, 
		QWidget *parent = nullptr, const QString &title = QString(), 
		QFontDialog::FontDialogOptions options = FontDialogOptions());
  
  [static] QFont QFontDialog::getFont(bool *ok, QWidget *parent = nullptr);

1.3.3.3 测试代码

通过字体对话框选择字体, 并将选择的字体设置给当前窗口

void MainWindow::on_fontdlg_clicked()
{
#if 1
    // 方式1
    bool ok;
    QFont ft = QFontDialog::getFont(
                &ok, QFont("微软雅黑", 12, QFont::Bold), this, "选择字体");
    qDebug() << "ok value is: " << ok;
#else
    // 方式2
    QFont ft = QFontDialog::getFont(NULL);
#endif
    // 将选择的字体设置给当前窗口对象
    this->setFont(ft);
}

字体对话框效果展示:
[Qt]窗口_第7张图片


1.3.4 QColorDialog

QColorDialog类是QDialog的子类
通过这个类我们可以得到一个选择颜色的对话框窗口
和前边介绍的对话框类一样,只需调用这个类的静态成员函数就可以得到想要的窗口了。

1.3.4.1 颜色类 QColor

关于颜色的属性信息, 在QT框架中被封装到了一个叫QColor的类中
各种颜色都是基于红, 绿, 蓝这三种颜色调配而成的, 并且颜色还可以进行透明度设置, 默认是不透明的。

// 构造函数
QColor::QColor(Qt::GlobalColor color);
QColor::QColor(int r, int g, int b, int a = ...);
QColor::QColor();

// 参数设置 red, green, blue, alpha, 取值范围都是 0-255
void QColor::setRed(int red);		// 红色
void QColor::setGreen(int green);	// 绿色
void QColor::setBlue(int blue);	// 蓝色
void QColor::setAlpha(int alpha);	// 透明度, 默认不透明(255)
void QColor::setRgb(int r, int g, int b, int a = 255);

int QColor::red() const;
int QColor::green() const;
int QColor::blue() const;
int QColor::alpha() const;
void QColor::getRgb(int *r, int *g, int *b, int *a = nullptr) const;

1.3.4.2 静态API函数

  // 弹出颜色选择对话框, 并返回选中的颜色信息
/*
参数:
    - initial: 对话框中默认选中的颜色, 用于窗口初始化
    - parent: 给对话框窗口指定父对象
    - title: 对话框窗口的标题
    - options: 颜色对话框窗口选项, 使用默认属性即可, 一般不需要设置
*/
  [static] QColor QColorDialog::getColor(
		const QColor &initial = Qt::white, 
		QWidget *parent = nullptr, const QString &title = QString(), 
		QColorDialog::ColorDialogOptions options = ColorDialogOptions());

1.3.4.3 测试代码

场景描述:

  1. 在窗口上放一个标签控件
  2. 通过颜色对话框选择一个颜色, 将选中的颜色显示到标签控件上
  3. 将选中的颜色的 RGBA 值分别显示出来
void MainWindow::on_colordlg_clicked()
{
    QColor color = QColorDialog::getColor();
    QBrush brush(color);
    QRect rect(0, 0, ui->color->width(), ui->color->height());
    QPixmap pix(rect.width(), rect.height());
    QPainter p(&pix);
    p.fillRect(rect, brush);
    ui->color->setPixmap(pix);
    QString text = QString("red: %1, green: %2, blue: %3, 透明度: %4")
            .arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha());
    ui->colorlabel->setText(text);
}

颜色对话框窗口效果展示

[Qt]窗口_第8张图片

测试代码效果展示
[Qt]窗口_第9张图片


1.3.5 QInputDialog

QInputDialog类是QDialog的子类, 通过这个类我们可以得到一个输入对话框窗口
根据实际需求我们可以在这个输入窗口中输入整形, 浮点型, 字符串类型的数据
并且还可以显示下拉菜单供使用者选择。
和前边介绍的对话框类一样, 只需调用这个类的静态成员函数就可以得到想要的窗口了。

1.3.5.1 API - 静态函数

// 得到一个可以输入浮点数的对话框窗口, 返回对话框窗口中输入的浮点数
/*
参数:
  - parent: 对话框窗口的父窗口
  - title: 对话框窗口显示的标题信息
  - label: 对话框窗口中显示的文本信息(用于描述对话框的功能)
  - value: 对话框窗口中显示的浮点值, 默认为 0
  - min: 对话框窗口支持显示的最小数值
  - max: 对话框窗口支持显示的最大数值
  - decimals: 浮点数的精度, 默认保留小数点以后1位
  - ok: 传出参数, 用于判断是否得到了有效数据, 一般不会使用该参数
  - flags: 对话框窗口的窗口属性, 使用默认值即可
*/
[static] double QInputDialog::getDouble(
    		QWidget *parent, const QString &title, 
    		const QString &label, double value = 0, 
    		double min = -2147483647, double max = 2147483647, 
    		int decimals = 1, bool *ok = nullptr, 
    		Qt::WindowFlags flags = Qt::WindowFlags());

// 得到一个可以输入整形数的对话框窗口, 返回对话框窗口中输入的整形数
/*
参数:
  - parent: 对话框窗口的父窗口
  - title: 对话框窗口显示的标题信息
  - label: 对话框窗口中显示的文本信息(用于描述对话框的功能)
  - value: 对话框窗口中显示的整形值, 默认为 0
  - min: 对话框窗口支持显示的最小数值
  - max: 对话框窗口支持显示的最大数值
  - step: 步长, 通过对话框提供的按钮调节数值每次增长/递减的量
  - ok: 传出参数, 用于判断是否得到了有效数据, 一般不会使用该参数
  - flags: 对话框窗口的窗口属性, 使用默认值即可
*/
[static] int QInputDialog::getInt(
    		QWidget *parent, const QString &title, 
    		const QString &label, int value = 0, 
    		int min = -2147483647, int max = 2147483647, 
    		int step = 1, bool *ok = nullptr, 
    		Qt::WindowFlags flags = Qt::WindowFlags());

// 得到一个带下来菜单的对话框窗口, 返回选择的菜单项上边的文本信息
/*
参数:
  - parent: 对话框窗口的父窗口
  - title: 对话框窗口显示的标题信息
  - label: 对话框窗口中显示的文本信息(用于描述对话框的功能)
  - items: 字符串列表, 用于初始化窗口中的下拉菜单, 每个字符串对应一个菜单项
  - current: 通过菜单项的索引指定显示下拉菜单中的哪个菜单项, 默认显示第一个(编号为0)
  - editable: 设置菜单项上的文本信息是否可以进行编辑, 默认为true, 即可以编辑
  - ok: 传出参数, 用于判断是否得到了有效数据, 一般不会使用该参数
  - flags: 对话框窗口的窗口属性, 使用默认值即可
  - inputMethodHints: 设置显示模式, 默认没有指定任何特殊显示格式, 显示普通文本字符串
    - 如果有特殊需求, 可以参数帮助文档进行相关设置
*/
[static] QString QInputDialog::getItem(
    		QWidget *parent, const QString &title, 
    		const QString &label, const QStringList &items, 
    		int current = 0, bool editable = true, bool *ok = nullptr, 
    		Qt::WindowFlags flags = Qt::WindowFlags(), 
    		Qt::InputMethodHints inputMethodHints = Qt::ImhNone);

// 得到一个可以输入多行数据的对话框窗口, 返回用户在窗口中输入的文本信息
/*
参数:
  - parent: 对话框窗口的父窗口
  - title: 对话框窗口显示的标题信息
  - label: 对话框窗口中显示的文本信息(用于描述对话框的功能)
  - text: 指定显示到多行输入框中的文本信息, 默认是空字符串
  - ok: 传出参数, 用于判断是否得到了有效数据, 一般不会使用该参数
  - flags: 对话框窗口的窗口属性, 使用默认值即可
  - inputMethodHints: 设置显示模式, 默认没有指定任何特殊显示格式, 显示普通文本字符串
    - 如果有特殊需求, 可以参数帮助文档进行相关设置
*/
[static] QString QInputDialog::getMultiLineText(
    		QWidget *parent, const QString &title, const QString &label, 
    		const QString &text = QString(), bool *ok = nullptr, 
    		Qt::WindowFlags flags = Qt::WindowFlags(), 
    		Qt::InputMethodHints inputMethodHints = Qt::ImhNone);

// 得到一个可以输入单行信息的对话框窗口, 返回用户在窗口中输入的文本信息
/*
参数:
  - parent: 对话框窗口的父窗口 
  - title: 对话框窗口显示的标题信息
  - label: 对话框窗口中显示的文本信息(用于描述对话框的功能)
  - mode: 指定单行编辑框中数据的反馈模式, 是一个 QLineEdit::EchoMode 类型的枚举值
    - QLineEdit::Normal: 显示输入的字符。这是默认值
    - QLineEdit::NoEcho: 不要展示任何东西。这可能适用于连密码长度都应该保密的密码。
    - QLineEdit::Password: 显示与平台相关的密码掩码字符,而不是实际输入的字符。
    - QLineEdit::PasswordEchoOnEdit: 在编辑时按输入显示字符,否则按密码显示字符。
  - text: 指定显示到单行输入框中的文本信息, 默认是空字符串
  - ok: 传出参数, 用于判断是否得到了有效数据, 一般不会使用该参数
  - flags: 对话框窗口的窗口属性, 使用默认值即可
  - inputMethodHints: 设置显示模式, 默认没有指定任何特殊显示格式, 显示普通文本字符串
     - 如果有特殊需求, 可以参数帮助文档进行相关设置
*/
[static] QString QInputDialog::getText(
    		QWidget *parent, const QString &title, const QString &label,
    		QLineEdit::EchoMode mode = QLineEdit::Normal, 
    		const QString &text = QString(), bool *ok = nullptr, 
    		Qt::WindowFlags flags = Qt::WindowFlags(), 
    		Qt::InputMethodHints inputMethodHints = Qt::ImhNone);

1.3.5.2 测试代码

  • 整形输入框
void MainWindow::on_inputdlg_clicked()
{
    int ret = QInputDialog::getInt(this, "年龄", "您的当前年龄: ", 10, 1, 100, 2);
    QMessageBox::information(this, "年龄", "您的当前年龄: " + QString::number(ret));
}
  • 窗口效果展示:

[Qt]窗口_第10张图片

  • 浮点型输入框
void MainWindow::on_inputdlg_clicked()
{
    double ret = QInputDialog::getDouble(this, "工资", "您的工资: ", 2000, 1000, 6000, 2);
    QMessageBox::information(this, "工资", "您的当前工资: " + QString::number(ret));
}
  • 窗口效果展示:

[Qt]窗口_第11张图片

  • 带下拉菜单的输入框
void MainWindow::on_inputdlg_clicked()
{
    QStringList items;
    items << "苹果" << "橙子" << "橘子" << "葡萄" << "香蕉" << "哈密瓜";
    QString item = QInputDialog::getItem(this, "请选择你喜欢的水果", "你最喜欢的水果:", items, 1, false);
    QMessageBox::information(this, "水果", "您最喜欢的水果是: " + item);
}
  • 窗口效果展示:

[Qt]窗口_第12张图片

  • 多行字符串输入框
void MainWindow::on_inputdlg_clicked()
{
    QString info = QInputDialog::getMultiLineText(this, "表白", "您最想对漂亮小姐姐说什么呢?", "呦吼吼...");
    QMessageBox::information(this, "知心姐姐", "您最想对小姐姐说: " + info);
}

窗口效果展示:
[Qt]窗口_第13张图片

  • 单行字符串输入框
void MainWindow::on_inputdlg_clicked()
{
    QString text = QInputDialog::getText(this, "密码", "请输入新的密码", QLineEdit::Password, "helloworld");
    QMessageBox::information(this, "密码", "您设置的密码是: " + text);
}

窗口效果展示:

[Qt]窗口_第14张图片


1.3.6 QProgressDialog

QProgressDialog类是QDialog的子类, 通过这个类我们可以得到一个带进度条的对话框窗口
这种类型的对话框窗口一般常用于文件拷贝、数据传输等实时交互的场景中。

1.3.6.1 常用API

// 构造函数
/*
参数:
  - labelText: 对话框中显示的提示信息
  - cancelButtonText: 取消按钮上显示的文本信息
  - minimum: 进度条最小值
  - maximum: 进度条最大值
  - parent: 当前窗口的父对象
  - f: 当前进度窗口的flag属性, 使用默认属性即可, 无需设置
*/
QProgressDialog::QProgressDialog(
	QWidget *parent = nullptr, 
	Qt::WindowFlags f = Qt::WindowFlags());

QProgressDialog::QProgressDialog(
	const QString &labelText, const QString &cancelButtonText, 
	int minimum, int maximum, QWidget *parent = nullptr,
	Qt::WindowFlags f = Qt::WindowFlags());


// 设置取消按钮显示的文本信息
[slot] void QProgressDialog::setCancelButtonText(const QString &cancelButtonText);

// 公共成员函数和槽函数
QString QProgressDialog::labelText() const;
void QProgressDialog::setLabelText(const QString &text);

// 得到进度条最小值
int QProgressDialog::minimum() const;
// 设置进度条最小值
void QProgressDialog::setMinimum(int minimum);

// 得到进度条最大值
int QProgressDialog::maximum() const;
// 设置进度条最大值
void QProgressDialog::setMaximum(int maximum);

// 设置进度条范围(最大和最小值)
[slot] void QProgressDialog::setRange(int minimum, int maximum);

// 得到进度条当前的值
int QProgressDialog::value() const;
// 设置进度条当前的值
void QProgressDialog::setValue(int progress);


bool QProgressDialog::autoReset() const;
// 当value() = maximum()时,进程对话框是否调用reset(),此属性默认为true。
void QProgressDialog::setAutoReset(bool reset);


bool QProgressDialog::autoClose() const;
// 当value() = maximum()时,进程对话框是否调用reset()并且隐藏,此属性默认为true。
void QProgressDialog::setAutoClose(bool close);

// 判断用户是否按下了取消键, 按下了返回true, 否则返回false
bool wasCanceled() const;


// 重置进度条
// 重置进度对话框。wascancelled()变为true,直到进程对话框被重置。进度对话框被隐藏。
[slot] void QProgressDialog::cancel();
// 重置进度对话框。如果autoClose()为真,进程对话框将隐藏。
[slot] void QProgressDialog::reset();   

// 信号
// 当单击cancel按钮时,将发出此信号。默认情况下,它连接到cancel()槽。
[signal] void QProgressDialog::canceled();

// 设置窗口的显示状态(模态, 非模态)
/*
参数:
	Qt::NonModal  -> 非模态
	Qt::WindowModal	-> 模态, 阻塞父窗口
	Qt::ApplicationModal -> 模态, 阻塞应用程序中的所有窗口
*/
void QWidget::setWindowModality(Qt::WindowModality windowModality);

1.3.6.2 测试代码

场景描述:

  1. 基于定时器模拟文件拷贝的场景
  2. 点击窗口按钮, 进度条窗口显示, 同时启动定时器
  3. 通过定时器信号, 按照固定频率更新对话框窗口进度条
  4. 当进度条当前值 == 最大值, 关闭定时器, 关闭并析构进度对话框
void MainWindow::on_progressdlg_clicked()
{
    // 1. 创建进度条对话框窗口对象
    QProgressDialog *progress = new QProgressDialog(
                "正在拷贝数据...", "取消拷贝", 0, 100, this);
    // 2. 初始化并显示进度条窗口
    progress->setWindowTitle("请稍后");
    progress->setWindowModality(Qt::WindowModal);
    progress->show();

    // 3. 更新进度条
    static int value = 0;
    QTimer *timer = new QTimer;
    connect(timer, &QTimer::timeout, this, [=]()
    {
         progress->setValue(value);
         value++;
         // 当value > 最大值的时候
         if(value > progress->maximum())
         {
             timer->stop();
             value = 0;
             delete progress;
             delete timer;
         }
    });

    connect(progress, &QProgressDialog::canceled, this, [=]()
    {
        timer->stop();
        value = 0;
        delete progress;
        delete timer;
    });

    timer->start(50);
}

进度窗口效果展示:
[Qt]窗口_第15张图片


1.4 QMainWindow

QMainWindow是标准基础窗口中结构最复杂的窗口, 其组成如下:

  • 提供了菜单栏, 工具栏, 状态栏, 停靠窗口
  • 菜单栏: 只能有一个, 位于窗口的最上方
  • 工具栏: 可以有多个, 默认提供了一个,窗口的上下左右都可以停靠
  • 状态栏: 只能有一个, 位于窗口最下方
  • 停靠窗口: 可以有多个, 默认没有提供, 窗口的上下左右都可以停靠

[Qt]窗口_第16张图片

1.4.1 菜单栏

  • 添加菜单项

关于顶级菜单可以直接在UI窗口中双击, 直接输入文本信息即可, 对应子菜单项也可以通过先双击在输入的方式完成添加,但是这种方式不支持中文的输入

[Qt]窗口_第17张图片

  • 常用的添加方式

一般情况下, 我们都是先在外面创建出QAction对象, 然后再将其拖拽到某个菜单下边, 这样子菜单项的添加就完成了。

[Qt]窗口_第18张图片

  • 通过代码的方式添加菜单或者菜单项
// 给菜单栏添加菜单
QAction *QMenuBar::addMenu(QMenu *menu);
QMenu *QMenuBar::addMenu(const QString &title);
QMenu *QMenuBar::addMenu(const QIcon &icon, const QString &title);

// 给菜单对象添加菜单项(QAction)
QAction *QMenu::addAction(const QString &text);
QAction *QMenu::addAction(const QIcon &icon, const QString &text);

// 添加分割线
QAction *QMenu::addSeparator();
  • 菜单项 QAction 事件的处理

单击菜单项, 该对象会发出一个信号

// 点击QAction对象发出该信号
[signal] void QAction::triggered(bool checked = false);

示例代码

// save_action 是某个菜单项对象名, 点击这个菜单项会弹出一个对话框
connect(ui->save_action, &QAction::triggered, this, [=]()
{
      QMessageBox::information(this, "Triggered", "我是菜单项, 你不要调戏我...");
});

1.4.2 工具栏

1.4.2.1 添加工具按钮

窗口中的工具栏我们经常见到, 并不会为此感到陌生, 如何往工具栏中添加工具按钮?
一共有两种方式

  • 方式1:先创建QAction对象, 然后拖拽到工具栏中, 和添加菜单项的方式相同
    [Qt]窗口_第19张图片

  • 方式2:如果不通过UI界面直接操作,那么就需要调用相关的API函数了

// 在QMainWindow窗口中添加工具栏
void QMainWindow::addToolBar(Qt::ToolBarArea area, QToolBar *toolbar);
void QMainWindow::addToolBar(QToolBar *toolbar);
QToolBar *QMainWindow::addToolBar(const QString &title);

// 将Qt控件放到工具栏中
// 工具栏类: QToolBar
// 添加的对象只要是QWidget或者启子类都可以被添加
QAction *QToolBar::addWidget(QWidget *widget);

// 添加QAction对象
QAction *QToolBar::addAction(const QString &text);
QAction *QToolBar::addAction(const QIcon &icon, const QString &text);

// 添加分隔线
QAction *QToolBar::addSeparator()

通过代码的方式对工具栏进行操作

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 添加第二个工具栏
    QToolBar* toolbar = new QToolBar("toolbar");
    this->addToolBar(Qt::LeftToolBarArea, toolbar);

    // 给工具栏添加按钮和单行输入框
    ui->toolBar->addWidget(new QPushButton("搜索"));
    QLineEdit* edit = new QLineEdit;
    edit->setMaximumWidth(200);
    edit->setFixedWidth(100);
    ui->toolBar->addWidget(edit);
    // 添加QAction类型的菜单项
    ui->toolBar->addAction(QIcon(":/er-dog"), "二狗子");
}

1.4.2.2 工具栏的属性设置

在UI窗口的树状列表中, 找到工具栏节点, 就可以得到工具栏的属性设置面板了
这样就可根据个人需求对工具栏的属性进行设置和修改了。

[Qt]窗口_第20张图片

在Qt控件的属性窗口中对应了一些属性, 这些属性大部分都应了一个设置函数

  • 在对应的类中函数名叫什么?
    • 规律: set+属性名 == 函数名
  • 某些属性没有对应的函数, 只能在属性窗口中设置

1.4.3 状态栏

一般情况下, 需要在状态栏中添加某些控件, 显示某些属性, 用最多的就是添加标签 QLabel

// 类型: QStatusBar
void QStatusBar::addWidget(QWidget *widget, int stretch = 0);

[slot] void QStatusBar::clearMessage();
[slot] void QStatusBar::showMessage(const QString &message, int timeout = 0);
  • 相关的操作代码
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 状态栏添加子控件
    // 按钮
    QPushButton* button = new QPushButton("按钮");
    ui->statusBar->addWidget(button);
    // 标签
    QLabel* label = new QLabel("hello,world");
    ui->statusBar->addWidget(label);
}

1.4.4 停靠窗口

停靠窗口可以通过鼠标拖动停靠到窗口的上、下、左、右,或者浮动在窗口上方。
如果需要这种类型的窗口必须手动添加
如果在非QMainWindow类型的窗口中添加了停靠窗口,那么这个窗口是不能移动和浮动的。
浮动窗口在工具栏中, 直接将其拖拽到UI界面上即可。

[Qt]窗口_第21张图片

  • 停靠窗口也有一个属性面板, 我们可以在其对应属性面板中直接进行设置和修改相关属性。

[Qt]窗口_第22张图片


1.5 资源文件 .qrc

资源文件顾名思义就是一个存储资源的文件,在Qt中引入资源文件好处在于他能提高应用程序的部署效率并且减少一些错误的发生。
在程序编译过程中,添加到资源文件中的文件也会以二进制的形式被打包到可执行程序中,这样这些资源就永远和可执行程序捆绑到一起了,不会出现加载资源却找不到的问题。
虽然资源文件优势很明显,但是它也不是万能的,资源文件中一般添加的都是比较小的资源,比如: 图片,配置文件,MP3等,如果是类似视频这类比较大的文件就不适合放到资源文件中了。

  • 比如我们需要给某个窗口设置图标, 代码如下:
// 创建图标对象
QIcon::QIcon(const QString &fileName);
// QWidget类的 公共成员函数
void setWindowIcon(const QIcon &icon);

// 给窗口设置图标
// 弊端: 发布的 exe 必须要加载 d:\\pic\\1.ico 如果当前主机对应的目录中没有图片, 图标就无法被加载
// 发布 exe 需要额外发布图片, 将其部署到某个目录中
setWindowIcon(QIcon("d:\\pic\\1.ico"));

我们可以使用资源文件解决上述的弊端, 这样发布应用程序的时候直接发布exe就可以, 不需要再额外提供图片了。

介绍一下关于资源文件的创建步骤:

[Qt]窗口_第23张图片

[Qt]窗口_第24张图片
[Qt]窗口_第25张图片
[Qt]窗口_第26张图片

  • 资源文件添加完毕之后, 继续给大家介绍资源文件的使用
  1. 使用资源编辑器打开资源文件

[Qt]窗口_第27张图片

  1. 给资源添加前缀

一个资源文件中可以添加多个前缀, 前缀就是添加的资源在资源文件中的路径, 前缀根据实际需求制定即可, 路径以/开头

[Qt]窗口_第28张图片

  1. 添加文件

前缀添加完毕, 就可以在某个前缀下边添加相关的资源了。

[Qt]窗口_第29张图片

  • 弹出以文件选择对话框, 选择资源文件
    • 资源文件放到什么地方?
      • 放到和 项目文件 .pro 同一级目录或者更深的目录中
      • 错误的做法: 将资源文件放到 .pro文件的上级目录, 这样资源文件无法被加载到
  • 可以给添加的资源文件设置别名, 设置别名之后原来的名字就不能使用了

温馨提示:

  1. 在高版本的QtCreator中, 资源文件名字或者别名不支持中文
  2. 如果设置了中文会出现编译会报错
  3. 在此只是演示, 使用过程中需要额外注意该问题

[Qt]窗口_第30张图片

  1. 如何在程序中使用资源文件中的图片

将项目树中的资源文件节点展开

[Qt]窗口_第31张图片

找到需要使用的资源图片节点, 鼠标右键, 弹出的菜单中选择Copy Path ...

[Qt]窗口_第32张图片


1.6 在Qt窗口中添加右键菜单

如果想要在某一窗口中显示右键菜单, 其处理方式大体上有两种
这两种方式分别为基于鼠标事件实现基于窗口的菜单策略实现
其中第二种方式中又有三种不同的实现方式
因此想要在窗口中显示一个右键菜单共四种实现方式

1.6.1 基于鼠标事件实现

1.6.1.1 实现思路

使用这种方式实现右键菜单的显示需要使用事件处理器函数, 在Qt中这类函数都是回调函数,
并且在自定义窗口类中我们还可以自定义事件处理器函数的行为(因为子类继承了父类的这个方法并且这类函数是虚函数)。

实现步骤如下:

  1. 在当前窗口类中重写鼠标操作相关的的事件处理器函数,有两个可以选择
// 以下两个事件二选一即可, 只是事件函数被调用的时机不同罢了
// 这个时机对右键菜单的显示没有任何影响
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
  1. 在数据表事件处理器函数内部判断是否按下了鼠标右键

  2. 如果按下了鼠标右键创建菜单对象(也可以提前先创建处理), 并将其显示出来

// 关于QMenu类型的菜单显示需要调用的 API
// 参数 p 就是右键菜单需要显示的位置, 这个坐标需要使用屏幕坐标
// 该位置坐标一般通过调用 QCursor::pos() 直接就可以得到了
QAction *QMenu::exec(const QPoint &p, QAction *action = nullptr);

1.6.1.2 代码实现

在头文件中添加要重写的鼠标事件处理器函数声明, 这里使用的是 mousePressEvent()

// mainwindow.h
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

protected:
    // 鼠标按下, 该函数被Qt框架调用, 需要重写该函数
    void mousePressEvent(QMouseEvent *event);

private:
    Ui::MainWindow *ui;
};

在源文件中重写从父类继承的虚函数mousePressEvent()

// mainwindow.cpp
void MainWindow::mousePressEvent(QMouseEvent *event)
{
    // 判断用户按下的是哪一个鼠标键
    if(event->button() == Qt::RightButton)
    {
        // 弹出一个菜单, 菜单项是 QAction 类型
        QMenu menu;
        QAction* act = menu.addAction("C++");
        connect(act, &QAction::triggered, this, [=]()
        {
            QMessageBox::information(this, "title", "您选择的是C++...");
        });
        menu.addAction("Java");
        menu.addAction("Python");
        menu.exec(QCursor::pos()); // 右键菜单被模态显示出来了
    }
}

1.6.2 基于窗口的菜单策略实现

这种方式是使用 Qt 中 QWidget 类中的右键菜单函数
QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy) 来实现,
因为这个函数的参数可以指定不同的值, 因此不同参数对应的具体的实现方式也不同。

这个函数的函数原型如下:

// 函数原型:
void QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy);
参数: 	
  - Qt::NoContextMenu	     --> 不能实现右键菜单
  - Qt::PreventContextMenu   --> 不能实现右键菜单
  - Qt::DefaultContextMenu   --> 基于事件处理器函数 QWidget::contextMenuEvent() 实现
  - Qt::ActionsContextMenu   --> 添加到当前窗口中所有 QAction 都会作为右键菜单项显示出来
  - Qt::CustomContextMenu    --> 基于 QWidget::customContextMenuRequested() 信号实现

1.6.2.1 Qt::DefaultContextMenu

使用这个策略实现右键菜单, 需要借助窗口类从父类继承的虚函QWidget::contextMenuEvent()并重写它来实现。

  • 第一步是在窗口类的头文件中添加这个函数的声明
// mainwindow.h
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

protected:
    // 如果窗口设置了 Qt::DefaultContextMenu 策略, 
    // 点击鼠标右键该函数被Qt框架调用
    void contextMenuEvent(QContextMenuEvent *event);

private:
    Ui::MainWindow *ui;
};
  • 第二步在这个窗口类的构造函数设置右键菜单策略
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 给窗口设置策略: Qt::DefaultContextMenu
    // 在窗口中按下鼠标右键, 这个事件处理器函数被qt框架调用 QWidget::contextMenuEvent()
    setContextMenuPolicy(Qt::DefaultContextMenu);
}
  1. 第三步在这个窗口类的源文件中重写事件处理器函数 contextMenuEvent()
// mainwindow.cpp
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
    // 弹出一个菜单, 菜单项是 QAction 类型
    QMenu menu;
    QAction* act = menu.addAction("C++");
    connect(act, &QAction::triggered, this, [=]()
    {
        QMessageBox::information(this, "title", "您选择的是C++...");
    });
    menu.addAction("Java");
    menu.addAction("Python");
    menu.exec(QCursor::pos());	// 右键菜单被模态显示出来了
}

1.6.2.2 Qt::ActionsContextMenu

使用这个策略实现右键菜单, 是最简单的一种, 我们只需要创建一些 QAction 类型的对象并且将他们添加到当前的窗口中,当我们在窗口中点击鼠标右键这些QAction类型的菜单项就可以显示出来了。
虽然这种方法比较简单,但是它有一定的局限性,就是在一个窗口中不能根据不同的需求制作不同的右键菜单,这种方式只能得到一个唯一的右键菜单。
相关的处理代码如下:

// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 只要将某个QAction添加给对应的窗口, 这个action就是这个窗口右键菜单中的一个菜单项了
    // 在窗口中点击鼠标右键, 就可以显示这个菜单
    setContextMenuPolicy(Qt::ActionsContextMenu);
    // 给当前窗口添加QAction对象
    QAction* act1  = new QAction("C++");
    QAction* act2 = new QAction("Java");
    QAction* act3  = new QAction("Python");
    this->addAction(act1);
    this->addAction(act2);
    this->addAction(act3);
    connect(act1, &QAction::triggered, this, [=]()
    {
         QMessageBox::information(this, "title", "您选择的是C++...");
    });
}

1.6.2.3 Qt::CustomContextMenu

使用这个策略实现右键菜单, 当点击鼠标右键,窗口会产生一个 QWidget::customContextMenuRequested()信号
注意仅仅只是发射信号,意味着要自己写显示右键菜单的槽函数(slot),这个信号是QWidget唯一与右键菜单有关的信号。

来看一下这个信号的函数原型:

// 注意: 信号中的参数pos为当前窗口的坐标,并非屏幕坐标,右键菜单显示需要使用屏幕坐标
[signal] void QWidget::customContextMenuRequested(const QPoint &pos)

代码实现也比较简单, 如下所示:

// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 策略 Qt::CustomContextMenu
    // 当在窗口中点击鼠标右键, 窗口会发出一个信号: QWidget::customContextMenuRequested()
    // 对应发射出的这个信号, 需要添加一个槽函数, 用来显示右键菜单
    this->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this, &MainWindow::customContextMenuRequested, this, [=](const QPoint &pos)
    {
        // 参数 pos 是鼠标按下的位置, 但是不能直接使用, 这个坐标不是屏幕坐标, 是当前窗口的坐标
        // 如果要使用这个坐标需要将其转换为屏幕坐标
        QMenu menu;
        QAction* act = menu.addAction("C++");
        connect(act, &QAction::triggered, this, [=]()
        {
            QMessageBox::information(this, "title", "您选择的是C++...");
        });
        menu.addAction("Java");
        menu.addAction("Python");
        // menu.exec(QCursor::pos());
        // 将窗口坐标转换为屏幕坐标
        QPoint newpt = this->mapToGlobal(pos);
        menu.exec(newpt);
    });
}

在上边的程序中, 我们通过窗口发射的信号得到了一个坐标类型的参数
注意这个坐标是当前窗口的窗口坐标, 不是屏幕坐标,显示右键菜单需要使用屏幕坐标。

对应这个坐标的处理可以有两种方式:

  • 弃用,选择使用 QCursor::pos() 得到光标在屏幕的坐标位置

  • 坐标转换, 将窗口坐标转换为屏幕坐标, 这里用到了一个函数 mapToGlobal

// 参数是当前窗口坐标, 返回值为屏幕坐标
QPoint QWidget::mapToGlobal(const QPoint &pos) const;

不管使用以上哪种方式显示右键菜单, 显示出来之后的效果是一样的

[Qt]窗口_第33张图片

最后如果想要让自己的右键菜单项显示图标, 可以调用这个函数

// 只显示文本字符串
QAction *QMenu::addAction(const QString &text);
// 可以显示图标 + 文本字符串
QAction *QMenu::addAction(const QIcon &icon, const QString &text);

2. 窗口布局

2.1 布局的样式

Qt窗口布局是指将多个子窗口按照某种排列方式将其全部展示到对应的父窗口中的一种处理方式

在Qt中常用的布局样式有三种,分别是:

布局样式 描述 行数 列数
水平布局 布局中的所有的控件水平排列 1行 N列 (N>=1)
垂直布局 布局中的所有的控件垂直排列 N行 (N>=1) 1列
网格(栅格)布局 布局中的所有的控件垂直+水平排列 N行 (N>=1) N列 (N>=1)
有问有答:
    1. 控件的位置可以通过坐标指定, 为什么还要使用布局?
    	- 坐标指定的位置是固定的, 当窗口大小发生改变, 子窗口位置不会变化
    	- 使用坐标指定子窗口位置, 这个控件可能会被其他控件覆盖导致无法显示出来
    	- 使用布局的方式可以完美解决以上的问题
    	- 一般在制作窗口的过程中都是给子控件进行布局, 而不是指定固定坐标位置
    2. 布局有局限性吗, 窗口结构复杂如何解决呢?
        - 没有局限性, 并且布局的使用是非常灵活的
    	- 各种布局是可以无限嵌套使用的, 这样就可以制作成非常复杂的窗口了
    	- 思路是这样的: 给窗口设置布局, 在布局中添加窗口, 子窗口中再设置布局, 
    	  在子窗口布局中再次添加窗口, ......(无限循环)

2.2 在UI窗口中设置布局

在UI窗口中进行布局的设置一共有两种处理方式

2.2.1 方式1

第一种方式是使用Qt提供的布局, 从工具箱中找到相关的布局, 然后将其拖拽到UI窗口中

[Qt]窗口_第34张图片

  • 将相应的控件放入到布局对应的红色框内部, 这些控件就按照布局的样式自动排列到一起了,是不是很方便,赶紧自己操作起来感受一下吧。

[Qt]窗口_第35张图片

  • 除此之外, 我们也可以修改当前布局, 需要先选中当前布局, 然后鼠标右键, 在右键菜单中找布局在其子菜单项中选择其他布局即可

[Qt]窗口_第36张图片


2.2.2 方式2

第二种方式是直接在父窗口中对子部件进行布局, 如果窗口结构很复杂需要嵌套, 那么就需要先将这些子部件放到一个容器类型的窗口中,然后再对这个容器类型的窗口进行布局操作。

  1. 首先需要从工具栏中拖拽一个容器类型的窗口到UI界面上

一般首选QWidget原因是简单, 并且窗口显示之后看不到任何痕迹

[Qt]窗口_第37张图片

  1. 将要布局的子控件放到这个QWidget中

控件拖放过程中无需在意位置和是否对齐, 没有布局之前显示杂乱无序是正常现象。

[Qt]窗口_第38张图片

  1. 对这个QWidget进行布局

首选选中这个存储子部件的父容器窗口, 然后鼠标右键, 在右键菜单中找布局, 通过其子菜单就可以选择需要的布局方式了。布局之后所有的子部件就能够按照对应样式排列了(如果是网格布局, 有时候需要使用鼠标调节一下)

[Qt]窗口_第39张图片

也可以通过窗口上方工具栏提供的布局按钮进行窗口布局

[Qt]窗口_第40张图片


2.2.3 弹簧的使用

弹簧也有对应的属性可以设置, 具体属性如下图所示:
[Qt]窗口_第41张图片

关于弹簧的sizeType属性, 有很多选项, 一般常用的只有两个:
Fixed: 得到一个固定大小的弹簧
Expanding: 得到一个可伸缩的弹簧, 默认弹簧撑到最大

[Qt]窗口_第42张图片


2.2.4 布局属性设置

当我们给窗口设置了布局之后, 选中当前窗口, 就可以看到在其对应的属性窗口中除了有窗口属性, 还有一个布局属性

介绍一下这些属性:

[Qt]窗口_第43张图片


2.2.5 布局的注意事项

通过UI编辑窗口的树状列表我们可以对所有窗口的布局进行检查, 如果发现某个窗口没有布局, 一定要对其进行设置
如果某个窗口没有进行布局, 那么当这个窗口显示出来之后里边的子部件就可能无法被显示出来

[Qt]窗口_第44张图片

一个简单的登录窗口

[Qt]窗口_第45张图片


2.3 通过API设置布局

在QT中, 布局也有对应的类, 布局类之间的关系如下:

[Qt]窗口_第46张图片

在上图中的布局类虽然很多, 常用的布局类有三个, 就前边给大家介绍的三种布局, 对应关系如下:

布局样式 类名
水平布局 QHBoxLayout
垂直布局 QVBoxLayout
网格(栅格)布局 QGridLayout

一般我们不使用这些布局类对窗口进行布局
但还是在这里介绍一下这些类中常用的一些API函数

2.3.1 QLayout

// 在布局最后面添加一个窗口
void QLayout::addWidget(QWidget *w);
// 将某个窗口对象从布局中移除, 窗口对象如果不再使用需要自己析构
void QLayout::removeWidget(QWidget *widget);
// 设置布局的四个边界大小, 即: 左、上、右和下的边距。
void QLayout::setContentsMargins(int left, int top, int right, int bottom);
// 设置布局中各个窗口之间的间隙大小
void setSpacing(int);

2.3.2 QHBoxLayout

这个类中的常用API都是从基类继承过来的

// 创建符窗口对象
QWidget *window = new QWidget;
// 创建若干个子窗口对象
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");

// 创建水平布局对象
QHBoxLayout *layout = new QHBoxLayout;
// 将子窗口添加到布局中
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);

// 将水平布局设置给父窗口对象
window->setLayout(layout);
// 显示父窗口
window->show();

代码效果展示:

在这里插入图片描述


2.3.3 QVBoxLayout

这个类中的常用API都是从基类继承过来的

// 创建符窗口对象
QWidget *window = new QWidget;
// 创建若干个子窗口对象
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");

// 创建垂直布局对象
QVBoxLayout *layout = new QVBoxLayout;
// 将子窗口添加到布局中
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);

// 将水平布局设置给父窗口对象
window->setLayout(layout);
// 显示父窗口
window->show();

代码效果展示:
[Qt]窗口_第47张图片


2.3.4 QGridLayout

// 构造函数
QGridLayout::QGridLayout();
QGridLayout::QGridLayout(QWidget *parent);

// 添加窗口对象到网格布局中
/*
参数:
  - widget: 添加到布局中的窗口对象
  - row: 添加到布局中的窗口对象位于第几行 (从0开始)
  - column: 添加到布局中的窗口对象位于第几列 (从0开始)
  - alignment: 窗口在布局中的对齐方式, 没有特殊需求使用默认值即可
*/
void QGridLayout::addWidget(
	QWidget *widget, int row, int column, 
Qt::Alignment alignment = Qt::Alignment());

/*
参数:
  - widget: 添加到布局中的窗口对象
  - fromRow: 添加到布局中的窗口对象位于第几行 (从0开始)
  - fromColumn: 添加到布局中的窗口对象位于第几列 (从0开始)
  - rowSpan: 添加的窗口从 fromRow 行开始跨越的行数
  - columnSpan: 添加的窗口从 fromColumn 列开始跨越的列数
  - alignment: 窗口在布局中的对齐方式, 没有特殊需求使用默认值即可
*/
void QGridLayout::addWidget(
QWidget *widget, int fromRow, int fromColumn, 
int rowSpan, int columnSpan, 
Qt::Alignment alignment = Qt::Alignment());

// 设置 column 对应的列的最新宽度, 单位: 像素
void QGridLayout::setColumnMinimumWidth(int column, int minSize);

// 设置布局中水平方向窗口之间间隔的宽度
void QGridLayout::setHorizontalSpacing(int spacing);

// 设置布局中垂直方向窗口之间间隔的宽度
void QGridLayout::setVerticalSpacing(int spacing);

测试代码如下:

// 创建父窗口对象
QWidget* window = new QWidget;
// 创建子窗口对象
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");
QPushButton *button6 = new QPushButton("Six");
// 多行文本编辑框
QTextEdit* txedit = new QTextEdit;
txedit->setText("我占用了两行两列的空间哦。");

QGridLayout* layout = new QGridLayout;
// 按钮起始位置: 第1行, 第1列, 该按钮占用空间情况为1行1列
layout->addWidget(button1, 0, 0);
// 按钮起始位置: 第1行, 第2列, 该按钮占用空间情况为1行1列
layout->addWidget(button2, 0, 1);
// 按钮起始位置: 第1行, 第3列, 该按钮占用空间情况为1行1列
layout->addWidget(button3, 0, 2);
// 编辑框起始位置: 第2行, 第1列, 该按钮占用空间情况为2行2列
layout->addWidget(txedit, 1, 0, 2, 2);
// 按钮起始位置: 第2行, 第3列, 该按钮占用空间情况为1行1列
layout->addWidget(button4, 1, 2);
// 按钮起始位置: 第3行, 第3列, 该按钮占用空间情况为1行1列
layout->addWidget(button5, 2, 2);
// 按钮起始位置: 第4行, 第1列, 该按钮占用空间情况为1行3列
layout->addWidget(button6, 3, 0, 1, 3);

// 网格布局设置给父窗口对象
window->setLayout(layout);
// 显示父窗口
window->show();  

测试代码效果展示:

[Qt]窗口_第48张图片


你可能感兴趣的:(Qt,qt,开发语言)