Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. General talk
  3. Qt 6
  4. Whether Qt Can Render Its Interface To A Vector Image Format
Qt 6.11 is out! See what's new in the release blog

Whether Qt Can Render Its Interface To A Vector Image Format

Scheduled Pinned Locked Moved Solved Qt 6
8 Posts 4 Posters 314 Views 4 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.
  • RokeJulianLockhartR Offline
    RokeJulianLockhartR Offline
    RokeJulianLockhart
    wrote last edited by RokeJulianLockhart
    #1

    Occasionally, I want to screenshot my applications in a way that is scalable. Because current compositors appear to exclusively render pixmaps, this isn't possible at that level. Consequently, I am hoping that Qt may be able to, because modern Q{Quick}Styles appear to be comprised of non-bitmap components.

    To summarise, can I implement a button in a standard QWidget QApplication, which captures the current interface as a static vectoral image (of which SVG is probably all that I care about)?

    When using a forum, remember to tag the person you are responding to, in case they are not subscribed to the thread.

    1 Reply Last reply
    0
    • GrecKoG Offline
      GrecKoG Offline
      GrecKo
      Qt Champions 2018
      wrote last edited by
      #6

      What about QWidget::render() with a QSvgGenerator ?

      Chris KawaC 1 Reply Last reply
      3
      • artwawA Offline
        artwawA Offline
        artwaw
        wrote last edited by
        #2

        Hi!
        I do not think it is possible as - as far as I know and I am only happy to be corrected - Widgets are rendered on-screen as raster images. Hence QWidget::grab() produces QPixmap.

        I'd think that providing styles in vector formats has benefit of applying appropriate scale before rendering to raster to limit the artefacts.

        But perhaps someone with better knowledge has better answers for you?

        For more information please re-read.

        Kind Regards,
        Artur

        RokeJulianLockhartR 1 Reply Last reply
        1
        • artwawA artwaw

          Hi!
          I do not think it is possible as - as far as I know and I am only happy to be corrected - Widgets are rendered on-screen as raster images. Hence QWidget::grab() produces QPixmap.

          I'd think that providing styles in vector formats has benefit of applying appropriate scale before rendering to raster to limit the artefacts.

          But perhaps someone with better knowledge has better answers for you?

          RokeJulianLockhartR Offline
          RokeJulianLockhartR Offline
          RokeJulianLockhart
          wrote last edited by
          #3

          QWidget::grab() produces QPixmap.

          discourse.slicer.org/t/32877/3 appears to demonstrate so. Perhaps, I'd need to see what APIs it calls.

          I'd think that providing styles in vector formats has benefit of applying appropriate scale before rendering to raster to limit the artefacts.

          Consequently, @artwaw, was my assumption, that most styles are vectoral, incorrect? I'd assumed it solely because Fusion and Breeze appearing crisp with fractional scaling enabled, and when rendered on high-resolution displays, appeared to demonstrate so. Alternatively, do I misunderstand what you mean?

          When using a forum, remember to tag the person you are responding to, in case they are not subscribed to the thread.

          1 Reply Last reply
          0
          • Chris KawaC Offline
            Chris KawaC Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote last edited by
            #4

            QStyle API is QPainter based, see QStyle::drawPrimitive for example.
            Internally the style can be whatever it needs, raster or vector based, but it's not exposed above that style API surface. A pixmap and a painter go into a black box and a rasterized UI element comes out.

            To add to that platform style plugins often use internally OS provided APIs for rendering UI elements, which is also usually bitmap based.
            You could write a vector based style, but there would still be no Qt API exposing its internals, so no way to get it back like that.

            Consequently there's no vector representation retained anywhere, so it can't be rendered out as such. The way scaling works is that Qt just renders the pixmaps with the scaling factor applied, so if you change scaling factor the element is re-painted bigger/smaller and cached for future use.

            1 Reply Last reply
            4
            • RokeJulianLockhartR RokeJulianLockhart has marked this topic as solved
            • RokeJulianLockhartR Offline
              RokeJulianLockhartR Offline
              RokeJulianLockhart
              wrote last edited by
              #5

              Appears that it would require a modification to Qt (and a conformant style), then. Thanks.

              When using a forum, remember to tag the person you are responding to, in case they are not subscribed to the thread.

              1 Reply Last reply
              0
              • GrecKoG Offline
                GrecKoG Offline
                GrecKo
                Qt Champions 2018
                wrote last edited by
                #6

                What about QWidget::render() with a QSvgGenerator ?

                Chris KawaC 1 Reply Last reply
                3
                • GrecKoG GrecKo

                  What about QWidget::render() with a QSvgGenerator ?

                  Chris KawaC Offline
                  Chris KawaC Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote last edited by
                  #7

                  @GrecKo Hm, well, I didn't think of that... Should work to an extent. The parts where the OS provided bitmaps are used will probably end up being just embedded binary image elements, so won't scale good and the resulting file could be very large, but some elements, like frames and text should be ok. Mostly depends on how the given style renders things - with painter shapes or with pixmaps. Good catch anyway.

                  RokeJulianLockhartR 1 Reply Last reply
                  4
                  • RokeJulianLockhartR RokeJulianLockhart has marked this topic as solved
                  • Chris KawaC Chris Kawa

                    @GrecKo Hm, well, I didn't think of that... Should work to an extent. The parts where the OS provided bitmaps are used will probably end up being just embedded binary image elements, so won't scale good and the resulting file could be very large, but some elements, like frames and text should be ok. Mostly depends on how the given style renders things - with painter shapes or with pixmaps. Good catch anyway.

                    RokeJulianLockhartR Offline
                    RokeJulianLockhartR Offline
                    RokeJulianLockhart
                    wrote last edited by
                    #8

                    @Chris-Kawa, using lookandfeeltool -a org.kde.breeze.desktop, that isn't a problem:

                    1. #!/usr/bin/env python3
                      
                      import sys
                      
                      from PySide6.QtCore import Qt, QRect
                      from PySide6.QtGui import QAction
                      from PySide6.QtSvg import QSvgGenerator
                      from PySide6.QtWidgets import (
                          QApplication,
                          QCheckBox,
                          QComboBox,
                          QFormLayout,
                          QGroupBox,
                          QLabel,
                          QLineEdit,
                          QMainWindow,
                          QPushButton,
                          QSpinBox,
                          QVBoxLayout,
                          QWidget,
                      )
                      
                      
                      class MainWindow(QMainWindow):
                          def __init__(self):
                              super().__init__()
                      
                              self.setWindowTitle("SVG Export Example")
                      
                              central = QWidget()
                              self.setCentralWidget(central)
                      
                              layout = QVBoxLayout(central)
                      
                              group = QGroupBox("Settings")
                              layout.addWidget(group)
                      
                              form = QFormLayout(group)
                      
                              self.name = QLineEdit("Roke")
                              self.number = QSpinBox()
                              self.number.setValue(42)
                      
                              self.combo = QComboBox()
                              self.combo.addItems(["Alpha", "Beta", "Gamma"])
                      
                              self.check = QCheckBox("Enable feature")
                              self.check.setChecked(True)
                      
                              form.addRow("Name:", self.name)
                              form.addRow("Number:", self.number)
                              form.addRow("Mode:", self.combo)
                              form.addRow("", self.check)
                      
                              self.label = QLabel(
                                  "This interface is rendered directly into SVG."
                              )
                              self.label.setWordWrap(True)
                      
                              layout.addWidget(self.label)
                      
                              self.export_button = QPushButton("Export SVG")
                              self.export_button.clicked.connect(self.export_svg)
                              layout.addWidget(self.export_button)
                      
                              export_action = QAction("Export SVG", self)
                              export_action.triggered.connect(self.export_svg)
                              self.menuBar().addAction(export_action)
                      
                          def export_svg(self):
                              svg = QSvgGenerator()
                      
                              svg.setFileName("ui.svg")
                              svg.setTitle("Qt Widget Export")
                              svg.setDescription("Exported using QWidget.render()")
                      
                              size = self.size()
                              svg.setSize(size)
                              svg.setViewBox(QRect(0, 0, size.width(), size.height()))
                      
                              self.render(svg)
                      
                              print("Exported to ui.svg")
                      
                      
                      app = QApplication(sys.argv)
                      
                      window = MainWindow()
                      window.resize(500, 300)
                      window.show()
                      
                      sys.exit(app.exec())
                      
                    2. <?xml version="1.0" encoding="UTF-8"?>
                      <svg width="176.39mm" height="105.83mm" baseProfile="tiny" version="1.2" viewBox="0 0 500 300" xmlns="http://www.w3.org/2000/svg">
                      	<title>Qt Widget Export</title>
                      	<desc>Exported using QWidget.render()</desc>
                      	<g fill-rule="evenodd" font-family="Monospace" font-size="10" font-weight="400" stroke-linecap="square" stroke-linejoin="bevel">
                      		<g fill="#eff0f1">
                      			<rect width="500" height="300"/>
                      		</g>
                      		<g fill="#dee0e2" stroke="#000" stroke-linecap="square" stroke-linejoin="bevel" stroke-opacity="0">
                      			<rect width="500" height="31"/>
                      		</g>
                      		<g fill="#dee0e2" stroke="#b9bbbd" stroke-linecap="square" stroke-linejoin="bevel">
                      			<polyline points="0 30 499 30" fill="none"/>
                      		</g>
                      		<g transform="translate(6 36)" fill="#f4f5f5" stroke="#c6c8c9" stroke-linecap="square" stroke-linejoin="bevel">
                      			<path d="m0.5005 5.0005c0-2.4853 2.0147-4.5 4.5-4.5h478c2.485 0 4.5 2.0147 4.5 4.5v168c0 2.485-2.015 4.5-4.5 4.5h-478c-2.4853 0-4.5-2.015-4.5-4.5v-168z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(6 36)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text x="212.313" y="20.0469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Settings</text>
                      		</g>
                      		<g transform="translate(30 70)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text y="20.5469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Name:</text>
                      		</g>
                      		<g transform="translate(75 70)" fill="#fff">
                      			<rect x="6" y="6" width="399" height="20"/>
                      		</g>
                      		<g transform="translate(75 70)" fill="#fff" stroke="#c6c8c9" stroke-linecap="square" stroke-linejoin="bevel">
                      			<path d="m0.5005 5.0005c0-2.4853 2.0147-4.5 4.5-4.5h401c2.485 0 4.5 2.0147 4.5 4.5v21.999c0 2.4853-2.015 4.5-4.5 4.5h-401c-2.4853 0-4.5-2.0147-4.5-4.5v-21.999z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(75 70)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text x="8" y="20.8906" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Roke</text>
                      		</g>
                      		<g transform="translate(14 108)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text y="20.5469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Number:</text>
                      		</g>
                      		<g transform="translate(75 108)" fill="#fff" stroke="#c6c8c9" stroke-linecap="square" stroke-linejoin="bevel">
                      			<path d="m0.5005 5.0005c0-2.4853 2.0147-4.5 4.5-4.5h46.999c2.4853 0 4.5 2.0147 4.5 4.5v21.999c0 2.4853-2.0147 4.5-4.5 4.5h-46.999c-2.4853 0-4.5-2.0147-4.5-4.5v-21.999z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(75 108)" fill="none" stroke="#444749" stroke-linecap="square" stroke-linejoin="miter" stroke-miterlimit="2" stroke-width="1.001">
                      			<polyline points="40.5 12.5 45 8 49.5 12.5" fill="none"/>
                      		</g>
                      		<g transform="translate(75 108)" fill="none" stroke="#444749" stroke-linecap="square" stroke-linejoin="miter" stroke-miterlimit="2" stroke-width="1.001">
                      			<polyline points="40.5 19.5 45 24 49.5 19.5" fill="none"/>
                      		</g>
                      		<g transform="translate(81 114)" fill="#fff">
                      			<rect width="31" height="20"/>
                      		</g>
                      		<g transform="translate(81 114)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text x="2" y="14.8906" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">42</text>
                      		</g>
                      		<g transform="translate(30 146)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text y="20.5469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Mode:</text>
                      		</g>
                      		<g transform="translate(75 146)" fill="none" stroke="#000" stroke-linecap="square" stroke-linejoin="bevel" stroke-opacity=".125" stroke-width="1.001">
                      			<path d="m1.5015 6.001c0-2.485 2.0145-4.4995 4.4995-4.4995h64.998c2.485 0 4.4995 2.0145 4.4995 4.4995v20.999c0 2.485-2.0145 4.4995-4.4995 4.4995h-64.998c-2.485 0-4.4995-2.0145-4.4995-4.4995v-20.999z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(75 146)" fill="#fcfcfc" stroke="#d1d1d2" stroke-linecap="square" stroke-linejoin="bevel" stroke-width="1.001">
                      			<path d="m1.5015 6.0015c0-2.4853 2.0147-4.5 4.5-4.5h64.997c2.4853 0 4.5 2.0147 4.5 4.5v19.997c0 2.4853-2.0147 4.5-4.5 4.5h-64.997c-2.4853 0-4.5-2.0147-4.5-4.5v-19.997z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(75 146)" fill="none" stroke="#444649" stroke-linecap="square" stroke-linejoin="miter" stroke-miterlimit="2" stroke-width="1.001">
                      			<polyline points="60.5 14.5 65 19 69.5 14.5" fill="none"/>
                      		</g>
                      		<g transform="translate(75 146)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text x="7" y="21.0469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Alpha</text>
                      		</g>
                      		<g transform="translate(75 184)" fill="#fcfcfc" stroke="#a6a6a6" stroke-linecap="square" stroke-linejoin="bevel" stroke-width="1.001">
                      			<path d="m2.5005 7.5005c0-2.2091 1.7909-4 4-4h6.999c2.2091 0 4 1.7909 4 4v6.999c0 2.2091-1.7909 4-4 4h-6.999c-2.2091 0-4-1.7909-4-4v-6.999z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(75 184)" fill="#a6a6a6" fill-opacity=".33001" stroke="#a6a6a6" stroke-linecap="square" stroke-linejoin="bevel" stroke-width="1.001">
                      			<path d="m2.5005 7.5005c0-2.2091 1.7909-4 4-4h6.999c2.2091 0 4 1.7909 4 4v6.999c0 2.2091-1.7909 4-4 4h-6.999c-2.2091 0-4-1.7909-4-4v-6.999z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(75 184)" fill-opacity="0" stroke="#232629" stroke-linecap="square" stroke-linejoin="miter" stroke-miterlimit="2" stroke-width="2.002">
                      			<path d="m6 11 3 3 5.5-5.5" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(75 184)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text x="24" y="16.0469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Enable feature</text>
                      		</g>
                      		<g transform="translate(6 220)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text y="22.0469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">This interface is rendered directly into SVG.</text>
                      		</g>
                      		<g transform="translate(6 260)" fill="none" stroke="#000" stroke-linecap="square" stroke-linejoin="bevel" stroke-opacity=".125" stroke-width="1.001">
                      			<path d="m1.5015 6.001c0-2.485 2.0145-4.4995 4.4995-4.4995h476c2.485 0 4.5 2.0145 4.5 4.4995v22.999c0 2.485-2.015 4.4995-4.5 4.4995h-476c-2.485 0-4.4995-2.0145-4.4995-4.4995v-22.999z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(6 260)" fill="#fcfcfc" stroke="#d1d1d2" stroke-linecap="square" stroke-linejoin="bevel" stroke-width="1.001">
                      			<path d="m1.5015 6.0015c0-2.4853 2.0147-4.5 4.5-4.5h476c2.485 0 4.5 2.0147 4.5 4.5v21.997c0 2.4853-2.015 4.5-4.5 4.5h-476c-2.4853 0-4.5-2.0147-4.5-4.5v-21.997z" fill-rule="evenodd"/>
                      		</g>
                      		<g transform="translate(6 260)" fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text x="205.016" y="22.0469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Export SVG</text>
                      		</g>
                      		<g fill="none" stroke="#232629" stroke-linecap="square" stroke-linejoin="bevel">
                      			<text x="10.0156" y="20.0469" fill="#232629" font-family="Monospace" font-size="10" font-weight="400" stroke="none" xml:space="preserve">Export SVG</text>
                      		</g>
                      		<g transform="translate(0 2)" fill="none" stroke="#808080" stroke-linecap="square" stroke-linejoin="bevel">
                      			<polyline points="10 24 88 24" fill="none"/>
                      		</g>
                      	</g>
                      </svg>
                      

                    Thanks, @GrecKo! Now, I need to ascertain how feasible this is to attach to other applications, after they've been compiled. Perhaps, I'll need a kind of DLL injection software.

                    When using a forum, remember to tag the person you are responding to, in case they are not subscribed to the thread.

                    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