1.概述
此篇文章主要用来记录自己在使用Qt框架开发应用的过程中遇到的问题及解决方法(需求及实现方式),同时也会记录一些开发心得,持续记载中…
2.问题及解决方法
- 问题: 无法使用使用属性样式选择器([enabled]=“false”)在样式中设置控件不可用时的样式。
解决方法:使用控件 :disabled 伪状态设置不可用时的样式。
- 问题: 直接使用css中的渐变色样式来设置控件背景渐变色没有效果。
解决方法:由qss与css的区别导致的,直接先在QtDesigner中添加渐变色(QtDesigner会自动生成渐变色的样式),然后修改需要用到的目标颜色即可。
- 问题: QMessageBox中按钮文字语言为英文。
解决方法:主要方法是加载翻译对象,解决过程如下,最后展示如何加载翻译对象。
①先将Qt安装目录下的中文翻译文件(qt_zh_CN.qm)拷贝到工程目录下(翻译文件源路径为:“F:\Software\Qt\5.15.2\msvc2019_64\translations\qt_zh_CN.qm”,其中前面部分为Qt的安装路径)
②调用QApplication对象的installTranslator(QTranslator* translator)加载翻译对象。
③此时如果弹窗还是英文的话,表示上面的翻译文件没有包含QMessageBox弹窗部分,则需要将另外一个翻译文件(qtbase_zh_CN.qm)也加载进来(文件路径为:https://github.com/doufu3344/qtbase_zh)。
int main(int argc,char* argv[]){
QApplication application(argc,argv);
//加载qt_zh_CN.qm
QTranslator qtTranslator;
qtTranslator.load(":/language/qt_zh_CN.qm");
application.installTranslator(&qtTranslator);
//如果上面代码未能解决问题,则需要追加下面部分,加载qtbase_zh_CN.qm
QTranslator qtbaseTranslator;
qtbaseTranslator.load(":/language/qtbase_zh_CN.qm");
application.installTranslator(&qtbaseTranslator);
}
3.开发手册
3.1控件样式定义
将控件样式统一记录在 .qss 文件中,然后在应用程序初始化时读取该文件内容并赋值给qApp对象,完成样式初始化。这样做的好处是:避免了在许多处代码中定义各种样式字符串,方便统一管控。然而,在窗口设计时,允许在QtDesigner中定义各个控件的样式,因为这样方便及时查看效果,提高设计效率。因此,.qss文件适合记录一些公共子控件的样式,比如菜单样式、滚动条样式等。(使用vscode打开.qss文件,会有语法高亮的效果,比在QtCreator里面编辑方便得多)。
3.2qss语法(选择器、子控件、伪状态)
表格来源为Qt QSS选择器简介,Qss语法文档为The Style Sheet Syntax,Qss参考手册为Qt Style Sheets Reference。
1. 选择器
选择器 | 示例 | 说明 |
---|---|---|
通用选择器 | * | 匹配所有部件 |
类型选择器 | QPushButton | 匹配QPushButton及其子类的实例 |
属性选择器 | QPushButton[flat=“false”] | 匹配所有属性flat属性为false的对象 |
类选择器 | .QPushButton | 匹配QPushButton实例,不包含子类 |
ID选择器 | QPushButton#btnOk | 匹配对象名为btnOk的对象 |
子选择器 | QDialog>QPushButton | 匹配作为QDialog的直接子级的QPushButton实例 |
后代选择器 | QDialob QPushButton | 匹配作为QDialog的后代(子、孙等)的QPushButton实例 |
2. 子控件
代码 | 说明 |
---|---|
QComboBox QAbstractItemView::item | 设置QComboBox下拉框子项样式 |
QScrollBar::handle | 设置滚动条滑块样式 |
… | … |
3. 伪状态
代码 | 说明 |
---|---|
QPushButton:hover | 设置QPushButton鼠标悬浮状态下的样式 |
QPushButton:disabled | 设置QPushButton不可用状态下的样式 |
… | … |
3.3设计QToolButton不同状态图标及样式
方式1:使用qss中的background属性设置控件不同状态下的样式。
方式2:使用监听器,通过调用按钮的installEventFilter函数,将按钮的事件委托给其他窗口(一般是父窗口)监听,其他窗口在eventFilter函数中处理QEvent:Enter与QEvent:Leave两个事件来处理按钮图标及样式。
/*方式1*/
QToolButton { background:url(:/image/ui/uires/normal.png) no-repeat center center; }
QToolButton:hover { background:url(:/image/ui/uires/hover.png) no-repeat center center;}
/*方式2*/
/**
* @brief 添加按钮函数
*/
void ExampelWidget::AddButton(const QString& text,const QIcon& icon){
QToolButton* button = new QToolButton(this);
button->setText(text);
button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
button->setIcon(icon);
button->setIconSize(QSize(27, 27));
button->installEventFilter(this);//委托给当前对象处理按钮的事件
}
/**
* @brief 重写QWidget的事件处理函数
*/
bool ExampleWidget::eventFilter(QObject *watched, QEvent *event){
QToolButton *button = qobject_cast<QToolButton *>(watched);
if (button != nullptr) {
if (event->type() == QEvent::Enter) {
button->setIcon(m_icons[button].pixmap(button->iconSize(), QIcon::Active));
} else if (event->type() == QEvent::Leave) {
button->setIcon(m_icons[button].pixmap(button->iconSize(), QIcon::Normal));
}
}
return QWidget::eventFilter(watched, event);
}
3.4设计QTableWidget表头居中自动换行
table->horizontalHeader()>setDefaultAlignment(Qt::AlignCenter | (Qt::Alignment)Qt::TextWordWrap);
3.5操作Excel表格文件
使用开源库QtXlsxWriter。下面写明如何在项目中配置使用QtXlsxWriter。(需要更详细说明的可以参照文章QtXlsx使用方法(强大的Excel))。
- 克隆QtXlsxWriter项目到本地,进行编译,版本要与主项目一致。
- 拷贝编译后的include文件夹到本地Qt安装目录下的include目录。(如:D:\3rdCode\QtXlsxWriter\QtXlsxWriter-master\build-qtxlsx-Desktop_Qt_5_15_2_MSVC2019_32bit-Debug\include\QtXlsx ⇒ F:\Software\Qt\5.15.2\msvc2019\include)
- 拷贝编译后的lib文件夹下面的 ** Qt5Xlsxd.lib Qt5Xlsx.lib Qt5Xlsx.prl Qt5Xlsxd.prl ** 4个文件到本地Qt安装目录下的lib目录。(如:D:\3rdCode\QtXlsxWriter\QtXlsxWriter-master\build-qtxlsx-Desktop_Qt_5_15_2_MSVC2019_32bit-Debug\lib ⇒ F:\Software\Qt\5.15.2\msvc2019\lib)。
- 拷贝编译后的lib文件夹下面的 ** Qt5Xlsx.dll Qt5Xlsxd.dll ** 2个文件到本地Qt安装目录下的bin目录。(如:D:\3rdCode\QtXlsxWriter\QtXlsxWriter-master\build-qtxlsx-Desktop_Qt_5_15_2_MSVC2019_32bit-Debug\lib ⇒ F:\Software\Qt\5.15.2\msvc2019\bin)
- 拷贝编译后的mkspecs\modules文件夹下面的 qt_lib_xlsx.pri 1个文件到本地Qt安装目录下的mkspecs\modules目录。(如D:\3rdCode\QtXlsxWriter\QtXlsxWriter-master\build-qtxlsx-Desktop_Qt_5_15_2_MSVC2019_32bit-Debug\mkspecs\modules ⇒ F:\Software\Qt\5.15.2\msvc2019\mkspecs\modules)
- 如果项目使用qmake编译,则直接在.pro文件里面加入QT+=xlsx,则可以使用了该库了。
- 如果项目使用cmake编译,则需要先拷贝编译后的lib/cmake/Qt5Xlsx文件夹到本地Qt安装目录下的lib/cmake目录下,再在CMakeLists文件中引入该库。(如:D:\3rdCode\QtXlsxWriter\QtXlsxWriter-master\build-qtxlsx-Desktop_Qt_5_15_2_MSVC2019_32bit-Debug\lib\cmake\Qt5Xlsx ⇒ F:\Software\Qt\5.15.2\msvc2019\lib\cmake)。
3.6项目使用过的qss样式
/* 设置表格滚动条样式 */
QTableView QScrollBar:vertical {
background: #d8dfe7;
width: 12px;
border-radius: 6px;
}
QTableView QScrollBar::handle:vertical {
background: #a2a1ab;
width: 12px;
border-radius: 6px;
min-height: 100px;
}
QTableView QScrollBar::handle:vertical:hover {
background: #82818b;
}
QTableView QScrollBar:horizontal {
background: #d8dfe7;
height: 12px;
border-radius: 6px;
}
QTableView QScrollBar::handle:horizontal {
background: #a2a1ab;
height: 12px;
border-radius: 6px;
min-width: 100px;
}
QTableView QScrollBar::handle:horizontal:hover {
background: #82818b;
}
/* 设置下拉框子项样式 */
QComboBox QAbstractItemView::item {
min-height: 25px;
padding-left: 3px;
}
/* 设置表格中文件上传按钮样式(带下划线的按钮,鼠标手样式只能通过代码设置) */
QPushButton[filebutton]{
border: none;
background: none;
text-decoration: underline;
color: #008b45;
}
/* 设置表头排序按钮样式 */
#LibraryPanel QHeaderView[direction="horizontal"]::down-arrow {
subcontrol-position: center right;
image: url(:/ui/ui/down.png);
padding-right: 8px;
}
#LibraryPanel QHeaderView[direction="horizontal"]::up-arrow {
subcontrol-position: center right;
image: url(:/ui/ui/up.png);
padding-right: 8px;
}
/* 设置QTabWidget模块样式 */
#tabWidgetModule::pane {
border-top: 1px solid #97aad3;
border-bottom: 1px solid #97aad3;
position: absolute;
top: -1px;
padding-left: 20px;
background-color: #cfd6e5;
}
#tabWidgetModule > QTabBar::tab {
min-width: 100px;
min-height: 30px;
border-bottom: 1px solid #97aad3;
background-color: #eceff6;
font-size: 14px;
color: #000000;
}
#tabWidgetModule > QTabBar::tab:selected {
background-color: #293955;
color: #ffffff;
}
#tabWidgetModule > QTabBar::tab:hover {
font-size: 16px;
}
3.7控制QAbstractTableModel某列不可编辑
通过派生QAbstractTableModel,重写Qt::ItemFlags flags(const QModelIndex& index) const函数来实现。
/* MyTableModel.cpp */
//重写flags函数
Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const{
auto flags = QAbstractTableModel::flags(index);
int columnNoEditable=3;//不可编辑的列,自己定义
if(index.column()==3){
return flags;//不可编辑
}else{
return flags|Qt::ItemIsEditable;//可编辑
}
}
注意:如果表格列column是不可编辑的,则将无法给该列的所有单元格赋值,代码赋值也不行,即model->setData(row,column,“value”)是无效的。
解决的方法:在setData之前先设置单元为可编辑状态,赋值后再改为不可编辑状态。
3.8QTableView+QSqlTableModel实现排序功能
1. 实现方式
主要编码:调用QHeaderView的setSortIndicator、setSortIndicatorShown函数、QSqlTableModel的sort函数,将QHeaderView排序状态切换信号(sortIndicatorChanged)绑定到QSqlTableModel数据切换排序(自己写)。
//需要排序且表格显示时初始排序的列
int columnSorted=0;
//设置初始排序的列,排序方式为升序
ui->tableView->horizontalHeader()->setSortIndicator(columnSorted, Qt::SortOrder::AscendingOrder);
//显示排序按钮,按钮样式可以通过QHeaderView::down-arrow、QHeaderView::up-arrow设置
ui->tableView->horizontalHeader()->setSortIndicatorShown(true);
//进行排序
m_sqlTableModel->sort(m_sortFieldColumns.at(0),::SortOrder::AscendingOrder);
//信号绑定
connect(ui->tableView->horizontalHeader(),::sortIndicatorChanged,
this,[=](int logicalIndex, Qt::SortOrder order) {
m_sqlTableModel->sort(logicalIndex, order);
});
2. 解决问题
由于QHeaderView的setSortIndicatorShown(true)函数调用后会,表格所有列都将出现排序按钮,因此需要解决这个问题,解决方法:派生QHeaderView,重写mousePressEvent、mouserReleaseEvent函数,在函数中将非排序列的表头点击事件过滤掉,最后为QTableView设置自定义的表头。
/* MyTableHeaderView.cpp */
MyTableHeaderView::SortTableHeaderView(const std::vector<int> &sortColumns,
QWidget *parent)
: QHeaderView(Qt::Horizontal, parent) {
m_sortColumns = sortColumns;//排序列集合
}
void MyTableHeaderView::mousePressEvent(QMouseEvent *e) {
const int index = logicalIndexAt(e->pos());
if (std::find(m_sortColumns.begin(), m_sortColumns.end(), index) ==
m_sortColumns.end()) {
setSectionsClickable(false);
}
QHeaderView::mousePressEvent(e);
setSectionsClickable(true);
}
void MyTableHeaderView::mouseReleaseEvent(QMouseEvent *e) {
const int index = logicalIndexAt(e->pos());
if (std::find(m_sortColumns.begin(), m_sortColumns.end(), index) ==
m_sortColumns.end()) {
setSectionsClickable(false);
}
QHeaderView::mouseReleaseEvent(e);
setSectionsClickable(true);
}