Using variables in qt StyleSheets - qt

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 :)

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.

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.

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".
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.

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)

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)

Related

How to use typescript variables in css files?

I am using Angular, my goal is to be able to use a string declared in typescript inside a CSS file. I am trying to set the background image of a navbar component. Later on, the background image path will be received from a database service, that's why I need it to be in the typescript file. I read something about using [ngStyle], but the img will not be updated, I just need the paths to be received from a database. Should I still try to use it? And how? I am a bit lost.
My typescript file has something like:
// ...
export class NavbarComponent{
background_url='../../../assets/img/background.png';
constructor() { }
// ...
And in my CSS file i want to do something like:
nav{
background-image: background_url;
}
However, this isn't working for me.
How could I better approach this? Thanks
I think it's not possible to access the ts file variables from the CSS file, but you can get elements from DOM and set style to that from the ts file.
an example:
document.getElementById('element').style.backgroundImage = background_url;
also if you are using frameworks like angular, you can use #ViewChild to get elements from DOM and style them by using the renderer2 library like this:
export class NavbarComponent {
#ViewChild('element') element: ElementRef;
background_url='../../../assets/img/background.png';
constructor(private renderer: Renderer2) {}
setStyle() {
this.renderer.setStyle(
this.element.nativeElement,
'background-image',
this.background_url
);
}
}
and then call the setStyle function where ever you want.
more from renderer2: https://angular.io/api/core/Renderer2

Inject CSS variable names that come form the server (Angular 12)

I'm trying to find the best solution to inject css variables into angular application (currently using Angular 12).
This is the code I'm using:
private injectStyles(data: any) {
const styles = this.document.createElement('STYLE') as HTMLStyleElement;
styles.id = 'dynamic-css';
this.test = `:root {
--main-bg-color: black;
}`;
styles.innerHTML = this.test;
this.renderer.appendChild(this.document.head, styles);
}
This code is being executed on app.component.ts file and this works quite well, i can use this css variable throughout the whole application.
What I'm trying to achieve now is to extend the this.test Object with data that comes from the server and apply these custom css values that were set on my Visual settings module before.
Css variables must not have "" quotes.
"--button-color": "#a86c6c" (this is what I get from the server) and I would like to inject this property without quotes on the this.test Object and I can't do that. Any ideas?
Any help is highly appreciated,
Thanks.
Object.keys is your friend
let dataString = '';
Object.keys(data).forEach(key => {
dataString += `${key}: ${data[key]};`
});
this.test = `:root {
--main-bg-color: black;
${dataString}
}`;
If needed, you may need to add quote to the value like this
`dataString += `${key}: "${data[key]};"`

Referencing class names between SASS modules

I'm confused how to reference my SASS module class name in another module. I have a Contact page module and a PhoneNumber module. In certain context on this page, I'd like to change the button inside the PhoneNumber. Using simplified example for clarity:
// src/pages/contact/index.module.scss
#use 'src/components/PhoneNumber/index.module' as phoneNumber;
.page {
phoneNumber.container {
:global(.button) {
background-color: pink;
}
}
}
It doesn't work, I'm not sure how to reference .container class name of PhoneNumber component.
There are 2 issues:
(1) If you include variables imported by directive #use ... as namespace you have to call them using the added namespace with character $ as well. So you have to call the variable with phoneNumber.$container, not: 'phoneNumber.container'.
(2) If you use a variable as element/id/class-name you have to wrap variables in #{$hereMyVariable} at all.
So try following code:
.page {
.#{phoneNumbers.$container}{
:global(.button) {
background-color: pink;
}
}
}
FURTHER EXPLANATION
Rule #use loads mixins, functions, and variables from other Sass stylesheets so you can use them. To separate same variables/mixins/functions names from different stylesheets you can separate them to different namespaces by #use scssFileName as myNamespace.
Now you can call i.e. variables with same name namespace1.$container and namespace2.$container from different stlesheetswith different values. Mixins works the same way just without $.
So: if you load #use filepath/filename as phoneNumber to your stylesheet and call phoneNumber.container SASS is looking to the other stylesheet for a function or mixin named container. From the context of you simplified example I assume you want to call a variable with the name of the class. Than you need to use $ and the brackets.
(Notice: But if you want just to name your class phoneNumber.container you should write .phoneNumber.containerso SASS will interpret it as classname and not as reference to the namespace of the other stylesheet ...)
Wider explanations how to use #use can be find in the excellent official docs: https://sass-lang.com/documentation/at-rules/use

Making customizable css properties with React and CSS Modules/PostCSS

I'm trying to make a library of react components that's external to an application. This will be an npm module, loaded with Webpack. I'm styling the component using CSS Modules, and I'm trying to see how to make some of its properties customizable. For instace, background color.
I would like to use css variables for this to have for instance this syntax in the css file:
.myClass {
backgrond-color: var(--backgroundColor, red);
}
Where --backgroundColor is a variable I can set, and red is the default. My question is, is there a way I can pass variables to the .css file when loading it from the .jsx file? So I could pass a variables object to the component, which then would influence how it loads it style? Could I use PostCSS for this?
Thanks.
PS: I know this could be solved by using inline JS styles, but I'm trying to give CSS a shot first.
You cannot inject javascript into a css file and PostCSS can only transform your css files, but not inject/replace variables.
However, one way of doing this would be to create .scss (sass) files with default variables, e.g. $background-color: red; One could then import your module and .scss files to their .scss files and overwrite any variables like $background-color with their own variables if they wish.
I'm not sure I understood you right, but here what I'm thinking of:
When you are requiring .css file with Webpack it adds this css as a string to the <head> element of the page behind the scene.
Why don't you do what Webpack does using your own function, like so.
Your module:
import $ from 'jquery';
/* this function builds string like this:
:root {
--bg: green;
--fontSize: 25px;
}
from the options object like this:
{
bg: 'green',
fontSize: '25px'
}
*/
function buildCssString(options) {
let str = ':root {\n'
for(let key in options) {
str += '\t--' + key + ': ' + options[key] + ';\n'
}
str += '}';
return str;
}
/* this function adds css string to head element */
export const configureCssVariables = function(options) {
$(function() {
var $head = $('head'),
$style = $('<style></style>');
$style.text(buildCssString(options));
$style.appendTo($head)
});
}
Usage:
import {configureCssVariables} from './your-module';
configureCssVariables({
bg: 'green',
fontSize: '25px'
});
And your css is simple
/* default variables that will be overwritten
by calling configureCssVariables()
*/
:root {
--bg: yellow;
--fontSize: 16px;
}
.myClass {
backgrond-color: var(--bg);
font-size: var(--fontSize);
}
It can be acheived by adding PostCSS and the postcss-custom-properties plugin in your pipeline. It has a variables option which will inject JS defined variables (properties) to any file being processed.
This eliminate the need to #import anything inside every CSS module file.
const theme = {
'color-primary': 'green',
'color-secondary': 'blue',
'color-danger': 'red',
'color-gray': '#ccc',
};
require('postcss-custom-properties')({
variables: theme,
});
See how to use it with babel-plugin-css-modules-transform https://github.com/pascalduez/react-module-boilerplate/blob/master/src/theme/index.js and https://github.com/pascalduez/react-module-boilerplate/blob/master/.babelrc#L21 but that works with Webpack as well.
I actually found a solution that already does this and takes advantage of the latest standardized JavaScript features
https://github.com/styled-components/styled-components
It may just be what I was looking for.

JavaFX - generate compile time error if a class doesn't exist in css

I develop an application in JavaFX. Now i've got the Problem that i would like to generate an error at compile time if a css class, e.g. for a button, which if've set in the code doesn't exist in the corresponding css file.
Example:
main.java
String sCSS = this.getClass().getResource("/main.css").toExternalForm();
scene.getStylesheets().add(sCSS);
Button btn = new Button("Hello JFXWorld...");
btn.getStyleClass().add("button"); // should show compile time error
main.css
.buttonWithWrongName {
-fx-background-color: red; }
My solution would be the following, but I'm looking for advice how I can realize it.
I include an preprocessor or create an new project or something like that which parses the css code and dynamically generates an enum (into a .jar). And this jar i could include into my Project. In the best case ant would trigger the build of the enum automatically if no preprocessor is used.
again an Example:
Css file is the same as above.
{
Do preprocessor stuff for the css file here (parse and create enum) and build it to a jar file.
}
main.java
String sCSS = this.getClass().getResource("/main.css").toExternalForm();
scene.getStylesheets().add(sCSS);
Button btn = new Button("Hello JFXWorld...");
// will throw an compile time error becaus the eMainCSS enum only contains the class ".buttonWithWrongName"
btn.getStyleClass().add(eMainCSS.button.toString());
// works fine
btn.getStyleClass().add(eMainCSS.buttonWithWrongName.toString());
I'm using Eclipse (Neon) and JDK 1.8.0.
I hope there's a solution.
Thank you for the answers.
Best Regards,
Max S.
-- Edit --
Solution:
I've used an Ant WatchTask to watch at all my less files in the project.
If I change something in the less file, ant will run a self written jar file with the file name as parameter. The jar converts the less file to a css file (with less4j) and the css file to a java file (my own parser) which contains the enum.
Note: Youre not allowed to change anything in the enum. Even if you do, after the next changes in the less file, itll be gone.
The result looks like this:
Stylesheet.less:
.button { -fx-stroke-width: (1 + 1); }
#anotherButton { -fx-stroke-width: (1 + 2); }
to Stylesheet.css:
.button { -fx-stroke-width: 2; }
#anotherButton { -fx-stroke-width: 3; }
to Stylesheet.java:
// Auto-generated java file
public enum Stylesheet {
C_BUTTON("button"),
ID_ANOTHERBUTTON("anotherButton");
private final String sValue;
Stylesheet(String sValue) {
this.sValue = sValue;
}
#Override
public String toString() {
return this.sValue;
}
}
Now the compiler will throw an error at the compile time if the class or id which i'm using in javafx doesn`t exist.
Best Regards,
Max S.

Resources