Using variables in qt StyleSheets
Asked Answered
Q

7

25

Is there any possibility of giving variable name to hex/rgb numbers in .qss file . For eh

myColor = #FFCC08
QPushButton { background-color: myColor;}

So that i can define the variable at the top of the stylesheet and use the variable name whereever required instead of using the hex code. Also if i need to change the color then i have to change in one place and it will be reflected throught the file.

I also searched for Saas but don't know how it can be used in qt.

Thanks :)

Quintessence answered 5/6, 2012 at 13:39 Comment(0)
M
23

You could build your own tiny Sass quite easily:

1.Create a text file with definitions of variables. Use simple format like this:

@myColor  = #FFDDEE
@myColor2 = #112233 
@myWidth  = 20px

2.In qss file use variable names:

QPushButton { 
    background-color: @myColor; 
    min-width: @myWidth;
}

3.Open both files and for each variable in definition file change its occurrence in qss file with the value (string) from the definition file. It is a simple string replacement.

4.Apply the preprocessed qss in the app.

This is the simplest solution. You can change both definition file and qss file outside the app and apply it without recompilation of code.

Monadelphous answered 14/6, 2012 at 11:19 Comment(1)
Thanks for your time. I used the same logic to create my own parser that replaces each custom variable-color that is defined in the .qss file with the actual color.Quintessence
C
7

What you're trying to accomplish simply isn't possible using pure Qt style sheets.

You can achieve a similar effect by modifying and reloading your style sheets from within your C++ code, for example:

QString myColor = "#FFCC08";
QString styleSheet = "QPushButton { background-color: %1;}";
...
myWidget->setStyleSheet( styleSheet.arg(myColor) );

Unfortunately this has several drawbacks (inability to preview in designer, changing code rather than a style sheet), but it's about as close as you can get to what you're trying to achieve with Qt.

Crigger answered 5/6, 2012 at 14:13 Comment(1)
Yes I understand but the problem with the above approach is that we are specifying the stylesheet properties within the c++ code for each component and not reading from a .qss file. Thanks for your time.Quintessence
C
4

Another way to accomplish this would be to use Dynamic Properties. This would let you easily assign multiple properties to an object or group of objects, sort of like assigning a css class to an object. https://wiki.qt.io/Dynamic_Properties_and_Stylesheets

For example, in your UI file, you could add a string dynamic property "colorStyle" with a value of "myStyle1".

enter image description here

Your stylesheet would look like:

QPushButton[colorStyle='myStyle1'] {
    background-color: #FFCC08;
    ... any other style changes...
}

Any QPushButton you assign 'myStyle1' will follow the stylesheet if you set it globally.

Christean answered 21/4, 2020 at 21:16 Comment(0)
I
1

Here is a solution using sass. First, install the python bindings:

pip install sass

Then, use it:

import sys
import sass
app = QApplication(sys.argv)

# Create your sass style sheet (you can also write this in a file and load the file)
style = '''
$bg-dark: #292929;

QPushButton {
color: red;
background-color: $bg-dark;
}
'''.encode('utf-8')

# Compile Sass to CSS
style = sass.compile_string(style).decode()

# And set it to your app
app.setStyleSheet(style)
Indeclinable answered 19/3, 2018 at 9:21 Comment(2)
If you are using Python anyway, why not use the built-in string formatting, with kwargs, instead of sass? E.g. '... background-color: {bg_dark}; ...'.format(..., bg_dark='#292929', ...)Present
Good point, that works too for this particular answer. But sass can do more than just replacing variables, like mixins, imports, nested rules, etc. In the end, you can style complex applications much more easily and elegantly.Indeclinable
C
0

I have similar, but different problem. In my case, I want to connect window size to QCheckBoxIndicator. Code below won't work due to css already use the {}.

        self.checkBoxYes.setStyleSheet('''
        QCheckBox::indicator {
        width: {sz} px;
        height: {sz} px;
        }
        '''.format(sz=rel_sz))

However, workaround can be achieved using old-formatted string below:

def resizeEvent(self, a0) -> None:
    rel_wdth = self.width() // 20
    rel_hgh = self.height() // 10
    rel_sz = str(min(rel_hgh, rel_wdth))
    self.checkBoxYes.setStyleSheet('''
        QCheckBox::indicator {
        width: %s px;
        height: %s px;
        }
        ''' %(rel_sz, rel_sz))
    return super().resizeEvent(a0)
Crossarm answered 15/10, 2022 at 21:1 Comment(0)
V
0

You can use string concatenation in Python. It is easy to implement.

self.setStyleSheet("""QPushButton { %s;}""" % (myColor))

Volost answered 5/5, 2023 at 13:53 Comment(0)
S
0

Based on the answer by @Pucor I created this function to automate the process and avoid having to create two files:

def load_style(path: str):
    invalid_syntax = Exception('Invalid syntax')
    stylesheet = ''
    with open(path, 'r') as file:
        # Block posidion code:
        # Its used to make sure the syntax is consistent with the rest of the file (I know there are better ways)
        root_block = 0  # 0= No parsing done yet
        variables = dict[str, str]()    # Create an empty dictionary to store the variables

        for line in file:
            if ':root' in line:
                root_block = 1      # 1= :root label found, expecting opening bracket
            if '{' in line:
                if root_block == 1:
                    root_block = 2  # 2= opening brakcet found, the block is "safe" to read
                    continue
                else:
                    raise invalid_syntax
            if '}' in line:         # If the closing bracket is found, check the opening bracket has been found first
                if root_block != 2: # If not the case, throw an error
                    raise invalid_syntax
                # Else we reached the end of the block and the rsulting stylesheet is the rest of the file from this point
                else:
                    stylesheet = file.read()
                    break
            # If its safe to read, then split the key, values by a colon
            if root_block == 2:
                split = line.split(':')

                # Using match/case since its cool; store the variable in the dict
                match split:
                    case  [key, value]:
                        variables[key.strip()] = value.strip()
                    case _:
                        raise invalid_syntax
    
    # This pattern uses a logical OR to capture the words in the brackets
    # The dictionary keys are sorted in reversed since the regex engine only captures the first occurence
    # If p.e. "primary" is set before "primary-dark" then it will only capture "primary" 
    pattern = re.compile('@(' + '|'.join(sorted(variables, key= len, reverse=True)).strip('|') + ')')
    # Replace each match with the value of the variables dictionary (group 0 is a match that contains the @)
    stylesheet = pattern.sub(lambda match: variables[match.group(1)], stylesheet)
    return stylesheet

Please note that the funciton is not bulletproof since it depends on a procedurally made regex capturing group at the end that I havent tested in depth..

Usage:

In your stylesheet.qss:

:root{
    primary: #1a1f1f
    primary-dark: #101010
    primary-light: #353535
    secondary: #2a82da
    secondary-dark: #163f68
}
QWidget{
    border: 0px;
    background-color: @primary;
    color: white;
}

The parser will start by storing all the "variables" inside the :root block and then replace each referenced variable starting with a @ in the rest of the file.

To use it in your application:

style = load_style('stylesheet.qss')
app.setStyleSheet(style)

I understand the code is messy and unreadable

Selden answered 29/1 at 10:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.