Qt/GUI/QComboBox 应用实践

概述

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 result
    QByteArray 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大小却不随动)。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值