前言
QT设计界面时,难免会遇到修改QCombobox样式的问题。相比于其他的QLabel、QPushButton等控件,QCobobox的样式设置明显困难并复杂很多。以下介绍一下目前为止,本人参考网上代码,制作的一款QCobobox下拉框的样式风格。
效果图:
正文
一、基础样式表
/*QCombobox主体*/
QComboBox {
border: 2px solid #f3f3f3;/*设置线宽*/
background-color: rgb(237, 242, 255);/*背景颜色*/
border-radius: 15px;/*圆角*/
padding: 1px 2px 1px 2px; /*针对于组合框中的文本内容*/
text-align:bottom;
min-width: 9em; /*# 组合框的最小宽度*/
/*min-height: 5em;*/
border-style:solid;/*边框为实线型*/
border-width:2px;/*边框宽度*/
border-color:rgb(77, 123, 255);/*边框颜色*/
padding-left: 10px;/*左侧边距*/
}
/*QCombobox右侧按钮*/
QComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;/*放于右方顶部*/
width: 50px;/*设置按钮范围宽度*/
/*border-radius: 15px;
border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid;*/
border-top-right-radius: 3px;/*设置边框圆角*/
border-bottom-right-radius: 3px;
/*padding-right: 50px;*/
}
/*QCombobox右侧按钮的箭头图标*/
QComboBox::down-arrow {
border-image: url(:/image/down_list.png);/*自定义图片填充*/
width: 10px;/*设置该图标的宽高*/
height: 10px;
}
/* 下拉后,整个下拉窗体样式 */
QComboBox QAbstractItemView {
border: 2px solid #f3f3f3;/*边框宽度、线形、颜色*/
background-color: rgba(237, 242, 255, 1);/*背景颜色*/
border-radius: 15px;/*圆角*/
padding: 1px 2px 1px 2px; /*针对于组合框中的文本内容*/
min-width: 9em; /*# 组合框的最小宽度*/
}
/* 下拉后,整个下拉窗体每项的样式 */
QComboBox QAbstractItemView::item {
border-radius: 15px;/*圆角*/
height: 30px; /* 项的高度(设置pComboBox->setView(new QListView());后,该项才起作用) */
background-color: rgb(237, 242, 255);
}
/*以下部分不知为何不生效,有待调试*/
/* 下拉后,整个下拉窗体越过每项的样式 */
QComboBox QAbstractItemView::item:hover {
color: #FFFFF0;
/* 整个下拉窗体越过每项的背景色 */
background-color: rgb(98, 0, 255);
}
/* 下拉后,整个下拉窗体被选择的每项的样式 */
QComboBox QAbstractItemView::item:selected {
color: #FFFFF0;
background-color: rgb(0, 85, 200);
}
补充:
1.用border-radius: 15px;设置圆角的时候,该控件的高度至少要保证30px,才能显示出圆角,否则为矩形。
2.要在对应的父窗口类初始化代码中,添加setView(new QListView());,下拉框的展开框样式才会生效。
//combobox下拉框样式表生效
ui->comboBox->setView(new QListView());
问题:
1.下拉框的圆角和透明颜色样式无法实现
/* 下拉后,整个下拉窗体样式 */
QComboBox QAbstractItemView {
border: 2px solid #f3f3f3;/*边框宽度、线形、颜色*/
background-color: rgba(237, 242, 255, 1);/*背景颜色*/
border-radius: 15px;/*圆角*/
padding: 1px 2px 1px 2px; /*针对于组合框中的文本内容*/
min-width: 9em; /*# 组合框的最小宽度*/
}
这个问题,初步猜想是因为展开框属于QWidget,可能需要重新自定义一下这个展开框,对它设置一下可透明属性。(有待验证)
2.可选项的悬浮和选中状态样式未生效
/*以下部分不知为何不生效,有待调试*/
/* 下拉后,整个下拉窗体越过每项的样式 */
QComboBox QAbstractItemView::item:hover {
color: #FFFFF0;
/* 整个下拉窗体越过每项的背景色 */
background-color: rgb(98, 0, 255);
}
/* 下拉后,整个下拉窗体被选择的每项的样式 */
QComboBox QAbstractItemView::item:selected {
color: #FFFFF0;
background-color: rgb(0, 85, 200);
}
3.QCombobox的文本无法居中
之前好像稍微查过,实现起来比较麻烦。成功了的朋友可以在评论区交流一下~~
二、ui designer直接添加可选项
如图:
这里可以设置icon大小
三、代码添加可选项
更为常见的,其实是程序在运行过程中,动态对QCombobox填充可选项。比如登录时填充人名数据、选择摄像头分辨率时的分辨率列表等等不固定的元素。
以下是具体操作的代码,有点多但很简单:
//填充下拉选项
ui->comboBox->clear();//清空combobox
QStandardItemModel *pItemModel = qobject_cast<QStandardItemModel*>(ui->comboBox->model());
//字体设置
int combobox_item_fontsize = 9;
QFont font;
//font.setPixelSize(combobox_item_fontsize*scale);
font.setPointSize(combobox_item_fontsize);
font.setFamily("黑体");
//填充默认项(在没有任何数据时,可以先做一个默认的提示项给用户,然后让用户自己输入)
QString tip_string(u8"请选择用户名");
ui->comboBox->addItem(tip_string);
pItemModel->item(0)->setIcon(QIcon(":/image/account.png")); //修改某项图标
pItemModel->item(0)->setForeground(QColor(255, 0, 0)); //修改某项文本颜色
pItemModel->item(0)->setBackground(QColor(220,220,220)); //修改某项背景颜色
pItemModel->item(0)->setFont(font);
pItemModel->item(0)->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter); //修改某项文本对齐方式
//填充正式项
if(ui->comboBox->currentText() == tip_string)
ui->comboBox->clear();
int i= 0;
QStringList m_list;//随便来点填充数据
m_list<<"AAA"<<"BBB"<<"CCC"<<"DDD";
foreach (QString name, m_list)
{
qDebug()<<"combobox additem:"<<name;
ui->comboBox->addItem(name);
pItemModel->item(i)->setIcon(QIcon(":/image/account.png")); //修改某项图标
//pItemModel->item(i)->setText("修改的文本 " + QString::number(i + 1)); //修改某项文本
//pItemModel->item(i)->setForeground(QColor(255, 0, 0)); //修改某项文本颜色
//pItemModel->item(i)->setBackground(QColor(220,220,220)); //修改某项背景颜色(若样式表中已经设置了表项的背景颜色,则不会生效)
pItemModel->item(i)->setFont(font);
pItemModel->item(i)->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter); //修改某项文本对齐方式
i++;
}
//以上设置完,会默认选择第一项。可以手动选择-1项,即为未选择状态
//ui->comboBox->setCurrentIndex(-1);
四、下拉框向上展开
有些时候,我们期望QCombobox的下拉框向上展开,例如控件已经来到屏幕下方边缘,若按照原本的向下展开,则被任务栏遮挡一部分。(虽然超出屏幕边缘这种情况,它好像自动会向上展开,不过总有向上展开的需求hh)
方法就是重新自定义一个类,继承QCombobox类,然后重载showPopup();函数
void showPopup() override;
void myCombox::showPopup()
{
QComboBox::showPopup();
QWidget *popup = this->findChild<QFrame*>();
//针对当前ui布局,计算QCombobox全局坐标
int combobox_y = static_cast<MainWindow *>(this->parent()->parent()->parent()->parent())->y() +
static_cast<QWidget *>(this->parent()->parent()->parent())->y() +
static_cast<QWidget *>(this->parent()->parent())->y()+
static_cast<QWidget *>(this->parent())->y()+
this->y();
if(popup->y() > combobox_y)
{
popup->move(popup->x(),popup->y()-this->height()-popup->height());//x轴不变,y轴向上移动 list的高+combox的高
}else{
//popup->move(popup->x(),popup->y()-this->height()-popup->height());//x轴不变,y轴向上移动 list的高+combox的高
}
}
展开的行为就是QComboBox::showPopup();,展开的窗口则是QWidget (所以其实还可以再优化得更好一点)
然后就是对这个窗口的移动。它肯定有一个默认的展开框的默认位置。
逻辑简单来说就是,先计算一下QCombobox的全局y坐标。然后再对比当前实际展开框的y值,判断其是向上展开还是向下展开的。
如果是向下展开,则手动move一下他,y值减少QCombobox的高度和展开框的高度,就能实现向上展开了。
另外,我计算QCombobox全局坐标的时候笨得要死hh,其实还有其他的算法,你们自己尝试吧。
五、详细样式表(编辑模式的状态)
(参考Qt.QComboBox样式表)
/* 未下拉时,QComboBox的样式 */
QComboBox {
border: 1px solid gray; /* 边框 */
border-radius: 3px; /* 圆角 */
padding: 1px 18px 1px 3px; /* 字体填衬 */
color: #000;
font: normal normal 15px "Microsoft YaHei";
background: transparent;
}
/* 下拉后,整个下拉窗体样式 */
QComboBox QAbstractItemView {
outline: 0px solid gray; /* 选定项的虚框 */
border: 1px solid yellow; /* 整个下拉窗体的边框 */
color: green;
background-color: red; /* 整个下拉窗体的背景色 */
selection-background-color: lightgreen; /* 整个下拉窗体被选中项的背景色 */
}
/* 下拉后,整个下拉窗体每项的样式 */
QComboBox QAbstractItemView::item {
height: 50px; /* 项的高度(设置pComboBox->setView(new QListView());后,该项才起作用) */
}
/* 下拉后,整个下拉窗体越过每项的样式 */
QComboBox QAbstractItemView::item:hover {
color: #FFFFFF;
background-color: lightgreen; /* 整个下拉窗体越过每项的背景色 */
}
/* 下拉后,整个下拉窗体被选择的每项的样式 */
QComboBox QAbstractItemView::item:selected {
color: #FFFFFF;
background-color: lightgreen;
}
/* QComboBox中的垂直滚动条 */
QComboBox QAbstractScrollArea QScrollBar:vertical {
width: 10px;
background-color: #d0d2d4; /* 空白区域的背景色 灰色green */
}
QComboBox QAbstractScrollArea QScrollBar::handle:vertical {
border-radius: 5px; /* 圆角 */
background: rgb(160,160,160); /* 小方块的背景色深灰lightblue */
}
QComboBox QAbstractScrollArea QScrollBar::handle:vertical:hover {
background: rgb(90, 91, 93); /* 越过小方块的背景色yellow */
}
/* 设置为可编辑(setEditable(true))editable时,编辑区域的样式 */
QComboBox:editable {
background: green;
}
/* 设置为非编辑(setEditable(false))!editable时,整个QComboBox的样式 */
QComboBox:!editable {
background: blue;
}
/* 设置为可编辑editable时,点击整个QComboBox的样式 */
QComboBox:editable:on {
background: green;
}
/* 设置为非编辑!editable时,点击整个QComboBox的样式 */
QComboBox:!editable:on {
background: blue;
}
/* 设置为可编辑editable时,下拉框的样式 */
QComboBox::drop-down:editable {
background: lightblue;
}
/* 设置为可编辑editable时,点击下拉框的样式 */
QComboBox::drop-down:editable:on {
background: lightgreen;
}
/* 设置为非编辑!editable时,下拉框的样式 */
QComboBox::drop-down:!editable {
background: lightblue;
}
/* 设置为非编辑!editable时,点击下拉框的样式 */
QComboBox::drop-down:!editable:on {
background: lightgreen;
}
/* 点击QComboBox */
QComboBox:on {
}
/* 下拉框样式 */
QComboBox::drop-down {
subcontrol-origin: padding; /* 子控件在父元素中的原点矩形。如果未指定此属性,则默认为padding。 */
subcontrol-position: top right; /* 下拉框的位置(右上) */
width: 15px; /* 下拉框的宽度 */
border-left-width: 1px; /* 下拉框的左边界线宽度 */
border-left-color: darkgray; /* 下拉框的左边界线颜色 */
border-left-style: solid; /* 下拉框的左边界线为实线 */
border-top-right-radius: 3px; /* 下拉框的右上边界线的圆角半径(应和整个QComboBox右上边界线的圆角半径一致) */
border-bottom-right-radius: 3px; /* 同上 */
}
/* 下拉箭头样式 */
QComboBox::down-arrow {
width: 15px; /* 下拉箭头的宽度(建议与下拉框drop-down的宽度一致) */
background: transparent; /* 下拉箭头的的背景色 */
padding: 0px 0px 0px 0px; /* 上内边距、右内边距、下内边距、左内边距 */
image: url(:/images/combobox_arrow_down.png);
}
/* 点击下拉箭头 */
QComboBox::down-arrow:on {
image: url(:/images/combobox_arrow_up.png); /* 显示下拉箭头 */
}
这个版本他考虑到了编辑模式的状态,而且分得很详细,可以学习一下。
1.遇到的坑
我们知道,如果想要改变QCombobox的字体,只需要setFont()就可以了,也就是
ui->comboBox->setFont(font);
但是当我们选中了可编辑的状态时,往往显示的文本字体却没有发生改变
ui.comboBox->setEditable(true);
//此后字体设置无效
这是因为常态的文本显示和可编辑状态下的显示时不一样的,按照我的理解的话,就是一个是内嵌了QLabel显示文本,另一个是内嵌了QLineEdit,用来进行文字的输入。所以这时,我们的修改对象变成了QCombobox的子对象QLineEdit:
ui->comboBox->lineEdit()->setFont(font);
这样的话,才真正响应设置成功
六、QCombobox内部的布局
按照第五步,我才终于意识到原来QCombobox只是一个集合的容器部件,它的里面其实封装了很多其他的东西,所以它才有什么lineedit和qicon什么的嘛。
那么就衍生出了另一种样式设置的方式,那就是直接设置内部的布局,如下:
QLabel *man = new QLabel(this);
man->setFixedSize(20*scale, 20*scale);
man->setCursor(QCursor(Qt::ArrowCursor));
man->setPixmap(QPixmap(":/login/icon_account.png"));
man->setScaledContents(true);
QSpacerItem *spaceItem_name = new QSpacerItem(100, 10, QSizePolicy::Expanding);
QHBoxLayout *editLayout_name = new QHBoxLayout();
editLayout_name->setContentsMargins(14*scale, 0, 0, 0);
editLayout_name->addWidget(man);
editLayout_name->addSpacerItem(spaceItem_name);
ui.comboBox->setLayout(editLayout_name);
如果你有手动代码布局的经验的话,应该能轻松看懂这种设置的方式。我这段代码的话,就是先用QLabel创建一个图标,然后加一条弹簧,在把layout设置进去。用这种做法的话,效果和直接样式表的设置一样,甚至更好更灵活。
比如如果拖拉窗口改变了QCombobox的尺寸,那顺带着也可以改变图标的尺寸了,你不想改变的话就Fix设置定死,但这种方式在跨屏的、不同分辨率底下显示器,需要完美适配的情况下,能够省下不少工作量。
你也可以做成左边图标,中间弹簧,右边下拉箭头的图标,这样只需要在样式表里面设置文本左右的间距,就能完美实现你想要的任何布局了。