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. Text drop shadow inside a custom paint function of QStyledItemDelegate
Forum Updated to NodeBB v4.3 + New Features

Text drop shadow inside a custom paint function of QStyledItemDelegate

Scheduled Pinned Locked Moved Unsolved General and Desktop
4 Posts 2 Posters 111 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
    sokka
    wrote last edited by
    #1

    Hi all,

    this has probably been asked hundreds of times, but I still couldn't find an answer for my use case.

    I want to draw some text with a drop shadow inside my custom override of QStyledItemDelegate::paint function. From searching other answers, I have found that I can apply QGraphicsDropShadowEffect to widgets with QWidget::setGraphicsEffect, but I don't know how I'd be doing something similar using just the painter API. Looking at the source for QGraphicsDropShadowEffect, it seems to delegate all its work to QPixmapDropShadowFilter, which is not part of the public API.

    My paint function draws a pixmap graphic and overlays it with some text, which is why a drop shadow would be good for legibility. Currently, I'm using a trivial implementation by just offsetting the text by 1 pixel and calling QStyle::drawItemText, i.e.:

    def paint(self, painter, opt, modelIdx):
        # ...
        textRect = self.style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, opt, opt.widget);
        align = QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter
        dsRect = QtCore.QRect(textRect)
        dsRect.translate(1, 1)
        # shadow
        painter.setPen(QtGui.QPen(QtGui.QColor("black")))
        self.style.drawItemText(painter, dsRect, align, opt.palette, bool(opt.state & QtWidgets.QStyle.State_Enabled), opt.text)
        # text
        painter.setPen(QtGui.QPen(QtGui.QColor("white")))
        self.style.drawItemText(painter, textRect, align, opt.palette, bool(opt.state & QtWidgets.QStyle.State_Enabled), opt.text)
    

    This looks okay at best for normal text, and pretty bad for text including emoji characters.

    My KDE Plasma desktop has a much nicer drop shadow for file names. Not sure if that's based on QML or Qt Widgets, but I'd like to get something similar:

    plasma_desktop_drop_shadow.png

    1 Reply Last reply
    0
    • I Offline
      I Offline
      IgKh
      wrote last edited by
      #2

      Hi!

      I don't think it is a common question at all, actually.

      Drop shadow is basically "just" a combination of blur and offset effects, right? Or as the closest AI to me says:

      1. Copy the Object: Create a duplicate of the object for which the shadow is being generated.
      2. Color Adjustment: Change the color of the duplicate to a darker shade, typically black or gray.
      3. Offset Positioning: Move the shadow copy slightly down and to the side of the original object to simulate the light source's angle.
      4. Blur Effect: Apply a blur effect to soften the edges of the shadow, making it appear more realistic.
      5. Alpha Blending: Blend the shadow with the background to enhance realism, adjusting the opacity as needed.

      While QPixmapDropShadowFilter is indeed private API, QPixmapDropShadowFilter::draw is pretty straightforward in following the above steps. No reason why you can't take a similar approach in your own code. Basically, draw the the text into a off-screen QImage with a suitable pixel format instead of the delegate's painter directly, and process that. There is no public API for blurring an image that I'm aware of but the algorithm for Guassian blur is well known, and in Python you should have easy access to other libraries specializing in that (PIL maybe? not an expert here).

      If you want to the drop shadow letter by letter (not sure it is needed, but just for completeness) - you'll need to go two levels deeper in the public API and use QTextLayout directly, which after layouting gives you the glyph runs - from which you can draw the individual glyphs.

      Regarding KDE, Plasma Desktop is all QML. You can perhaps start sniffing in the Plasma Desktop Containment code for the implementation details, or just ask in the KDE chat room I guess.

      1 Reply Last reply
      2
      • S Offline
        S Offline
        sokka
        wrote last edited by
        #3

        Thanks for the link!
        If I traced it down correctly, it appears KDE uses org.kde.plasma.extras.ShadowedLabel, which in turn uses the DropShadow QML Type, which is only available with the OpenGL backend. In Qt6, this was replaced by the newer MultiEffect QML Type, but similar caveats apply.

        So there's apparently no public API for CPU / painter-based equivalent.

        1 Reply Last reply
        0
        • S Offline
          S Offline
          sokka
          wrote last edited by sokka
          #4

          I've found an answer using QGraphicsDropShadowEffect on Stack Overflow:
          You can use QGraphicsPixmapItem to apply the effect purely on a pixmap instead of a widget, and use QGraphicsScene to render back to a pixmap. Not sure how expensive that is, might make sense to also throw QPixmapCache into the mix.

          I wanted to post the Python code for the solution translated to my use case, but the forum thinks that's spam, so 🤷‍♂️.

          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