在使用 QT 开发客户端的过程中,QT 自带的弹窗类实现的功能很简洁,例如 QMessageBox 作为提醒报错等功能弹窗, QInputDialog 作为 int
, string
等需要用户定义值的获取,使用起来很方便,不用自己重新造轮子,但是在实际开发客户端的过程中,为了保证软件整体样式风格一致,需要对这些弹窗的样式也进行统一,这篇文章简单介绍一下这些类修改样式的一些方法。
这篇文章主要以 QInputDialog
为例,其他弹窗都可以使用类似的方法设置样式
非静态函数调用时的样式设置方法
最基本方式
在使用 QInputDialog
获取一个字符串的时候,需要对界面的样式进行调整,可以使用 setStyleSheet()
来设置,简单的代码如下(这里选择了一个简单的 TextInput
类型的 QInputDialog
):
QInputDialog dialog{this, Qt::WindowCloseButtonHint};
dialog.setWindowTitle(tr("创建文件夹"));
dialog.setInputMode(QInputDialog::InputMode::TextInput);
dialog.setTextEchoMode(QLineEdit::Normal);
dialog.setLabelText(tr("请输入文件夹名称:"));
dialog.setOkButtonText(QObject::tr("确定"));
dialog.setCancelButtonText(QObject::tr("取消"));
dialog.setFixedSize(350,250);
dialog.setTextValue("");
// 设置了 QPushButtuon 的三态, QLineEdit 的样式, QLable 和 整体的背景色
QString style = "QPushButton{background:rgb(26,179,148); color:rgb(255,255,255); border-radius:3px; min-height:30px; min-width:60px; font:13px \"Microsoft YaHei\";}"
"QPushButton:hover{background:rgb(24,166,137);}"
"QPushButton:pressed{background:rgb(32,75,148);}"
"QLineEdit{border:2px solid rgb(229,230,231);padding:4px;padding-left:10px;border-radius:3px;color:rgb(105,105,105);font:13px \"Microsoft YaHei\";}"
"QLineEdit:focus{border:2px solid rgb(26,179,148);}"
"QLineEdit:disabled{background-color:rgb(238,238,238);}"
"QLabel{color:rgb(85,85,85); font:12px \"Microsoft YaHei\"; font-weight:bold;}"
"QInputDialog{background-color:rgb(255,255,255); }";
dialog.setStyleSheet(style);
最后得到的样式图大概如下:
现在我们有2个需求:
- 确定 和 取消 这2个按钮设计成不同的样式
- QLable, QLineEidt 以及 QPushButtion 之间布局需要调整,比如间距,占比等
关于第一个需求,我们知道在使用样式表的时候,设置指定对象的样式是可以通过类名加对象名来区分
例如现在有2个 QPushButton , 一个叫 ptn_ok, 另一个是 ptn_cancel, 我们可以通过 QPushButton#ptn_ok
指定其样式
但是现在的问题,QInputDialog 并没有提供接口直接获取确定取消按键
第二个需求中,同样也不能通过 QInputDialog
提供的接口直接拿到 QVBoxLayout 对象,来调整布局
通过 findChild() 方法设置
接下来先简单说一下解决思路
对于 QInputDialog
对象 dialog
使用 findChild()
函数获取指定对象,然后对得到的对象单独设置样式
QInputDialog
中的按钮是在QDialogButtonBox
类中, 之后会有一篇关于QInputDialog
源码分析,主要学习一下这个类的实现逻辑QInputDialog
中的布局是在QVBoxLayout
类中
代码如下:
QInputDialog dialog{this, Qt::WindowCloseButtonHint};
dialog.setWindowTitle(tr("创建文件夹"));
dialog.setInputMode(QInputDialog::InputMode::TextInput);
dialog.setTextEchoMode(QLineEdit::Normal);
dialog.setLabelText(tr("请输入文件夹名称:"));
dialog.setOkButtonText(QObject::tr("确定"));
dialog.setCancelButtonText(QObject::tr("取消"));
dialog.setFixedSize(350,250);
dialog.setTextValue("");
if (QDialogButtonBox *btn_box = dialog.findChild<QDialogButtonBox*>() ){
QString ok_style = "QPushButton{background:rgb(26,179,148); color:rgb(255,255,255); border-radius:3px; min-height:30px; min-width:60px; font:13px \"Microsoft YaHei\";}"
"QPushButton:hover{background:rgb(24,166,137);}"
"QPushButton:pressed{background:rgb(32,75,148);}";
btn_box->button(QDialogButtonBox::Ok)->setStyleSheet(ok_style);
QString cancel_style = "QPushButton{background:rgb(237,85,101); color:rgb(255,255,255); border-radius:3px; min-height:30px; min-width:60px; font:13px \"Microsoft YaHei\";}"
"QPushButton:hover{background:rgb(236,71,88);}"
"QPushButton:pressed{background:rgb(171,71,37);}";
btn_box->button(QDialogButtonBox::Cancel)->setStyleSheet(cancel_style);
}
if (QVBoxLayout * layout = dialog.findChild<QVBoxLayout *>() ) {
layout->setSpacing(10);
}
QString style = "QLineEdit{border:2px solid rgb(229,230,231);padding:4px;padding-left:10px;border-radius:3px;color:rgb(105,105,105);font:13px \"Microsoft YaHei\";}"
"QLineEdit:focus{border:2px solid rgb(26,179,148);}"
"QLineEdit:disabled{background-color:rgb(238,238,238);}"
"QLabel{color:rgb(85,85,85); font:12px \"Microsoft YaHei\"; font-weight:bold;}"
"QInputDialog{background-color:rgb(255,255,255); }";
dialog.setStyleSheet(style);
先展示一下最后的结果
其实2个样式整体区别并不大,这篇文章主要是想说明一下 findChild()
函数,虽然 QInputDialog
并没有提供 QVBoxLayout
和 确定取消
按钮对象,但是可以通过此函数来查找,关于 findChild()
函数具体用法和细节可以参考以下这篇博客
https://blog.csdn.net/liang19890820/article/details/52118210
静态函数调用时的样式设置方法
以 getText() 函数为例,简单看一下静态函数调用的方法
static QString getText(QWidget *parent, const QString &title, const QString &label,
QLineEdit::EchoMode echo = QLineEdit::Normal,
const QString &text = QString(), bool *ok = Q_NULLPTR,
Qt::WindowFlags flags = Qt::WindowFlags(),
Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
参数 | 描述 |
---|---|
QWidget *parent | 弹窗的父对象,后面会根据是否设置了父对象分为2中情况说明样式设置的方法 |
const QString &title | 标题栏信息 |
const QString &label | QLabel 上显示的提示信息 |
QLineEdit::EchoMode echo | EchoMode { Normal, NoEcho, Password, PasswordEchoOnEdit } QLineEdit 展示内容的方式 |
const QString &text | QLineEdit 预展示的内容 |
bool *ok | 返回用户操作QInputDialog的结果,确定为true, 取消或关闭为false |
Qt::WindowFlags flags | 作为 QWidget 的子类,可以设置窗口的一些格式 |
Qt::InputMethodHints inputMethodHints | 输入类控件属性,输入法使用它来检索有关输入法应如何操作的提示,具体用户可以参考官方文档 |
针对于静态函数,重点不是如何设置样式,而是如何获取
QInputDialog
的对象,设置样式可以使用上面提到的2种方法,下面按照是否设置父对象来分情况说明
设置了父对象
bool ok = false;
// 这里捕获对象是 getText() 函数设置的父对象,我这里的父对象是this,所以捕获了this
QTimer::singleShot(0, [this](){
QInputDialog *dialog = this->findChild<QInputDialog *>();
// 我把样式放在了界面的 te_default 控件中,便于调试
QString style = ui->te_default->toPlainText();
dialog->setStyleSheet(style);
});
QString value = QInputDialog::getText(this, tr("创建文件夹"), tr("请输入文件夹名称:"),QLineEdit::Normal, "", &ok, Qt::WindowCloseButtonHint );
未设置父对象
bool ok = false;
// 这里可以不捕获this, 但是因为我使用this指针中的ui部分信息,所以需要捕获 this
QTimer::singleShot(0, [this](){
for(QWidget *widget: QApplication::topLevelWidgets()){
if(QInputDialog *dialog = qobject_cast<QInputDialog*>(widget)){
QString style = ui->te_default->toPlainText();
dialog->setStyleSheet(style);
}
}
});
QString value = QInputDialog::getText(nullptr, tr("创建文件夹"), tr("请输入文件夹名称:"),QLineEdit::Normal, "", &ok, Qt::WindowCloseButtonHint );
总结
- 其实这篇博客真正的收获是对于Qt很多封装好的简易类时,提供的接口是很有限的, 学会使用
findChild()
找到自己要针对操作的对象, 所以除了QInputDialog
类,其他类也可以使用同样的方法 - 这个类在真正使用的时候,推荐自己在重新封装,将样式和一些确定不会改变的图标之类的封装好,便于更简单的调用
- 这个类本身继承自
QDialog
,整体的布局,控件都很简单,可以根据自己实际的业务需求重新实现一下,这样灵活度更高 - 之后会有一篇关于
QInputDialog
的源码分析,太复杂啃起来太累,自己先看看这些简单类的实现逻辑 - 测试的工程地址: https://github.com/catcheroftime/QInputDialogCss