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. How do I customize icon scaling behaviour in a QListView?
QtWS25 Last Chance

How do I customize icon scaling behaviour in a QListView?

Scheduled Pinned Locked Moved Unsolved General and Desktop
x11qiconqlistviewqitemdelegateicons
9 Posts 4 Posters 7.9k Views
  • 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.
  • S Offline
    S Offline
    ssokolow
    wrote on 13 Sept 2016, 11:10 last edited by
    #1

    I have an application where I want to mix QIcon::fromTheme with custom upscaling in my listview.

    Specifically, I don't want to lose fromTheme's ability to retrieve the most appropriate icon size offered by the theme, but I then want to apply custom upscaling if I receive an icon that's too small.

    (It's a game browser and I've found that I can get better results with tiny icons from old games if I carefully control what mixture of nearest neighbour and smooth scaling gets used.)

    I've looked at the docs for QIcon and QIconEngine, since I thought it'd be nice to address it at the same time as I hook up a fallback to PyXDG's icon resolver to work around fromTheme's refusal to fall back to hicolor (in Qt 5.2.1) like the desktop does when searching for icons. However, I'm having trouble finding the how the default item delegate requests the icon and where Qt actually allows me to hook in and interfere without going overboard and reinventing the moon.

    While I technically am prototyping this in PyQt, I'm asking in here because I might want to change languages once the prototype's design stabilizes and I'm much more knowledgeable with Python than C++ and so I'm more likely to have trouble adapting Python advice to C++ than vice-versa.

    R 1 Reply Last reply 13 Sept 2016, 11:13
    0
    • S ssokolow
      13 Sept 2016, 11:10

      I have an application where I want to mix QIcon::fromTheme with custom upscaling in my listview.

      Specifically, I don't want to lose fromTheme's ability to retrieve the most appropriate icon size offered by the theme, but I then want to apply custom upscaling if I receive an icon that's too small.

      (It's a game browser and I've found that I can get better results with tiny icons from old games if I carefully control what mixture of nearest neighbour and smooth scaling gets used.)

      I've looked at the docs for QIcon and QIconEngine, since I thought it'd be nice to address it at the same time as I hook up a fallback to PyXDG's icon resolver to work around fromTheme's refusal to fall back to hicolor (in Qt 5.2.1) like the desktop does when searching for icons. However, I'm having trouble finding the how the default item delegate requests the icon and where Qt actually allows me to hook in and interfere without going overboard and reinventing the moon.

      While I technically am prototyping this in PyQt, I'm asking in here because I might want to change languages once the prototype's design stabilizes and I'm much more knowledgeable with Python than C++ and so I'm more likely to have trouble adapting Python advice to C++ than vice-versa.

      R Offline
      R Offline
      raven-worx
      Moderators
      wrote on 13 Sept 2016, 11:13 last edited by
      #2

      @ssokolow
      icons get requested via QAbstractItemModel::data() with the Qt::DecorationRole item role.

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      S 1 Reply Last reply 13 Sept 2016, 11:24
      0
      • R raven-worx
        13 Sept 2016, 11:13

        @ssokolow
        icons get requested via QAbstractItemModel::data() with the Qt::DecorationRole item role.

        S Offline
        S Offline
        ssokolow
        wrote on 13 Sept 2016, 11:24 last edited by ssokolow
        #3

        @raven-worx I'm using a custom model, so I do understand that.

        What I need to know is the least reinvention-heavy way to customize the interaction between QIcon and the default item delegate so that I'm no longer forced to choose between two less-than-ideal options:

        1. Forcing the largest possible icon on the delegate when the theme may provide a pre-scaled icon with the design tweaked for the requested size.
        2. Speculatively generating every scaled copy the delegate might request.
        R 1 Reply Last reply 13 Sept 2016, 11:34
        0
        • S ssokolow
          13 Sept 2016, 11:24

          @raven-worx I'm using a custom model, so I do understand that.

          What I need to know is the least reinvention-heavy way to customize the interaction between QIcon and the default item delegate so that I'm no longer forced to choose between two less-than-ideal options:

          1. Forcing the largest possible icon on the delegate when the theme may provide a pre-scaled icon with the design tweaked for the requested size.
          2. Speculatively generating every scaled copy the delegate might request.
          R Offline
          R Offline
          raven-worx
          Moderators
          wrote on 13 Sept 2016, 11:34 last edited by
          #4

          @ssokolow
          only the item delegate knows what size to request. Since a model can be shared between views and each view might have it's own icon size.

          A QIcon can contain various sizes. I don't know if your theme returns such icons.

          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
          If you have a question please use the forum so others can benefit from the solution in the future

          S 1 Reply Last reply 13 Sept 2016, 11:40
          0
          • R raven-worx
            13 Sept 2016, 11:34

            @ssokolow
            only the item delegate knows what size to request. Since a model can be shared between views and each view might have it's own icon size.

            A QIcon can contain various sizes. I don't know if your theme returns such icons.

            S Offline
            S Offline
            ssokolow
            wrote on 13 Sept 2016, 11:40 last edited by ssokolow
            #5

            @raven-worx

            Hence my decision to ask about putting some code in between the QIcon code which services the request and the delegate code which makes the request (or the closest feasible alternative), so I can implement algorithms like this:

            1. Check what sizes QIcon has available and retrieve the closest one.
            2. Depending on how close we are, apply different mixes of scaling algorithms to reach the target size.
            3. Return what the default delegate code asked for.

            That way, I don't have to reinvent QIcon or the default item delegate when all I want to do is customize the scaling behaviour.

            EDIT: For example, if I could figure out how the default item delegate actually handles requesting the icon, maybe I'd discover a method I could wrap or replace in a subclass to rework the code relating to Qt::DecorationRole without having to write a whole new delegate.

            1 Reply Last reply
            0
            • C Offline
              C Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on 13 Sept 2016, 12:16 last edited by Chris Kawa
              #6

              The road for an icon from a model to the screen is quite short - the delegate calls index.data() and the model returns it.

              Subclassing a delegate in this case is ugly because it forces you to use that particular delegate and reimplement it in every delegate you'd need.

              Returning a pixmap of the desired size from the model is ugly too, because, as @raven-worx mentioned the model can be shared between views and should not know that size in general.

              The other option that Qt offers in cases like this are proxy models. You can subclass QIdentityProxyModel and implement the needed transformation in the data method. You'd have an instance of that proxy for each view with a different size "hint" set.
              This way you're not tied to any particular delegate or model.

              A possible implementation of such proxy would look something like this:

              class MyProxy : public QIdentityProxyModel
              {
                  QSize desired_size;
              public:
                  MyProxy(QObject* parent) : QIdentityProxyModel(parent) {}
                  void setDesiredSize(const QSize& size) { desired_size = size; }
              
                  QVariant data(const QModelIndex &proxyIndex, int role) const override
                  {
                      if (role == Qt::DecorationRole)
                      {
                          QVariant orig_data = QIdentityProxyModel::data(proxyIndex, role);
                          if (orig_data.canConvert<QIcon>())
                          {
                              QPixmap p = orig_data.value<QIcon>().pixmap(desired_size);
                              //do the needed transformation to p
                              return p;
                          }
                      }
                      return QIdentityProxyModel::data(proxyIndex, role);
                  }
              };
              

              You could of course cache the pixmaps or do other stuff there if you wanted to.

              S 1 Reply Last reply 16 Sept 2016, 18:54
              2
              • C Chris Kawa
                13 Sept 2016, 12:16

                The road for an icon from a model to the screen is quite short - the delegate calls index.data() and the model returns it.

                Subclassing a delegate in this case is ugly because it forces you to use that particular delegate and reimplement it in every delegate you'd need.

                Returning a pixmap of the desired size from the model is ugly too, because, as @raven-worx mentioned the model can be shared between views and should not know that size in general.

                The other option that Qt offers in cases like this are proxy models. You can subclass QIdentityProxyModel and implement the needed transformation in the data method. You'd have an instance of that proxy for each view with a different size "hint" set.
                This way you're not tied to any particular delegate or model.

                A possible implementation of such proxy would look something like this:

                class MyProxy : public QIdentityProxyModel
                {
                    QSize desired_size;
                public:
                    MyProxy(QObject* parent) : QIdentityProxyModel(parent) {}
                    void setDesiredSize(const QSize& size) { desired_size = size; }
                
                    QVariant data(const QModelIndex &proxyIndex, int role) const override
                    {
                        if (role == Qt::DecorationRole)
                        {
                            QVariant orig_data = QIdentityProxyModel::data(proxyIndex, role);
                            if (orig_data.canConvert<QIcon>())
                            {
                                QPixmap p = orig_data.value<QIcon>().pixmap(desired_size);
                                //do the needed transformation to p
                                return p;
                            }
                        }
                        return QIdentityProxyModel::data(proxyIndex, role);
                    }
                };
                

                You could of course cache the pixmaps or do other stuff there if you wanted to.

                S Offline
                S Offline
                ssokolow
                wrote on 16 Sept 2016, 18:54 last edited by
                #7

                @Chris-Kawa Sorry for the delayed response. Busy couple of days.

                Your idea had promise, but then I remembered that setSelectionModel's "same model" requirement has no way to see through proxy models, so I'd be forced to have the QTableView use the larger icon size from the QListView, which puts me right back where I started.

                (Not wanting the QTableView to have to use a naïve 16x16px down-scale of the icon view's icon (which could be as large as 256x256px) when the icon theme is likely to use different SVG source files for the large and small scales to ensure they remain recognizable.)

                ...which, again, makes me wish that QIcon::paint and QIcon::pixmap were virtual, so I could just wrap them in a subclass and fix this in the simplest, cleanest way possible.

                As-is, unless anyone can suggest another approach, it looks like I'm back to asking about how to extend either QIconEngineV2 or QListView's default item delegate with minimal wheel-reinvention.

                (And I'm unsure whether PyQt's support for loading Python-based Qt plugins extends beyond Qt Designer, which makes the icon engine approach of questionable appropriateness for an answer that should apply to both C++ and Python codebases.)

                1 Reply Last reply
                0
                • H Offline
                  H Offline
                  hskoglund
                  wrote on 16 Sept 2016, 19:02 last edited by hskoglund
                  #8

                  Hi, have you looked at using a QListWidget instead of a QListView?
                  (A couple of times I wanted similar customization as yours and instead of fighting with the model, I switched to using the simpler QListWidget.)

                  Edit: or using a QTableWidget instead of a QTableView.

                  S 1 Reply Last reply 16 Sept 2016, 20:06
                  0
                  • H hskoglund
                    16 Sept 2016, 19:02

                    Hi, have you looked at using a QListWidget instead of a QListView?
                    (A couple of times I wanted similar customization as yours and instead of fighting with the model, I switched to using the simpler QListWidget.)

                    Edit: or using a QTableWidget instead of a QTableView.

                    S Offline
                    S Offline
                    ssokolow
                    wrote on 16 Sept 2016, 20:06 last edited by
                    #9

                    @hskoglund I already have an underlying data store and I'm using a subclass of QAbstractTableModel to bridge it into a Qt GUI.

                    Making two in-memory copies of that data store and then jerry-rigging View-like synchronization between the actual data and two widget-specific data stores (plus all the requisite automated testing to make sure it works reliably) seems like the exact opposite of what I want.

                    1 Reply Last reply
                    0

                    9/9

                    16 Sept 2016, 20:06

                    • Login

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