很多人应该和我一样,想做界面才接触的Qt,结果就是做不出来华丽的界面,想给控件上个色?不会,百度半天,好不容易给控件添加了背景色,下一个控件又不会了,别急,这次福利来了,我将平时用到的样式表做了一个总结,并做了一个一键生成,调节数据就可以实时显示,里面包括了Label,LineEdit,PushButton,CheckBox,RadioButton,ScrollBar,Slider,Progressbar,Tabwidget,ToolBox,TabWidget控件的自定义。代码很简单,就是重复写槽函数,但其对于新手的学习很有帮助,避免了盲目,大量的通过百度数据拼接样式表。一来方便学习,所以参数都写在左下角,方便了解到使用了什么生成了什么,二来不用重复造轮子,调节后,可直接将左下角生成的QSS代码复制到qt的样式表里面即可显示效果。可能有一些人会说这是一种偷懒,让人逐渐不想学习,只能说智者见智仁者见仁吧。
然后上面是效果图,哈哈哈,大家如果想要软件,可直接拉到文末地址获取,而这篇文章除了想告诉大家这个一键生成之外,就是凑个字数好上推荐,哈哈,开个玩笑,还是想主要说一下qt里面样式表的一个语法和使用步骤,以及一些经验。随着深入学习,你会渐渐知道阅读文档和自己动手实践才是最高效的学习方法。
无论哪一种添加,都离不开样式表语法,样式表语法由选择器和声明构成,选择器可以指定对谁生效,可以指定不同状态时生效不同的样式,而声明就是样式,这些知识点,下面都将一一说明。
这种方式的优点就是快,直接,缺点也很明显,就是只能设定一次,应用在固定不变的ui上,这种方法是首选,但是当需要根据当前操作做出对应的变化,第一种方法就不行了,例如按一下按钮变一种颜色,或者调节参数,这时我们就需要第二种方法。
比如在Qt Designer 上我们拖了一个标签控件,我们就可以在cpp这样来设置样式表:
ui->Lable->setStyleSheet("background:rgb(150, 190, 60);");
//注意后面的分号要加上,其实就是把第一种方法里面的样式表,加上双引号放在括号里面就可以了。
利用第二种方法就可以随意更改样式表:
if(xxxx)
{
ui->Lable->setStyleSheet("background:rgb(150, 190, 60);");
}
if(xxxx)
{
ui->Lable->setStyleSheet("background:rgb(150, 100, 100);");
}
第二种方法也有缺点,就是耦合性高,为了降低耦合性(与逻辑代码分离),我们通常会定义一个QSS文件,来存放样式,也就是第三种方式。
注意:setStyleSheet的设置以最后一次设置为准,每次设置(调用setStyleSheet(“样式”))都会覆盖之前一次设置的样式。
创建qss文件不要使用windows下的记事本,这里推荐使用Nodetad++或者更高级的编辑器。否则可能会出错。
原因是记事本生成的utf-8文件是带bom(自行百度),这个我们无法通过记事本去掉,而Nodetad++可以,尽管qt在编码项目-编码有一个总是bom的选项,但是经测试,没什么用,bom还是存在,可以看一下这个Qt读取qss文件失败或qss不生效解决方案。
我们创建一个qss后缀的文件,并写入
#label
{
background:rgb(100,100,100);
}
将这个qss文件作为资源文件加载到qt中,如下图,创建一个资源文件,并添加现有文件(qss文件):
在构造函数中编写如下代码:
QString qss;
QFile qssFile("./lib/sheet.qss");
qssFile.open(QFile::ReadOnly);
if(qssFile.isOpen())
{
qss=QLatin1String(qssFile.readAll());
qDebug()<<qss;
this->setStyleSheet(qss);
qssFile.close();
}
最后将utf-8 bom由原先的是utf-8就添加改为目前存在了则保留。至此qss样式表加载完成。
运行结果:
这就是第三方法,这种方法在界面样式较复杂时,我们在文件中编写样式,内容比较清晰,降低耦合性(与逻辑代码分离)。
为了讲解方便,下面的说明将使用第一种方法展开。
qt的官方文档介绍了最有的选择器,而不是最全的,Qt样式表支持CSS2中定义的所有选择器。下面截取了qt支持的选择器,点击浏览CSS2文档。
翻译过来就是,使用的网页翻译,可能有错误。
想要全部介绍,我觉得不现实,所以就拿一些常用的来举例子,各位看官举一反三。
通用选择器可以说是最老实的选择器,它匹配所以的控件。如果通用选择器不是"简单选择器"的唯一组成部分,则可以省略“ *”。比如
*#label
{
background-color:rgb(50,50,50);//声明
}
选择器中除了通用选择器,还有我们下面将要介绍的ID选择器,注意上面那句话:如果通用选择器不是"简单选择器"的唯一组成部分,则可以省略“ *”。所以这里的“*”是可以省略的。
#label
{
background-color:rgb(50,50,50);//声明
}
所达到的效果和上面的效果是一样的。所以也是最简单的选择器。
类型选择器会匹配控件类及其子类的实例,与类选择器的不同的是类选择器匹配控件类实例,但不匹配其子类的实例。
QPushBuuton
{
background-color: rgb(0, 255, 255);
}
当有多个相同控件,例如按钮需要使用一种样式表的时候,就可以使用类型选择器,我们只需要将按钮放在同一个容器中,例如frame,就可以应用到容器中的按钮了,点击Apply,糟糕,是不是没有任何作用?不急,让我们看看文档怎么说。
在按钮控件旁边的说明中,有一个警告,翻译过来就是:
警告:如果仅在QPushButton上设置背景色,除非将border属性设置为某个值,否则背景可能不会出现。这是因为,默认情况下,QPushButton绘制的本机边框与背景色完全重叠。
哦,我们知道了想要让背景生效,就需要指定border这个值,那我们就随便给border加一个值,再点击Apply看看,是不是好了。
QFrame QPushBuuton
{
background-color: rgb(0, 255, 255);
border:none;
}
匹配所有QPushButton实例,它们是QFrame的后代(子代,孙子代等),如图中的QFrame(蓝色)中包含了四个QPushButton,以及一个QWidget(黄色),并且QWidget下面还有两个QPushButton,这样对于QFrame来说,四个QPushButton和QWidget是子代,而QWidget里面的两个QPushButton对于QFrame来说就是孙子代。
QFrame > QPushBuuton
{
background-color: rgb(0, 255, 255);
border:none;
}
匹配所有QPushButton实例,它们是QFrame的直接子代,还是拿上面的图来体现,QFrame QPushBuuton中间加了>符号以后,只有QFrame的直接子代可以匹配。
匹配对象名称为label的所有QLabel实例。
这里的QLabel和通用选择器一个,可以选择省略,因为每个控件的ID(名字)是一样的,无需指定类型,这个没什么难点。
.QPushBuuton
{
background-color: rgb(0, 255, 255);
border:none;
}
匹配QPushButton的实例,但不匹配其子类的实例,这个可以说与类型选择器是一对,就好像后代选择器和子代选择器的关系,这种选择器只会匹配该类的所有对象, 而不会匹配其派生类的对象。
属性选择器应用于同一个类型下不同实现效果(如希望 QPushButton 有两套通用样式),文字好理解,操作起来不一定好理解,我举两个例子帮助大家理解。
QPushButton
{
color:rgb(100,0,0);
border:none;
}
QPushButton[flat = "true"]
{
background-color:rgb(100,100,100);
}
QPushButton[flat = "false"]
{
background-color:rgb(255,255,255);
}
flat=“false”/flat=“true”这两个值,可以随意定义,格式就是“key”对应“value”。(随便说一下这个flat属性,为真的时候就是去掉边框,鼠标按下去才会出现边框,可以提示用户体验。)定义类型属性,需要用到setProperty(“key”,“value”);这个函数,我们来试一下,先创建两个按钮,然后用我们上面说过的第二种样式表添加方法来实现。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->pushButton_3->setProperty("name","zhangsan");
ui->pushButton_4->setProperty("name","lisi");
this->setStyleSheet("QPushButton{color:rgb(255,255,255);}"
"QPushButton[name = 'zhangsan']{background:rgb(0,0,255);}"
"QPushButton[name = \"lisi\"]{background:rgb(255,0,0);}");
//这里的value 用双引号或者单引号都可以
}
伪状态对类型选择器或类选择器指定的所有控件设置它在指定状态时的样式,伪状态以冒号(:)作为分隔 紧跟着选择器,状态伪类很多,上图是从官方截取的,所有的伪状态,都打包进了软件,大家可以下载软件查看,这里就介绍两个初学者常用的,让大家体会到伪状态的用法。
可能细心读者已经发现了,至此,我们上面所说的这些样式是固定样式,通俗一点就是不会动,为了能给用户更好的体验和互交,软件界面的按钮或者什么功能控件,当鼠标滑过或者按下去,控件本身就会发送变化,伪状态就是运用到了这里,我们一起往下看。
#pushButton_4
{
background-color: rgb(78, 110, 255);
border:none;
}
#pushButton_4:hover
{
background-color: rgb(255, 80, 141);
}
声明就是{}号里面的内容,例如:
更多的可以使用软件查看:里面包含了大量声明。包括文末最后的官方链接。
除了像QLabel,QPushButton这一类比较简单的控件,没有子控件,还有复杂控件,他们除了本身,还有属于自己的子控件。
为什么复杂控件需要子控件呢,比如Slider滑条:
如果只是简单控件,那么一旦设置背景颜色,整个滑块和滑块的拇指都是一个颜色,显然对用户不太友好,而将两个简单控件组成复杂控件,这样就可以单独对某一个小控件进行样式调整,大大提示美感。
关于所有的子控件,大家可以点击文末地址,也可以使用软件。
当多个样式规则使用不同的值指定相同的属性时,就会发生冲突。考虑以下样式表:
QPushButton #okButton {
color: gray} QPushButton {
color: red}
这两个规则都匹配被调用的QPushButton实例okButton,并且该color属性存在冲突。要解决此冲突,我们必须考虑选择器的特殊性。在上面的示例中,QPushButton#okButton被认为比更为具体QPushButton,因为它(通常)引用单个对象,而不是类的所有实例。
同样,具有伪状态的选择器比未指定伪状态的选择器更具体。因此,以下样式表指定当鼠标悬停在QPushButton上时,QPushButton应该具有白色文本,而不是红色文本:
QPushButton:hover {
color: white }
QPushButton {
color: red }
为了确定规则的特殊性,Qt样式表遵循CSS2规范:
选择器的特异性计算如下:
计算选择器中ID属性的数量(= a)
计算选择器中其他属性和伪类的数量(= b)
计算选择器中元素名称的数量(= c)
忽略伪元素[即子控件 ]。
将三个数字abc(在基数较大的数字系统中)连接起来可得出特异性。
一些例子:
得到的数字最大者即最终样式,如果数字一样,则以最后样式表为准。
可以在QApplication父窗口小部件和子窗口小部件上设置样式表。通过合并在小部件祖先(父母,祖父母等)上设置的样式表以及在QApplication上设置的任何样式表,可以获取任意小部件的有效样式表。
当发生冲突时,无论冲突规则的特殊性如何,始终要优先于任何继承的样式表使用窗口小部件自己的样式表。同样,父窗口小部件的样式表优先于祖父母的样式表等。
这样的结果之一是,在窗口小部件上设置样式规则会自动赋予它优先于祖先窗口小部件的样式表或QApplication样式表中指定的其他规则的优先级。考虑以下示例。首先,我们在QApplication上设置样式表:
qApp->setStyleSheet("QPushButton { color: white }");
然后,在QPushButton对象上设置样式表:
myPushButton->setStyleSheet("* { color: blue }");
在样式表QPushButton力QPushButton(以及任何子部件)有蓝色的文字,尽管应用程序范围内的样式表提供的更具体规则集。
如果我们写了,结果将是相同的
myPushButton->setStyleSheet("color: blue");
除非QPushButton有子级(这不太可能),否则样式表对它们没有影响。
在经典CSS中,当未明确设置项目的字体和颜色时,它将自动从父项继承。当使用Qt样式表,一个小部件并不会自动从其父继承控件的字体和颜色设置。
例如,考虑QGroupBox内的QPushButton:
qApp->setStyleSheet("QGroupBox { color: red; } ");
该QPushButton没有一个明确的颜色设置。因此,它具有系统颜色,而不是继承其父QGroupBox的颜色。如果要在QGroupBox及其子级上设置颜色,可以编写:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
相反,设置字体并使用QWidget :: setFont()和QWidget :: setPalette()传播到子窗口小部件。