• Qt 外观之一 ——Qt Style Sheet


    Qt Style Sheet

    目录

    使用

    对于应用程序

    创建自定义控件

    QSS语法

    一般选择器(selector)

    伪选择器

    解决冲突

    使用specificity

    Namespace冲突

    级联效应

    设置对象属性

    comments

    STYLE基础知识的说明

    CSS 背景属性(Background)

    CSS 边框属性(Border 和 Outline)

    Box 属性

    CSS 字体属性(Font)

    CSS 外边距属性(Margin)

    CSS 内边距属性(Padding)

    CSS 定位属性(Positioning)

    CSS 文本属性(Text)

    借用css 的灵感, Qt也支持Qt自己的css, 简称qss。同css 相似,qss的主要功能与最终目的都是能使界面的表现与界面的元素分离,即质与形的分离,就如同一个人可以在不同的时候穿上不同的衣服一样,css机制的引入,使得设计一种皮肤与界面控件分离的软件成为可能,应用程序也能像web界面那样随意地改变外观。

                   

    使用

    对于应用程序

    特定应用的QSS效果可以使用命令-styele定制,也可以使用 QApplication::setStyle()定义。 特定的widgets 可以使用  QWidget::setStyle() 定义。   

    创建自定义控件

    在Widget的paint方法里使用 QStyle::drawItemText(pixmap...), 可以以一些格式绘制文字、图片等, 相对于使用QPainter, 简单许多。

    例如, 使用 Delegate 绘制QQ 好友列表(Model View 略)

    void ShareeSignatureDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const

    {

    // 沿用父类的绘制效果

    QStyledItemDelegate::paint(painter, option, index);

       

    painter->save();

       

    // modedata接口里根据role获取姓名和签名内容(NameRole,SignatureRoledata接口里自定义的角色名)

    QString sharerName = index.data(NameRole).toString();

    QString signature = index.data(SignatureRole).toString();

       

    QStyleOptionViewItemV4 optV4 = option;

    initStyleOption(&optV4, index);

    const QWidget *widget = optV4.widget;

    QStyle *style = widget ? widget->style() : QApplication::style();

       

    QRect rect = style->subElementRect(QStyle::SE_ItemViewItemText, &optV4, widget);

       

    // 定义起始位置

    rect = rect.adjusted(5, 0, 0, - optV4.rect.height() / 2);

       

    // 设置文字的长度,以及文字过长时的省略模式

    QFontMetrics fontMetrics = painter->fontMetrics();

    QString elidedNameLine = fontMetrics.elidedText(sharerName, Qt::ElideMiddle, rect.width());

    QString elidedSignatureLine = fontMetrics.elidedText(signature, Qt::ElideMiddle, rect.width());

       

    // 执行文字绘制

    style->drawItemText(painter, rect, Qt::AlignBottom, QApplication::palette(), true, elidedNameLine);

    // 设置签名内容的位置,使得该内容在姓名的下方

    rect.moveTop(rect.bottom());

    painter->setPen(QColor("#949fa5"));

    if(!signature.isEmpty())

    style->drawItemText(painter, rect, Qt::AlignVCenter, QApplication::palette(), true, elidedSignatureLine);

    else

    style->drawItemText(painter, rect, Qt::AlignVCenter, QApplication::palette(), true, "T???息¢");

       

    painter->restore();

    }

    QWidget 的绘制使用双缓冲技术, 即, 前景与背景分离, 所以可以根据自己的需要仅仅定制前景或者背景。 也可以定义自己QPalette, 并且制定它的PaletteRole,Widgets 使用双缓冲技术绘制, 可以固定前景去修改背景, 也可以固定背景去修改前景。 对于实时性要求比较高、绘制量大的系统, 例如 图表 和 曲线, 可以使用 palette 设定背景,然后在 paintEvent 里面绘制关注的内容即可。 palette的绘制会先于paintEvent, 而且 palette 使用 Implicit Sharing, 它的使用比较有效。 

    this->setBackgroundRole( QPalette::Window);

    this->setAutoFillBackground(true);

       

    QPalette palette (this->palette());

    palette.setColor(QPalette::Background, Qt::black);

    this->setPalette( palette );

    /*

    重写 paintEvent

    */

    QSS语法

    同css一样,他也有由一个selector与一个declaration组成,selector指定了是对哪一个控件产生效果,而declaration才是真正的产生作用的语句。如:
    QPushButton { color: red }
    QPushButton指定了是对所有的QPushButton或是其子类控件(如用户定义的MyPushButton)产生影响,而color:red表明所有的受影响控件的前景色都为red。
    除了"类名","对象名","Qt属性名"这三样东西是大小写敏感的外其他的东西都是大小写不敏感的,如red 与Red代表同一属性。

    declaration部份是一系列的(属性:值)对,使用分号(;)将各个不同的属性值对分开,使用大括号({})将所有declaration包含在一起。

     

    一般选择器(selector)

    Qt支持所有的CSS2定义的选择器(链接类型除外),其祥细内容可以在w3c的网站上查找http://www.w3.org/TR/CSS2/selector.html , 其中比较常用的selector类型有:

    通用类型选择器:* 会对所有控件有效果。

    类型选择器:QPushButton匹配所有QPushButton的实例和其子类的实例。

    属性选择器:QPushButton[flat="false"]匹配所有QPushButton属性flat为false的实例,属性分为两种,静态的和动态的,静态属性可以通过Q_PROPERTY() 来指定,来动态属性可以使用setProperty来指定,如:
    QLineEdit *nameEdit = new QLineEdit(this);
    nameEdit->setProperty("mandatoryField", true);
    如果在设置了qss后Qt属性改变了,需要重新设置qss来使其生效,可以使用先unset再set qss

    类选择器:.QPushButton匹配所有QPushButton的实例,但不包含其子类,这相当于:*[class~="QPushButton"]     ~=的意思是测试一个QStringList类型的属性是否包含给定的QString

    ID选择器:QPushButton#okButton对应Qt里面的object name设置,使用这条CSS之前要先设置对应控件的object name为okButton,如:Ok->setObjectName(tr("okButton"));

    后裔选择器:QDialog QPushButton匹配所有为QDialog后裔(包含儿子,与儿子的儿子的递归)为QPushButton的实例

    子选择器:QDialog > QPushButton匹配所有的QDialog直接子类QPushButton的实例,不包含儿子的儿子的递归。

    伪选择器:有两种; 一种用于选择Qt提供的Widgets类的子控件,一种用于选择自定义状态, 如QCheckBox:::checked 表示被勾选的复选框。 see   Qt Style Sheets Reference, 这里提供了Qt 所有的可以使用QSS 的 Widgets 及其分类, 以及其属性列表。 

     

    伪选择器

    • 表示状态的伪选择器

    以冒号(:)表示,与css里的伪选择器相似,是基于控件的一些基本状态来限定程序的规则,如hover规则表示鼠标经过控件时的状态,而press表示按下按钮时的状态。如:

    QPushButton:hover {

    Background-color:red;

    }

          表示鼠标经过时QPushButton背景变红。

    Pseudo还支持否定符号(!),如:                

    QRadioButton:!hover { color: red }

    表示没有鼠标移上QRadioButton时他显示的颜色是red。

    Pseudo支持状态的有效与、与非,有效或、或非比如:

    QPushButton:hover:!pressed { color: blue; }

    QCheckBox:hover, QCheckBox:checked { color: white }

         前者表示QPushButton在鼠标移上却没有点击时显示blue字,但如果点击的时候就不会显示blue颜色了。后者表示 checkbox 在被选中或者鼠标移动到其上时, 颜色变为白色

                同样可以和以下所述的子控件选择器一起联合使用,如:

    QSpinBox::down-button:hover { image: url(btn-combobox-press.bmp) }

    与前面所讲的一样,伪选择器,子控件选择器等都是可以用逗号(,)分隔表示连续相同的设置的,如:
    QPushButton:hover,QSpinBox::down-button, QCheckBox:checked { color: white ;image: url(btn-combobox-press.bmp);} 表示如下
    更多请参考:http://pepper.troll.no/s60prereleases/doc/stylesheet-reference.html#list-of-pseudo-states

     

    • 表示子控件的伪选择器

    对于复杂的控件,可能会在其中包含其他子控件,如一个QComboxBox中有一个drop-down的按钮。那么现在如果要设置QComboxBox的下拉按钮的话,就可以这样访问:

    QComboBox::drop-down { image: url(dropdown.png) }

         子控件选择器是用位置的引用来代表一个元素,这个元素可以是一个单一控件或是另一个包含子控件的复合控件。使用subcontrol-origin属性可以改变子控件的默认放置位置,如:

    QComboBox {

    margin-right: 20px;

    }

    QComboBox::drop-down {

    subcontrol-origin: margin;

    }

       如上语句可以用来改变drop-down的margin。

       相对位置属性可以用来改变子控件相对于最初位置的偏移量,如当一个QCombox的drop-down按钮被按下时,我们可以用一个内部的小偏移量来表示被按下的效果,如下:

    QComboBox::down-arrow {

    image: url(down_arrow.png);

    }

    QComboBox::down-arrow:pressed {

    position: relative;

    top: 1px; left: 1px;

    }

       绝对位置属性允许子控件改变自己的位置和大小而不受引用元素的控件。一但位置被设定了,这些子控件就可以被当成一般的widget来对待,并且可以使用合模型。关于合模型可以参考下面。

    如果你要查看Qt支持哪些子控件选择器,可以参考:http://pepper.troll.no/s60prereleases/doc/stylesheet-reference.html#list-of-sub-controls-syntax.html

    解决冲突

    使用specificity

    在程序里面要先设置控件的,如:

    btnOne = new QPushButton(centralWidget);

    btnOne->setObjectName(QString::fromUtf8("btnOneCh"));

    这样,我们有了一个object name为btnOneCh的QPushButton,试验一下,使用指定object name的方式,如:

    QPushButton#btnOneCh { color: red }

    QPushButton { color: white }

    可以看出,btnOncCh的color变成了red。 

    使用伪选择器,如hover,press,enabled等,如:按扭"1"是disable了的,QPushButton:!enabled {color: white}

    QSS 使用 specificity 来确定控件的样式, 这一点跟 CSS 相同, 如上例中,前者的 specificity 大于后者 。 又如:

    QPushButton:hover:enabled { color: white }

    QPushButton:enabled { color: red }

                前者的 specificity 大于后者;

    所有的类型选择器都有一个共同的特性,就是如果有两个属性冲突了的话就会以最后出现的一个为准,如:

    QPushButton:hover { color: white }

    QPushButton:enabled { color: red }

                这个例子本来意思是pushbutton 正常时是红色, 鼠标进入时是白色, 但是由于两行代码的specificity 相同,终的结果是第一行效果被覆盖。 

            又如

    QPushButton { color: red }

    QAbstractButton { color: gray }

      人们会想当然以为 pushbutton的 颜色会是红色, 因为pushbutton 是 abstractbutton的子类。 但是在Qt中, 所有类的 specificity 值是相同的, 所以最终的结果是 第一行效果被覆盖。 

    specificity 的计算方法参看 commment。 

     

    Namespace冲突

    类型选择器能够使用到一个特定的类型

    如:

    class MyPushButton : public QPushButton {

    // ...

    }

    qApp->setStyleSheet("MyPushButton { background: yellow; }");

            因为QSS使用QObject::className来判断要赋与style sheet的控件类型,如果一个用户定义控件类型在一个namespace里面的话,QObject::className会返回<namespace>::<classname> 的名字,这和子控件选择器的语法相冲突,为了解决此问题,使用"--"来代替"::"

    比如:

    namespace ns

    {      

    class MyPushButton : public QPushButton

    {

              // ...      

    }

    }

    qApp->setSytleSheet("ns--MyPushButton { background: yellow; }");

     

    如果有几个selector指定了相同的declaration, 可以使用逗号(,)将各个选择器分开,如:

    QPushButton, QLineEdit, QComboBox { color: red }

    他相当于:

    QPushButton { color: red }

    QLineEdit { color: red }

    QComboBox { color: red }

     

    级联效应

    级联是指一个子类的 style 由自己的 qss 和 祖先(父类, 父类的父类……)合并而来, 但是如果子类里面设置了StyleSheet与父类里在设置的有冲突,那么子类无论是否符合specificity规则, 都会优先使用自己的。      

    同样,如果在qApp时面设置了,但是在某一个特定控件里面也设置,如果有冲突,也是优先使用控件自己的,例如,我在程序时面设置了:btnOneEn->setStyleSheet("QPushButton { color: red }");

    而,当我再设置qApp时,如果,将QPushButton的color设置成grey的,那么结果是对于btnOneEn这个QPushButton来说他的颜色还是red。

    这就是为什么这里设置为grey了btnOneEn却还是red。 

    如果我们对一个控件设置StyleSheet为:

    QPushButton* myPushButton;

    myPushButton->setStyleSheet("* { color: blue }");

    其实他和设置为:myPushButton->setStyleSheet("color: blue");
    效果相同,只是后一种设置不会对QPushButton的子类产生作用,但第一种却会。

    css中,如果一个元素的font color 值没有被显式的定义,则它会继承父节点的font color但是 QSS 中则不然。 

    例如, 对于一个 包含了数个 pushbutton 的groupbox

    qApp->setStyleSheet("QGroupBox { color: red; } ");

     groupbox 的颜色为红色, 但是pushbutton 的颜色还是系统颜色。除非如此设置

    qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");

         Qt Widgets 的 pallete 和 font 是有级联效应的, 也就是说, 一个控件设定了以后, 那么它的所有子控件都会被设定。 但是 Window 类型的除外。 除非设定了Qt::WA_WindowPropagation 参数。

        如果 QSS 的设定 和 Widgets 的属性(目前只有font)冲突的话, 那么系统会采用QSS 的设定。  

    设置对象属性

    如果在程序里面使用Q_PROPERTY设置的属性,可以在qss里面使用:qproperty-<property name>的形式来访问并设置值。

    如:

    MyLabel { qproperty-pixmap: url(pixmap.png); }

    MyGroupBox { qproperty-titleColor: rgb(100, 200, 100); }

    QPushButton { qproperty-iconSize: 20px 20px; }

    如果属性引用到的是一个由Q_ENUMS申明的enum 时,要引用其属性名字要用定义的名称而不是数字。

    注意:对象属性不支持 状态伪选择器

    引用:http://pepper.troll.no/s60prereleases/doc/stylesheet

    comments  

    原文地址

             http://www.cnblogs.com/davesla/archive/2011/01/30/1947928.html

     

    如何计算css选择符的特异度

    第一步,如果声明代码是在元素的style属性里,也就是行内样式(Inline Style)的话,那么该声明代码拥有最高的优先级,如果不是属于行内样式,那么跳到第二步;行内样式例子:<span style="color:red:></span>;

    第二步,统计ID选择符的数量,数量越多的则特异度越高。如果拥有相同个数的ID选择符,或者都没有ID选择符,那么跳到第三步;

    第三步,统计类选择符(如,.test),属性选择符(如,[type="submit"])和伪类(如,:hover)的总和,数值越大则特异度越高,如果总和相同或者都为0,那么跳到第四步;

    第四步,统计元素类型选择符(如,div)和伪元素(如,:first-letter),数值越大则特异度越高。

    如果两个或以上的选择符拥有相同的物异度,那么根据css级联的规则,后来者居上,也就是最后指定的将会被应用。

    如果你想变得更加专业,那么可以参考W3C recommendation (6.4.3)的计算方法。计算结果用逗号分隔的a,b,c,d四列格式表示,其中a列的值是最重要的,而d的值则是最不重要的。一个选择符的特异度计算方式如下:

    1.计算a列的值,如果是行内样式(Inline Style)的话,则该列的值为1,否则为0;

    2.计算b列的值,值等于选择符所有ID属性个数的总和;

    3.计算c列的值,值为选择符非ID属性的其他属性个数及伪类个数的总和

    4.计算d列的值,值为选择符中的所有元素名个数及伪元素个数的总和

    例如,一个形如"[id=p33]"的css属性选择符的计算结果如下是:a=0,b=0,c=1,d=0。

    参照    http://www.laokboke.com/2011/10/07/how-to-calculate-specificity-of-css-selectors/

       

     

    STYLE基础知识的说明

    我们控制STYLE主要是将STYLE添加到控件上,通过控制控件不同状态的STYLE以达到整体的显示效果

    对于控件,我将其分解为以下几部分来理解

    Ø  从层次上来说:

    控件可分为前景与背景

    前景:多包含文件,图片等内容

    背景:多包含图片,图形等内容

       

    Ø  从结构上来说:

    由于QT style是模拟CSS的布局结构,因此其满足CSS的盒子模型

       

    从里到外的4个区域分别是:

    1 content

    2:  padding

    3:  border

    4:  margin

    通过控制一个控件的前景,背景内容已经结构上的4个区域,我们就可以达到对一个控件为所欲为的控制

     CSS基本功能

    CSS的强大在于组合功能的强大,这里只是简单介绍基本功能,将简单功能组合起来才能实现好看的效果。

    CSS 背景属性(Background)

    属性

    描述

    CSS

    background

    在一个声明中设置所有的背景属性。

    1

    background-attachment

    设置背景图像是否固定或者随着页面的其余部分滚动。

    1

    background-color

    设置元素的背景颜色。

    1

    background-image

    设置元素的背景图像。

    1

    background-position

    设置背景图像的开始位置。

    1

    background-repeat

    设置是否及如何重复背景图像。

    1

    background-clip

    规定背景的绘制区域。

    3

    background-origin

    规定背景图片的定位区域。

    3

    background-size

    规定背景图片的尺寸。

    3

    CSS 边框属性(Border 和 Outline)

    属性

    描述

    CSS

    border

    在一个声明中设置所有的边框属性。

    1

    border-bottom

    在一个声明中设置所有的下边框属性。

    1

    border-bottom-color

    设置下边框的颜色。

    2

    border-bottom-style

    设置下边框的样式。

    2

    border-bottom-width

    设置下边框的宽度。

    1

    border-color

    设置四条边框的颜色。

    1

    border-left

    在一个声明中设置所有的左边框属性。

    1

    border-left-color

    设置左边框的颜色。

    2

    border-left-style

    设置左边框的样式。

    2

    border-left-width

    设置左边框的宽度。

    1

    border-right

    在一个声明中设置所有的右边框属性。

    1

    border-right-color

    设置右边框的颜色。

    2

    border-right-style

    设置右边框的样式。

    2

    border-right-width

    设置右边框的宽度。

    1

    border-style

    设置四条边框的样式。

    1

    border-top

    在一个声明中设置所有的上边框属性。

    1

    border-top-color

    设置上边框的颜色。

    2

    border-top-style

    设置上边框的样式。

    2

    border-top-width

    设置上边框的宽度。

    1

    border-width

    设置四条边框的宽度。

    1

    outline

    在一个声明中设置所有的轮廓属性。

    2

    outline-color

    设置轮廓的颜色。

    2

    outline-style

    设置轮廓的样式。

    2

    outline-width

    设置轮廓的宽度。

    2

    border-bottom-left-radius

    定义边框左下角的形状。

    3

    border-bottom-right-radius

    定义边框右下角的形状。

    3

    border-image

    简写属性,设置所有 border-image-* 属性。

    3

    border-image-outset

    规定边框图像区域超出边框的量。

    3

    border-image-repeat

    图像边框是否应平铺(repeated)、铺满(rounded)或拉伸(stretched)

    3

    border-image-slice

    规定图像边框的向内偏移。

    3

    border-image-source

    规定用作边框的图片。

    3

    border-image-width

    规定图片边框的宽度。

    3

    border-radius

    简写属性,设置所有四个 border-*-radius 属性。

    3

    border-top-left-radius

    定义边框左上角的形状。

    3

    border-top-right-radius

    定义边框右下角的形状。

    3

    box-decoration-break

      

    3

    box-shadow

    向方框添加一个或多个阴影。

    3

    Box 属性

    属性

    描述

    CSS

    overflow-x

    如果内容溢出了元素内容区域,是否对内容的左/右边缘进行裁剪。

    3

    overflow-y

    如果内容溢出了元素内容区域,是否对内容的上/下边缘进行裁剪。

    3

    overflow-style

    规定溢出元素的首选滚动方法。

    3

    rotation

    围绕由 rotation-point 属性定义的点对元素进行旋转。

    3

    rotation-point

    定义距离上左边框边缘的偏移点。

    3

    CSS 字体属性(Font)

    属性

    描述

    CSS

    font

    在一个声明中设置所有字体属性。

    1

    font-family

    规定文本的字体系列。

    1

    font-size

    规定文本的字体尺寸。

    1

    font-size-adjust

    为元素规定 aspect 值。

    2

    font-stretch

    收缩或拉伸当前的字体系列。

    2

    font-style

    规定文本的字体样式。

    1

    font-variant

    规定是否以小型大写字母的字体显示文本。

    1

    font-weight

    规定字体的粗细。

    1

    CSS 外边距属性(Margin)

    属性

    描述

    CSS

    margin

    在一个声明中设置所有外边距属性。

    1

    margin-bottom

    设置元素的下外边距。

    1

    margin-left

    设置元素的左外边距。

    1

    margin-right

    设置元素的右外边距。

    1

    margin-top

    设置元素的上外边距。

    1

    CSS 内边距属性(Padding)

    属性

    描述

    CSS

    padding

    在一个声明中设置所有内边距属性。

    1

    padding-bottom

    设置元素的下内边距。

    1

    padding-left

    设置元素的左内边距。

    1

    padding-right

    设置元素的右内边距。

    1

    padding-top

    设置元素的上内边距。

    1

    CSS 定位属性(Positioning)

    属性

    描述

    CSS

    bottom

    设置定位元素下外边距边界与其包含块下边界之间的偏移。

    2

    clear

    规定元素的哪一侧不允许其他浮动元素。

    1

    clip

    剪裁绝对定位元素。

    2

    cursor

    规定要显示的光标的类型(形状)。

    2

    display

    规定元素应该生成的框的类型。

    1

    float

    规定框是否应该浮动。

    1

    left

    设置定位元素左外边距边界与其包含块左边界之间的偏移。

    2

    overflow

    规定当内容溢出元素框时发生的事情。

    2

    position

    规定元素的定位类型。

    2

    right

    设置定位元素右外边距边界与其包含块右边界之间的偏移。

    2

    top

    设置定位元素的上外边距边界与其包含块上边界之间的偏移。

    2

    vertical-align

    设置元素的垂直对齐方式。

    1

    visibility

    规定元素是否可见。

    2

    z-index

    设置元素的堆叠顺序。

      

      

      

      

      

      

      

      

      

    CSS 文本属性(Text)

    属性

    描述

    CSS

    color

    设置文本的颜色。

    1

    direction

    规定文本的方向 / 书写方向。

    2

    letter-spacing

    设置字符间距。

    1

    line-height

    设置行高。

    1

    text-align

    规定文本的水平对齐方式。

    1

    text-decoration

    规定添加到文本的装饰效果。

    1

    text-indent

    规定文本块首行的缩进。

    1

    text-shadow

    规定添加到文本的阴影效果。

    2

    text-transform

    控制文本的大小写。

    1

    unicode-bidi

    设置文本方向。

    2

    white-space

    规定如何处理元素中的空白。

    1

    word-spacing

    设置单词间距。

    1

    hanging-punctuation

    规定标点字符是否位于线框之外。

    3

    punctuation-trim

    规定是否对标点字符进行修剪。

    3

    text-align-last

    设置如何对齐最后一行或紧挨着强制换行符之前的行。

    3

    text-emphasis

    向元素的文本应用重点标记以及重点标记的前景色。

    3

    text-justify

    规定当 text-align 设置为 "justify" 时所使用的对齐方法。

    3

    text-outline

    规定文本的轮廓。

    3

    text-overflow

    规定当文本溢出包含元素时发生的事情。

    3

    text-shadow

    向文本添加阴影。

    3

    text-wrap

    规定文本的换行规则。

    3

    word-break

    规定非中日韩文本的换行规则。

    3

    word-wrap

    允许对长的不可分割的单词进行分割并换行到下一行。

    3

     

     

  • 相关阅读:
    Video Test Pattern Generator(7.0)软件调试记录
    阅读<Video Test Pattern Generator v7.0>笔记
    阅读<Vivado Design Suite Tutorial---Logic Simulation>笔记
    Modelsim使用流程---基于TCL命令的仿真
    BT.656 NTSC制式彩条生成模块(verilog)
    Video to SDI Tx Bridge模块video_data(SD-SDI)处理过程
    时钟分频方法---verilog代码
    手动按键复位程序(包含按键消抖)
    使用Vivado进行行为级仿真
    阅读OReilly.Web.Scraping.with.Python.2015.6笔记---Crawl
  • 原文地址:https://www.cnblogs.com/aslistener/p/4689594.html
Copyright © 2020-2023  润新知