UN-GCPDS / qt-material

Material inspired stylesheet for PySide2, PySide6, PyQt5 and PyQt6
https://qt-material.readthedocs.io/en/latest/
BSD 2-Clause "Simplified" License
2.27k stars 241 forks source link

Can this be used for regular Qt applications built in C++? #25

Open GavinNL opened 3 years ago

GavinNL commented 3 years ago

If so, how can this be achieved? I tried using the template file as a qss, but it didn't seem to work.

YeisonCardona commented 3 years ago

Hi @GavinNL,

Are you using the material.css.template as it or after rendering?

GavinNL commented 3 years ago

I'm not quite familiar with how to use the .templates. I've only ever used the .qss file. I renamed the .template to .qss and tried to import it using the standard method

    //create the application and the main window
    QApplication app(argc, argv);
    MainWindow w;

    //open qss file
    QFile file("material.qss");
    file.open(QFile::ReadOnly);

    QString styleSheet { file.readAll() };
GavinNL commented 3 years ago

I assume you need a generate a .qss file somehow before it can be used within Qt as a stylesheet?

YeisonCardona commented 3 years ago

Try with this file dark_teal.txt (as .qss), if it works, the colors and widgets must change, but with missing icons.

GavinNL commented 3 years ago

Okay that worked! Thanks

I found the build_stylesheet() function, I managed to get it to build the qss sheet.

#!/usr/bin/python3
import sys
from PySide2 import QtWidgets

from qt_material import build_stylesheet

# create the application and the main window
app = QtWidgets.QApplication(sys.argv)

print( build_stylesheet(theme='light_blue.xml') )

I believe you can embed base64 images right into the stylesheet (https://forum.qt.io/topic/89128/qt-stylesheet-image-can-i-add-svg-code-in-image-instead-of-specifying-url/2 )

I think a really useful feature would be a command line function to generate the qss files with embedded images. That way people can use your themes outside of pyside.

python3 -m qt_material.buildqss "light_blue.xml" > output.qss
YeisonCardona commented 3 years ago

I was trying with data:image/svg+xml instead of base64 because I have SVG images, but I can not view them. The template assumes that there is an icon:/ path, I'm adding this path with QDir.addSearchPath('icon', 'theme')

Can you please try this in C++, with this theme.zip generated theme folder as SearchPath.

Now you could see the theme and icons, or not.

GavinNL commented 3 years ago

I added this line to my C++ code, and placed the "theme" folder in the same directory as my executable. I don't think the icons showed up.

QDir::addSearchPath("icon", "./theme");

In the qss file, I noticed

QSpinBox::down-button:disabled {
  image: url(:/icon/disabled/downarrow.svg);
}

So i tired setting the folder structure as:

theme
+ -- icon
      +--disabled
      +--primary

But that didn't work either

GavinNL commented 3 years ago

I think I managed to get it to work...but I had to do the following

I had to modify the .qss file and replace all instances of url(:/icon/XXXXXX) with url(icon/XXXXX)

Then i had to have my folder structure looking like this:

executable
icon
+--primary
+--disabled

Then it was able to find the files properly

GavinNL commented 3 years ago

Okay more success.

In my C++ code I added this:

QDir::addSearchPath("icon", QDir::currentPath() + "/theme");

Then I had to modify all the urls to look something like this:

url(icon:/disabled/downarrow.svg")

Then it started working and I could see the various icons.

YeisonCardona commented 3 years ago

At this point, I can add a method to generate the qss and the icons folder for a specific theme and let the users add it to their code.

GavinNL commented 3 years ago

That would be awesome!

YeisonCardona commented 3 years ago

Ok In the last commit, you can do:

from qt_material import export_theme

extra = {

    # Button colors
    'danger': '#dc3545',
    'warning': '#ffc107',
    'success': '#17a2b8',

    # Font
    'font_family': 'monoespace',
    'font_size': '14px',
    'line_height': '14px',
}

export_theme(theme='dark_teal.xml', qss='dark_teal.qss', output='theme', prefix='icon:/',
             invert_secondary=False, extra=extra,)

or

from qt_material import export_theme

export_theme(theme='dark_teal.xml', qss='dark_teal.qss', output='theme', prefix='icon:/')

With the same documented arguments for apply_stylesheet and new ones:

So, try it and tell me how works for you, or if there are needed more options.

GavinNL commented 3 years ago

Looks like its working! Thanks for the quick turn around on this!

akshaybabloo commented 3 years ago

@GavinNL Do you mind sharing your code on how you were able to set the SVG icons without using a qrc file?

GavinNL commented 3 years ago

Hi @akshaybabloo , you can do this relatively easily in 2 steps.

  1. Generate the qss and icon files. I do this directly from my bash terminal. Type in each line individually
git clone https://github.com/UN-GCPDS/qt-material.git
cd qt-material
python3

While in the python terminal, run the following

from qt_material import export_theme
export_theme(theme='dark_teal.xml', qss='dark_teal.qss', output='theme', prefix='icon:/')
exit()

This will create a file dark_teal.qss and a folder, theme.

  1. Copy these two folders to where your Qt C++ binary is.

In your C++ code, add the following right after you create your QApplication

  QApplication app(argc, argv);

  {
     // must give an absolute path to the "theme" folder
      QDir::addSearchPath("icon", QDir::currentPath() + "/theme");

          QFile file("dark_teal.qss");
          file.open(QFile::ReadOnly);

          QString styleSheet { file.readAll() };
          app.setStyleSheet(styleSheet);
  }

Hopefully this helps

akshaybabloo commented 3 years ago

@GavinNL I tried it, but I still get file not found. Creating a qrc file fixed it. See #28.

GavinNL commented 3 years ago

@akshaybabloo , did you properly load the qss file from the correct location?

Edit the .qss file and make sure all the paths to the icons look correct, they should look something like this:

  image: url(icon:/disabled/downarrow.svg);

Make sure that the qss file and the theme folder that was generated are in the same folder as your binary file. You can change this, but then you'll have to update the paths in the sample code I gave you.

martinrotter commented 2 years ago

I get segfaults when trying to produce QSS.

>>> from qt_material import export_theme
>>>
>>> extra = {
...
...     # Button colors
...     'danger': '#dc3545',
...     'warning': '#ffc107',
...     'success': '#17a2b8',
...
...     # Font
...     'font_family': 'monoespace',
...     'font_size': '14px',
...     'line_height': '14px',
... }
>>> export_theme(theme='dark_teal.xml', qss='dark_teal.qss', output='theme', prefix='icon:/',
...              invert_secondary=False, extra=extra,)
Segmentation fault
pstavirs commented 2 years ago
import sys
import PyQt5
from qt_material import export_theme

extra = {
}

app = PyQt5.QtWidgets.QApplication(sys.argv)

export_theme(theme='dark_amber.xml', qss='dark_amber.qss', output='dark_amber_icons',
                       prefix='icon:/', invert_secondary=False, extra=extra,)