Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [solved] Need help for implementing themes support
Forum Updated to NodeBB v4.3 + New Features

[solved] Need help for implementing themes support

Scheduled Pinned Locked Moved General and Desktop
13 Posts 4 Posters 4.1k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    T3STY
    wrote on 13 Sept 2013, 01:19 last edited by
    #1

    Qt offers various ways of customizing widgets by subclassing the base classes and reimplementing the painting functions, and that's quite cool. But while looking in the examples provided in the documentation and elsewhere I've been unable to find a proper example of themes support implementation in an application. I don't mean simply customizing the look with different colors or images that you define in your code; instead I mean dynamically loading a custom theme from say an XML file (read as "any kind of settings storage implementation" - files, registry... doesn't matter) having all the settings and path to any images used (like colors, widget sizes, text size, background images, etc...)

    Does someone have any simple example to share that uses some dynamic theme loading from some settings file? I don't need anything too complicated, I just need some start to get in the right direction for implementing my own themes support in my applications.
    Also, any other suggestions about themes is greatly appreciated, so let me know any goods you know. And thank you very much in advance!

    p.s. I'm using Windows and Qt 5.1.1 but examples of previous versions of Qt or an OS-specific implementation is also accepted. As I said, I need to get into the right direction about this.

    1 Reply Last reply
    0
    • D Offline
      D Offline
      dbzhang800
      wrote on 13 Sept 2013, 01:44 last edited by
      #2

      Hi, have you read the documentation of QStyle and Qt stylesheet ?

      1 Reply Last reply
      0
      • T Offline
        T Offline
        T3STY
        wrote on 13 Sept 2013, 02:11 last edited by
        #3

        Yes I did, I'm also reading the QCommonStyle documentation, and I came across this example:
        http://qt-project.org/doc/qt-5.1/qtwidgets/widgets-styles.html
        In the example there's a very neat explanation of what is being customized, but the actual customizations are made in code: all the colors, all the image paths, anything is defined in code.
        What I'm looking for is an example of getting these customizations (well... most of them, like colors and image paths) from some settings file and applying them dynamically at runtime. Dynamically also means being able to select another theme and applying it immediately at runtime without restarting the application.

        1 Reply Last reply
        0
        • C Offline
          C Offline
          clogwog
          wrote on 13 Sept 2013, 05:18 last edited by
          #4

          on a limited scale i've done the dynamic loading of stylesheets by implementing a
          changeTheme(bool day)

          on every window and calling it with a different stylesheet string (for day and night ui)

          my changeTheme sets the stylesheet with one of 2 compiled in strings for every sub-widget depending on the boolean

          but there is no reason why those string can't come out of a config file.

          so perhaps have a stylesheet config file (sorry no xml here :) ) could look like:

          day.style
          @mainwindow="color: white; ..."
          mainwindow.button1="color:white; font-family: arial; ..."@

          and then a "

          night.style
          @mainwindow="color: black; ..."
          mainwindow.button1="color:black; font-family: arial; ..."@

          and in your :

          @MyWindow::changeTheme(QString filename)
          {
          this->setStylesheet( ..read mainwindow from filename )
          this->button1.setStylesheet( ..read mainwindow.button1 from filename )
          }@

          perhaps you could even use introspection to automatically find matching widget names in the config file (with a fallback to class-name, super-classname..)

          i too am looking for a more 'integrated' framework for themes. so if you find one please reply here and i'll get an email.

          1 Reply Last reply
          0
          • T Offline
            T Offline
            thEClaw
            wrote on 13 Sept 2013, 06:31 last edited by
            #5

            Solution one: There is a commandline parameter you can give any Qt application to make it use a custom stylesheet. These are evaluated before your application receives these parameters, I think.

            Solution two:I have a folder "Styles" within my application folder, within that I store a lot of styles in qss format. Some of them bring along icons/pictures, so they are inside their own folders.

            This is how I look for them:
            @QStringList FindStyleSheets()
            {
            QStringList files = ListFiles("Styles");//this recursively list all files in the folder
            for(int i = files.count() - 1 ; i >= 0 ; i --)
            {
            if(files.at(i).endsWith(".qss", Qt::CaseInsensitive))
            files.replace(i, files.at(i).left(files.at(i).count() - QString(".qss").count()));
            else
            files.takeAt(i);
            }
            return(files);
            }@

            The results then fill a QComboBox:
            @QComboBox *boxStyles = new QComboBox(this);
            boxStyles->addItems(QStyleFactory::keys() << FindStyleSheets());//I do include the standard Qt styles, mainly Fusion@

            Once a style is selected in the combobox, I call this function:
            @void ChangeStyle()
            {
            if(boxStyles->currentText().isEmpty())
            {
            qApp->setStyle(nativeStyle);//I saved the initial style somewhere, so I can go back w/o trouble
            }
            else if(QStyleFactory::keys().contains(boxStyles->currentText(), Qt::CaseInsensitive))//actual style
            {
            qApp->setStyleSheet("");//you could probably combine styles and stylesheets
            qApp->setStyle(boxStyles->currentText());
            }
            else//stylesheet - this should be interesting for you
            {
            CustomStyles c = CustomStyles(QString("Styles") + "/" + boxStyles->currentText() + QString(".qss"));
            c.SetCustomStyleSheet(); //more detail on this will follow
            }
            }@

            The CustomStyles class is very simple:
            @class CustomStyles
            {
            public:
            CustomStyles(const QString &Name);
            QString GetName();
            void SetName(const QString &Name);
            void SetCustomStyleSheet();
            private:
            QString name;
            };@

            All the functions are one-liners, except "SetCustomStyleSheet()":
            @void CustomStyles::SetCustomStyleSheet()
            {
            FILE * in = NULL;//wrote this a long time ago, still was used to C back then - should be upgraded to exclusively use QFile
            QByteArray data;
            QString style;
            QFile f(name);
            if(name.isEmpty())
            {
            qApp->setStyleSheet("");
            return;
            }
            if(!f.exists())
            return;

            in = fopen&#40;name.TOLOCAL , "rb"&#41;;
            if(in != NULL)
            {
                f.open(in , QIODevice::ReadOnly);
                data = f.readAll();
                f.close();
                fclose(in); //read in the file
                style = QString::fromUtf8(data.constData() );
                style.replace("skin:" , name.left(name.lastIndexOf(QRegExp("[\\\\/]")) + 1) , Qt::CaseInsensitive);//this line I'll explain below
                qApp->setStyleSheet(style);
            }
            

            }@

            Basically this reads the content of a stylesheet (.qss file) and calls
            @qApp->setStyleSheet(content_of_qss_file);@

            I changed some things so the stylesheets paths to images can be dynamic, that's why all the paths inside start with "skin:" and are replaced by actual paths right before the stylesheet is set.

            This is a somewhat long example, and it might be more involved than necessary if you just want simple style support. But all the elements are in there, the most important ones I mentioned within the last two paragraphs.
            I hope this helps instead of confusing you. And if anybody has a suggestion to improve this code, let me know. ;)

            1 Reply Last reply
            0
            • T Offline
              T Offline
              T3STY
              wrote on 13 Sept 2013, 14:28 last edited by
              #6

              I see that basically the best/fastest way of implementing themes support in Qt apps is by using stylesheets. This makes sense since it's highly integrated into the Qt framework. I'm getting back to reading the docs anyway since looks to me like I've missed some points on using stylesheets.

              So thank you very much for your answers guys, this has definitely pointed me in the right direction and I think I'll be using stylesheets for adding themes support in my apps.
              Anyway, if someone else has other suggestions they're always welcome!

              1 Reply Last reply
              0
              • T Offline
                T Offline
                thEClaw
                wrote on 13 Sept 2013, 14:53 last edited by
                #7

                I thought you wanted to use sylesheets anyway, maybe I misunderstood you. But you can apply them dynamically and you can even change them on the fly, without recompiling anything. Creating a custom style (not sheet) is probably more involved, at least it looks a lot more intimidating than just writing a stylesheet.
                Plus, there are quite some stylesheets available for other applications, so you have a lot of "examples", while I never really dug up a "binary" style in form of some QStyle subclass.

                1 Reply Last reply
                0
                • T Offline
                  T Offline
                  T3STY
                  wrote on 13 Sept 2013, 15:49 last edited by
                  #8

                  Using stylesheets wasn't my first bet, I was actually looking for anything that would customize the look of my application - stylesheets just got in the way and they look easy to make and use.
                  QStyle sublcassing is also useful for customizing the look, but only for what it comes to customizing the Widget from the very bottom of it, starting on how and which things are drawn (like images, labels, their position, etc.). For customizing the look only (say, the background image or the text color) I think there is definitely too much work to be done while reimplementing the painting methods. In the QStyle example link that I posted in reply n.2 you can clearly see how much work is needed, while a stylesheet would've been way more simpler for applying the images only.

                  1 Reply Last reply
                  0
                  • C Offline
                    C Offline
                    clogwog
                    wrote on 19 Sept 2013, 04:42 last edited by
                    #9

                    this got me looking at my own code again and have just implemented a ThemeManager

                    each window has a
                    @AWindow::loadTheme(QString themeName = "original" )
                    {
                    ThemeManager::loadTheme(themeName);
                    setStyleSheet(ThemeManager::getStyleFromName("MAIN_WINDOW_STYLE"));
                    ui->pb_Menu->setStyleSheet (ThemeManager::getStyle(ui->pb_Menu));
                    // also call any child windows loadTheme() here...
                    }@

                    thememanager is pretty simple:
                    thememanager.h
                    @#ifndef THEMEMANAGER_H
                    #define THEMEMANAGER_H

                    #include <QString>
                    #include <QHash>
                    #include <QObject>

                    class ThemeManager
                    {
                    public:
                    static bool loadTheme(QString themeName = "original");
                    static QString getStyle(QWidget* instance);
                    static QString getStyleFromName(QString styleName);
                    static QString getCurrentTheme() { return currentTheme; }
                    private:
                    ThemeManager();
                    static QHash<QString, QString> themeList;
                    static QString currentTheme;
                    };
                    #endif // THEMEMANAGER_H@

                    thememanager.cpp
                    @#include "thememanager.h"
                    #include <QFile>
                    #include <QVariant>
                    #include <QDebug>
                    #include "mainwindow.h"

                    QHash<QString, QString> ThemeManager::themeList;
                    QString ThemeManager::currentTheme = "";

                    ThemeManager::ThemeManager()
                    {
                    }
                    bool ThemeManager::loadTheme(QString themeName)
                    {
                    bool retValue = false;
                    if( currentTheme.compare(themeName,Qt::CaseInsensitive) == 0)
                    return true;
                    currentTheme = "<loading>";
                    themeList.clear();

                    // try to locate the file in /opt/bla/etc/themes/themename.css first
                    QString fileName = "/opt/bla/etc/themes/" + themeName + ".css";
                    qDebug() << "loading style file from " << fileName;
                    if( QFile::exists(fileName) )
                    {
                        QFile file&#40;fileName&#41;;
                        if(!file.open(QIODevice::ReadOnly&#41;&#41;
                        {
                            qDebug() << "error reading css: "  << file.errorString();
                            return false;
                        }
                        QTextStream in(&file);
                        while(!in.atEnd())
                        {
                            QString line = in.readLine().trimmed();
                            if( !line.startsWith("#")) // comments allowed
                            {
                                // find the first =
                                int location = line.indexOf("=");
                                if( location != -1 )
                                {
                                    QString name = line.mid(0,location).trimmed();
                                    QString value = line.mid(location+1).trimmed();
                    
                                    if( value.contains("$"))
                                    {
                                        // go through the value and find any $VALUES and replace them with previously added items
                                        QStringList wordlist = value.split(" ");
                                        for(int t=0; t < wordlist.size(); ++t)
                                        {
                                            QString aword = wordlist[t].trimmed();
                                            if( aword.startsWith("$"))
                                            {
                                                QString searchkey = aword.mid(1);
                                                if( themeList.contains(searchkey))
                                                {
                                                    QVariant a = themeList.value(searchkey);
                                                    value.replace(aword, a.toString());
                                                }
                                            }
                                        }
                                    }
                                    qDebug() << " found " << name << ":" << value;
                                    themeList.insert(name, value);
                                }
                            }
                        }
                        file.close();
                        qDebug() << "loaded css file: " ;
                        retValue = true;
                    }
                    
                    currentTheme = themeName;
                    return retValue;
                    

                    }

                    QString ThemeManager::getStyle(QWidget* instance)
                    {
                    QString name = instance->objectName();
                    if( instance->parent() != NULL)
                    name = instance->parent()->objectName() + "-" + name;

                    qDebug() << "first looking for:" << name;
                    if( themeList.keys().contains( name));
                    {
                        qDebug() << "found: " << name;
                        QVariant qv = themeList.value(name);
                        if( qv.toString().size() > 0)
                        {
                            qDebug() << "found value: " << qv.toString();
                            return qv.toString();
                        }
                    }
                    
                    name = instance->objectName();
                    // find it by object name
                    if( themeList.keys().contains( name));
                    {
                        qDebug() << "found: " << name;
                        QVariant qv = themeList.value(name);
                        if( qv.toString().size() > 0)
                        {
                            qDebug() << "found value: " << qv.toString();
                            return qv.toString();
                        }
                    }
                    
                    // else find it by generic class
                    if( themeList.keys().contains( instance->metaObject()->className()) )
                    {
                        qDebug() << "found: " << instance->metaObject()->className();
                        QVariant qv = themeList.value(instance->metaObject()->className());
                        return qv.toString();
                    }
                    

                    // perhaps later also by parentname-generic class name
                    return "";
                    }

                    QString ThemeManager::getStyleFromName(QString styleName)
                    {
                    if( themeList.contains( styleName));
                    {
                    QVariant qv = themeList.value(styleName);
                    qDebug() << "returning : " << qv;
                    return qv.toString();
                    }
                    return "";
                    }@

                    1 Reply Last reply
                    0
                    • C Offline
                      C Offline
                      clogwog
                      wrote on 19 Sept 2013, 04:51 last edited by
                      #10

                      and the ini files looks something like: original.css

                      @TEXT_COLOR = #eeeeee
                      BACKGROUND_COLOUR = #555555
                      TEXT_COLOR = #eeeeee
                      TEXT_STYLE = color: $TEXT_COLOR

                      LARGE_FONT = font: 27pt Arial
                      MEDIUM_FONT = font: 18pt Arial
                      SMALL_FONT = font: 14pt Arial

                      PB_BORDER_WIDTH = 4px solid
                      PB_BORDER_WIDTH_PUSHED = 2px solid
                      PB_BORDER_WIDTH_DISABLED = 1px solid

                      PB_BORDER_COLOUR = #bbbbbb

                      PB_BORDER_RADIUS = border-radius: 20px

                      PB_WIDTH = min-width: 80px; max-width: 80px
                      PB_HEIGTH = min-height:50px; max-height:50px

                      PB_SMALL_SCREEN_WIDTH = min-width: 80px; max-width: 180px
                      PB_SMALL_SCREEN_HEIGTH = min-height:40px; max-height: 45px

                      PB_BG_GRADIENT = qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(100, 100, 100, 255), stop: 1 rgba(32, 32, 32, 255))
                      PB_PRESSED_BG_GRADIENT = qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 rgba(100, 100, 100, 255), stop: 1 rgba(32, 32, 32, 255))

                      PB_STYLE = QPushButton { $SMALL_FONT ; border: $PB_BORDER_WIDTH $PB_BORDER_COLOUR ; $PB_BORDER_RADIUS ; $TEXT_STYLE ; background-color: $PB_BG_GRADIENT ; $PB_SMALL_SCREEN_WIDTH ; $PB_SMALL_SCREEN_HEIGTH ; } QPushButton:pressed { border: $PB_BORDER_WIDTH_PUSHED #333333; $PB_BORDER_RADIUS ; background-color: $PB_PRESSED_BG_GRADIENT ; $PB_SMALL_SCREEN_WIDTH ; $PB_SMALL_SCREEN_HEIGTH ; } QPushButton:checked { border: $PB_BORDER_WIDTH white; $PB_BORDER_RADIUS ; $PB_SMALL_SCREEN_WIDTH ; $PB_SMALL_SCREEN_HEIGTH ; } QPushButton:disabled { border: $PB_BORDER_WIDTH_DISABLED #bbbbbb; $PB_BORDER_RADIUS ; background-color: $PB_BG_GRADIENT ; color: rgb(100, 100, 100); $PB_SMALL_SCREEN_WIDTH ; $PB_SMALL_SCREEN_HEIGTH ; }

                      MAIN_WINDOW_BACKGROUND = background-color: $BACKGROUND_COLOUR
                      MAIN_WINDOW_STYLE = $MAIN_WINDOW_BACKGROUND ; color: rgb(238, 238, 238);

                      Numberpad_Widget-pb_Menu = $PB_STYLE

                      @

                      1 Reply Last reply
                      0
                      • T Offline
                        T Offline
                        T3STY
                        wrote on 19 Sept 2013, 17:40 last edited by
                        #11

                        Hey, that's some impressive work, thank you very much for sharing!
                        Just one question: you seem to load .qss files at first, but then you use a INIs. Is there any particular reason for this?

                        1 Reply Last reply
                        0
                        • C Offline
                          C Offline
                          clogwog
                          wrote on 19 Sept 2013, 22:42 last edited by
                          #12

                          yes agreed, the filename extension naming is misleading.. the files contain css but are in ini file format like

                          name = value

                          where the value is the css

                          it was what i could implement the quickest..
                          also please note that if you are using variables ( in $NAME ) inside a value, then the NAME has to be defined/specified as a name earlier in the file

                          feel free to change at will

                          1 Reply Last reply
                          0
                          • T Offline
                            T Offline
                            T3STY
                            wrote on 19 Sept 2013, 22:52 last edited by
                            #13

                            I see. While I can't find a proper reason for loading QSS from INI stored values, I still think that this code is very useful! If I/somone doesn't like this method it can easily be changed to loading a .QSS file or some other file format that better suits the application (I can think of XML right now).
                            So thank you again ;)

                            1 Reply Last reply
                            0

                            1/13

                            13 Sept 2013, 01:19

                            • Login

                            • Login or register to search.
                            1 out of 13
                            • First post
                              1/13
                              Last post
                            0
                            • Categories
                            • Recent
                            • Tags
                            • Popular
                            • Users
                            • Groups
                            • Search
                            • Get Qt Extensions
                            • Unsolved