当前位置: 代码迷 >> 综合 >> Qt/GUI/QComboBox 应用实践
  详细解决方案

Qt/GUI/QComboBox 应用实践

热度:83   发布时间:2023-12-12 13:09:45.0

文章目录

  • 概述
  • QComboBox焦点事件
  • QComboBox样式
    • 基本设置
    • QComboBox可编辑样式
    • QComboBox样式表
    • 设置红色字体(告警)
  • 样式效果异常
    • setView 影响样式表效果
    • 延伸-UI绘制-默认字体

概述

GUI写到现在,个人有点不太喜欢使用QComboBox控件,其运行效果有点丑(当然这可能是偏见),在手机移动设备中很少能看见其身影!下边的博文记录主要是给QComboBox设置样式表效果的过程中遇到的问题,并由这些问题做了一些延伸学习!

QComboBox焦点事件

原始场景:之前写过一个列表软件盘(含有QComboBox对象),当时用FocusIn/Out事件处理来判断QLineEdit是否编辑完成,为了不产生额外的焦点事件,设置了整个软件盘窗口及其子窗口的Qt::NoFocus属性,但是依然的,当点击下拉项时,还是产生了额外的焦点变动事件。当时的解决办法是:

ui->cmbLstData->view()->viewport()->setFocusProxy(RecvLineEdit()); 有待重新验证

前几天在处理"ToolBox抢焦点"问题时,由回想到这里,猜测这里的原因可能差不多。那么,先来看看cmb对象的构造层级:

//ui->comboBox->children().size() //==1
//其 metaObject()->className() //== "QStandardItemModel"

QStandardItemModel 对应的obj再取children的结果是0。children的计算来自QObject ( QObject * parent )构造对parent的指定,那么问题来了,子窗哪里去了呢?

QComboBox样式

让我一起来让QComboBox控件变得更漂亮!

基本设置

//测试使用的样式表
QComboBox QAbstractItemView::item
{
    padding: 4px 4px;margin: 3px 3px;height: 35px;
}//委托设置 //多个cmb对象可共用delegate
m_pitemDelegate = new QStyledItemDelegate();
ui->comboBox->setItemDelegate(m_pitemDelegate);

需要注意的是,给QComboBox设置样式表后,若想运行生效,必须为comboBox其对象设置一个QStyledItemDelegate委托(参考)。

QComboBox可编辑样式

在学些QPrintPreviewDialog类时,发现里头QcomboBox组件样式上还不错,想学下?

在这里插入图片描述 在这里插入图片描述
void QPrintPreviewDialogPrivate::init(QPrinter *_printer)
{
     ...zoomFactor = new QComboBox;zoomFactor->setEditable(true);zoomFactor->setMinimumContentsLength(7);zoomFactor->setInsertPolicy(QComboBox::NoInsert);LineEdit *zoomEditor = new LineEdit;zoomEditor->setValidator(new ZoomFactorValidator(1, 1000, 1, zoomEditor));zoomFactor->setLineEdit(zoomEditor);static const short factorsX2[] = {
     25, 50, 100, 200, 250, 300, 400, 800, 1600 };for (auto factorX2 : factorsX2)zoomFactor->addItem(QPrintPreviewDialog::tr("%1%").arg(factorX2 / 2.0));...toolbar->addWidget(zoomFactor);...
} zoomFactor->setEditText(QString::fromLatin1("%1%").arg(factor));

遗憾的是并没有找到什么特殊的样式设置代码,后来测试得出来的结论是setEditable-true设置导致了上述样式变化。

QComboBox样式表

我的第一目标是实现下拉列表框的扁平化风格,最起码的得更可编辑状态下的那个样子! 如下是使用了Qt帮助文档(Qt Style Sheets Examples)中提供的样式表Customizing QComboBox设置后的效果。
在这里插入图片描述

设置红色字体(告警)

//方案1 //效果如右图 
ui->comboBox->setStyleSheet("color:red;");  //不设置委托就能生效//方案1 //效果如左图 
ui->comboBox->setStyleSheet("QComboBox#comboBox {color:red};");  //不设置委托就能生效
#if 0 //实际项目中的代码
QString qss = QString("%1#%2 {color: %3;}").arg(pWdtObjectForStyleSheet->metaObject()->className()).arg(pWdtObjectForStyleSheet->objectName()).arg(QString("rgb(138, 18, 20)"));   //宝石红
//注意不能多次对this执行setStyleSheet
((QWidget*)pWdtObjectForStyleSheet)->setStyleSheet(qss);
#endif
在这里插入图片描述 在这里插入图片描述

样式效果异常

某年某月,在进行HMI显示优化时,按部就班的设置了ComboBox的样式表,设置了委托对象,但在运行时却出现了如下图示的"项重叠"的现象。

异常显示 正常效果应该为 使用的样式设置代码
在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

花费了2个半小时才在独立测例中复现了上述重叠效果,问题代码如下:

int main(int argc, char *argv[])
{
    ...//QLineEdit、QToolButton is the same resultQByteArray btaContent = "QWidget{font:24pt}";//here 不知名的异常影响qApp->setStyleSheet(btaContent);CMyWidget w; w.show();return a.exec();
}CMyWidget::CMyWidget(QWidget *parent) :QWidget(parent), ui(new Ui::CMyWidget)
{
    ui->setupUi(this);this->setStyleSheet("QComboBox QAbstractItemView::item{padding:8px 8px;margin:3px 3px; height:35px;}");m_pItemDelegate = new QStyledItemDelegate();ui->comboBox->setItemDelegate(m_pItemDelegate);ui->comboBox_2->setItemDelegate(m_pItemDelegate);
}

测试分析:

  • 正常情况下,只要对comboBox进行了QStyledItemDelegate委托设置,QComboBox的样式表即可生效正常,若没有委托,则与设置样式表前,显示效果不变。故下边的结论都是在有样式表+委托的情况下进行的。
  • 意外测试发现,若在QComboBox执行setStyleSheet前,先对qApp进行过任何的setStyleSheet设置QComboBox对象font设置不默认父窗使用了委托,满足上述3个条件,此时就会出现上图示项重叠现象,缺一不可。若已执行了qApp->setStyleSheet(.complex.),又想QComboBox自定义样式和委托效果正常,此时必须保证QComboBox对象(eg:comboBox_2)字体设置必须保持与第一级父窗对象同步(即font属性中的小箭头为灰色)。
  • 上述两个问题结论及其解决法都不是分析出来的,是经过多人次测试出来的,具体原因截止目前尚未清楚!由于项目中通常在main函数中会存在qApp的样式设置过程,故约定今后在绘制QComboBox(4.8.6)控件时,尽量保持默认字体(可以先设父窗对象的字体,然后直接拖进小控件)。
    其它结论(在斜体的3条件下):
  • 若绘制时不加入链表项,而是全部动态addItem增加进去,则不会出现重叠异常。

setView 影响样式表效果

至少在4.8.6版本中,使用QStyledItemDelegate委托是解决QComboBox样式表生效问题不错方案。后来在使用自定义View过程中,发现了下边这种方案(setView操作),这里没有进行委托设置,样式表也生效啦,做分析如下:

void QComboBox::setView(QAbstractItemView *itemView)if (itemView->model() != d->model)itemView->setModel(d->model);d->viewContainer()->setItemView(itemView); void QComboBoxPrivateContainer::setItemView(QAbstractItemView *itemView)if (view) {
    ..delete view;  view = 0; ..}

通过上述源码分析节选可看出,QComboBox在设置新视图时,先复制了的数据项,会先将老的干掉,设置为新视图,这个过程与动态增加新item项类似,故,此时就算不使用委托,QComboBox的样式表也可以生效。但是此处却有另一个缺憾:

//在样式表总增加
QComboBox{
    font:20pt Source Code Pro;}  //对所有combobox对象生效,包含默认view对象也生效QListView *pNewView = new QListView();
ui->comboBox_3->setView(pNewView);     //不设委托的情况下可使样式表生效

若样式表中增加,其对于QComboBox原有的view均生效,但是对setView的这个新new的Veiw不生效,这是因为默认的View类型是QComboBoxListView(: public QListView),而我们新setView是QListView。故,此种情况下,请单独设置NewView的字体。后,进一步分析源码也得出,setView的过程中,保留了原view的model,却没有保留原view的font设置。

延伸-UI绘制-默认字体

一个控件字体属性的截图-
在这里插入图片描述
在上述测试过程中,发现了几个以前的认识错误:

  • 点击恢复默认红色小箭头时,所谓的默认是指父窗口的对应设置,而不是.[simSun 9] …
  • 先设置了父窗口的字体属性,后续拖进去的控件,会自动继承。前提是这个控件没有进行过独立的字体属性设置。对控件执行恢复默认后,自动继承的性能重新获得。

验证过程
新建一个UI-Widget,若未进行font属性设置,则在xml中找不到任何的</font/>标签。现设置Widget其字体为Consolas-18,然后,拖两个QComboBox控件(cmb1、cmb2)进去,发现,他们自动继承了父窗Widget的字体属性,打开xml文件,在cmb1/2下均无增</font/>标签。然后,改变其中cmb2的字体设置(字号14、字体、加粗等),重新打开xml文件(仅节选font相关)查看:

 <widget class="QWidget" name="Widget">...<property name="font"><font><family>Consolas</family><pointsize>18</pointsize></font>...<widget class="QComboBox" name="cmb1">...</widget><widget class="QComboBox" name="cmb2">...<property name="font"><font><family>Consolas</family><pointsize>14</pointsize><weight>75</weight><bold>true</bold></font></property>...</widget></widget>...

到这,可基本推测出:
-QtDesigner对ui(xml)文件字体属性的解析,是沿窗口父关系向上查找的,若自己没有font设置则找到父的font标签解析到绘制工具中。且[simSun 9]属于绘制工具持有,不写xml文件。
-QtDesigner中对子控件进行的字体属性恢复默认值得操作,其本质是,从xml中删除了改对象自己独有的font标签。
-透过xml的内容,也不难理解,当再次修改Widget父窗字体时,cmb1随动,cmb2却不在随之变动,除非cmb2进行了恢复默认操作。

关于默认父窗口的问题补充
但是发现,先设置父窗字体,然后拖子部件进去使自动继承,这种操作,很容易造成运行显示异常(遇到过两次啦),具体规律和原因未知,请注意。
QComboBox对象如果没有持有默认(父窗)字体设置,则在样式表和委托的双重作用下,会导致运行异常,现象如上图-重叠(margin生效了view大小却不随动)。