文章目录
- 概述
- 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大小却不随动)。