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.
  • D Offline
    D Offline
    dbzhang800
    wrote on last edited by
    #2

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

    1 Reply Last reply
    0
    • T3STYT Offline
      T3STYT Offline
      T3STY
      wrote on 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
      • clogwogC Offline
        clogwogC Offline
        clogwog
        wrote on 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 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
          • T3STYT Offline
            T3STYT Offline
            T3STY
            wrote on 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 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
              • T3STYT Offline
                T3STYT Offline
                T3STY
                wrote on 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
                • clogwogC Offline
                  clogwogC Offline
                  clogwog
                  wrote on 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
                  • clogwogC Offline
                    clogwogC Offline
                    clogwog
                    wrote on 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
                    • T3STYT Offline
                      T3STYT Offline
                      T3STY
                      wrote on 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
                      • clogwogC Offline
                        clogwogC Offline
                        clogwog
                        wrote on 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
                        • T3STYT Offline
                          T3STYT Offline
                          T3STY
                          wrote on 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

                          • Login

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