{"id":12122,"date":"2025-06-23T10:56:31","date_gmt":"2025-06-23T08:56:31","guid":{"rendered":"https:\/\/www.basyskom.de\/?p=12122"},"modified":"2025-06-23T10:57:24","modified_gmt":"2025-06-23T08:57:24","slug":"pitfalls-in-pyside6","status":"publish","type":"post","link":"https:\/\/www.basyskom.de\/en\/pitfalls-in-pyside6\/","title":{"rendered":"Pitfalls in PySide6"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"12122\" class=\"elementor elementor-12122\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b2e3b58 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-parent\" data-id=\"b2e3b58\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f009c92 elementor-widget elementor-widget-text-editor\" data-id=\"f009c92\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Last time I have shown you the basics of PySide6 and what you can do with it. My colleague Berthold further <a href=\"https:\/\/www.basyskom.de\/en\/interactive-plots-with-pyside6\/\">demonstrated the use of Matplotlib<\/a> to visualize data. It\u2019s one of the the key selling points for using Python in your project. As with most things in life, where there is light, there is darkness. Let\u2019s have a look at some of the pitfalls to look out for, particularly when piping lots of data through a PySide6 application.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-35fb3bc elementor-widget elementor-widget-text-editor\" data-id=\"35fb3bc\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In addition to a plot to visualize your data, you most likely also need some sort of list or table to choose what data to render in the first place. Qt comes with a <a href=\"https:\/\/doc.qt.io\/qt-6\/model-view-programming.html\" target=\"_blank\" rel=\"noopener\">powerful set of item view classes<\/a> that use a model-view architecture to manage data and how it\u2019s presented to the user.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-16254f8 elementor-widget elementor-widget-heading\" data-id=\"16254f8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Be careful with the Qt global object!<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-61ca2ed elementor-widget elementor-widget-text-editor\" data-id=\"61ca2ed\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Let\u2019s have a look at the following snippet from a table model returning some data (of course, somewhat simplified):<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-485799a elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"485799a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-python' data-no-translation=\"\">def data(self, index, role):\n    item = index.internalPointer()\n    match role:\n        case Qt.DisplayRole:\n            return item.text()\n        case Qt.DecorationRole:\n            return item.icon()\n        case Qt.ToolTipRole:\n            return item.toolTip() <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8885091 elementor-widget elementor-widget-text-editor\" data-id=\"8885091\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Spot anything wrong with this? This method is called a lot, every entry in the model has a dozen of roles (display, decoration, tooltip, foreground color, etc). Now imagine scrolling through a table with 10 columns and maybe 30 entries per screen page. While Qt is clever enough not to ask for cells that are out of the visible area and only fetches data when actually needed (e.g. tooltip role is only queried when you hover the item), you\u2019re still looking at maybe 100 calls for each scrolled line. Still, looks like your typical Qt item model code, doesn\u2019t it?<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cbf3ada elementor-widget elementor-widget-text-editor\" data-id=\"cbf3ada\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>While investigating a performance problem with a table model, we fired up a profiler and were dismayed by what we found: It wasn\u2019t our custom delegate painting code after all! Instead, python spent a good amount\u00a0 of time in <code data-no-translation=\"\">PyObject_GetAttr<\/code>. Turns out, the <code data-no-translation=\"\">Qt<\/code> object is <em>huge<\/em>. It contains all the enum values you could think of. Calling <code data-no-translation=\"\">Qt.DisplayRole<\/code> then has to look up the corresponding value in this giant object. Narrowing it down by using <code data-no-translation=\"\">Qt.<em>ItemDataRole<\/em>.DisplayRole<\/code> significantly improved the model\u2019s performance.\u00a0Likewise, in painting code you\u00a0probably want\u00a0to use <code data-no-translation=\"\">Qt.<em>GlobalColor<\/em>.transparent<\/code> instead of <code data-no-translation=\"\">Qt.transparent<\/code>\u00a0to fill your pixmap. While it doesn\u2019t hurt much in a button handler, it definitely adds up in a function that is called thousands of times. Therefore: always use fully qualified enums with PySide6!<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-32179ca elementor-widget elementor-widget-heading\" data-id=\"32179ca\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Function calls are expensive<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cd8a16f elementor-widget elementor-widget-text-editor\" data-id=\"cd8a16f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tIn addition, don\u2019t re-implement native methods if you don\u2019t have to. While it\u2019s tempting to just override <code data-no-translation=\"\">lessThan<\/code> or <code data-no-translation=\"\">filterAcceptsRow<\/code> in your <code data-no-translation=\"\">QSortFilterProxyModel<\/code> or making use of <code data-no-translation=\"\">__lt__<\/code> in your <code data-no-translation=\"\">QListWidget<\/code>, they are also called frequently. If you can, try to use the built-in sort or filter criteria to keep the logic on the C++ side. In case of custom sorting, your model\u2019s <code data-no-translation=\"\">sort()<\/code> function is a good place to do this since it\u2019s only called when actually changing the sort order.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e3f3b17 elementor-widget elementor-widget-text-editor\" data-id=\"e3f3b17\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>For example, rather than adjusting the sort role on the fly like this:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0734426 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"0734426\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-python' data-no-translation=\"\">def lessThan(self, source_left, source_right):\n    if self.sortColumn() == MyModel.Columns.SPECIAL_COLUMN:\n        data_left = source_left.data(MyModel.Roles.SPECIAL_ROLE)\n        data_right = source_right.data(MyModel.Roles.SPECIAL_ROLE)\n        return data_left &lt; data_right\n    else:\n        return super().lessThan(source_left, source_right) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-40e9f79 elementor-widget elementor-widget-text-editor\" data-id=\"40e9f79\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Adjust the sort column just once:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cea4995 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"cea4995\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-python' data-no-translation=\"\">def sort(self, column, order=Qt.SortOrder.AscendingOrder):\n    self.setSortRole(MyModel.Roles.SPECIAL_ROLE if column == MyModel.Columns.SPECIAL_COLUMN else Qt.ItemDataRole.DisplayRole)\n    super().sort(column, order) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-34f5391 elementor-widget elementor-widget-text-editor\" data-id=\"34f5391\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Of course, this is only an option if the sorting algorithm is quite simple. It can still come in handy some day.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1b25d03 elementor-widget elementor-widget-heading\" data-id=\"1b25d03\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">API caveats<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-23a4b57 elementor-widget elementor-widget-text-editor\" data-id=\"23a4b57\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Finally, there\u2019s a few API concepts in Qt that don\u2019t map well to the Python environment, notably, a function taking a reference and thereby altering an existing object. One such example is <code data-no-translation=\"\">QValidator<\/code>. It has a <code data-no-translation=\"\">fixup(QString &amp;input)<\/code> method that you can override to sanitize the given input. PySide6 addresses this by altering the function signature to instead <em>return<\/em> a string. This is a common difference between C++ Qt and PySide6. Unfortunately, sometimes this wasn\u2019t done consistently and the aforementioned method wasn\u2019t usable from Python. Luckily, once reported, the bug was fixed within an hour and from Qt 6.8.3 it\u2019s possible to write fully custom input validators in Python!<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d54f67e elementor-widget elementor-widget-text-editor\" data-id=\"d54f67e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Another one is <code data-no-translation=\"\">QDataStream<\/code>: In C++ you can stream stuff into it using <code data-no-translation=\"\">operator&lt;&lt;<\/code>. In Python, while possible, it often doesn\u2019t yield the desired result since Python really only knows Int and Float (and Complex) numbers. Instead, use the type-safe <code data-no-translation=\"\">readInt16<\/code>, <code data-no-translation=\"\">readInt32<\/code>, <code data-no-translation=\"\">readUInt16<\/code>, <code data-no-translation=\"\">readUInt32<\/code>, etc methods PySide6 adds.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-14142a0 elementor-widget elementor-widget-heading\" data-id=\"14142a0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Conclusion<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5d0a840 elementor-widget elementor-widget-text-editor\" data-id=\"5d0a840\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>PySide6 is easy to get started with, both when coming from a Python or C++ background. However, there are a few things to consider, particularly if you have a strong background with Qt in C++. If you merely write down the code you thought up in your head with a different syntax (i.e. without braces and semicolons) you can be in for a little surprise as we have seen above. The overhead of Python as an interpreted language and the translation between it and C++ can add up, too. Nevertheless, this blog post should help you overcome the worst obstacles in your next Qt project with Python!<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2601 elementor-mobile-align-center elementor-widget elementor-widget-post-info\" data-id=\"2601\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"post-info.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<ul class=\"elementor-inline-items elementor-icon-list-items elementor-post-info\">\n\t\t\t\t\t\t\t\t<li class=\"elementor-icon-list-item elementor-repeater-item-3631f59 elementor-inline-item\" itemprop=\"author\">\n\t\t\t\t\t\t<a href=\"https:\/\/www.basyskom.de\/en\/author\/kubr\/\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-author\">\n\t\t\t\t\t\t\t\t\t\tKai Uwe Broulik\t\t\t\t\t<\/span>\n\t\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t<\/li>\n\t\t\t\t<li class=\"elementor-icon-list-item elementor-repeater-item-2853ced elementor-inline-item\" itemprop=\"datePublished\">\n\t\t\t\t\t\t<a href=\"https:\/\/www.basyskom.de\/en\/2025\/06\/23\/\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-date\">\n\t\t\t\t\t\t\t<span class=\"elementor-post-info__item-prefix\">\/\/<\/span>\n\t\t\t\t\t\t\t\t\t\t<time>June 23, 2025<\/time>\t\t\t\t\t<\/span>\n\t\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t<\/li>\n\t\t\t\t<li class=\"elementor-icon-list-item elementor-repeater-item-ea2575e elementor-inline-item\" itemprop=\"about\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-terms\">\n\t\t\t\t\t\t\t<span class=\"elementor-post-info__item-prefix\">\/\/<\/span>\n\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-post-info__terms-list\">\n\t\t\t\t<a href=\"https:\/\/www.basyskom.de\/en\/tag\/pyside\/\" class=\"elementor-post-info__terms-list-item\">Pyside<\/a>, <a href=\"https:\/\/www.basyskom.de\/en\/tag\/python\/\" class=\"elementor-post-info__terms-list-item\">Python<\/a>, <a href=\"https:\/\/www.basyskom.de\/en\/tag\/qt\/\" class=\"elementor-post-info__terms-list-item\">Qt<\/a>\t\t\t\t<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t\t\t\t<\/li>\n\t\t\t\t<\/ul>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>PySide6 is easy to use and powerful but there\u2019s a few pitfalls to look out for. We\u2019ll investigate a performance issue in a large data model, find out that function calls are expensive, and that some of Qt\u2019s C++ APIs had to be adapted to work in a Python environment.<\/p>","protected":false},"author":23,"featured_media":12155,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1,2,846,8],"tags":[805,804,15],"class_list":["post-12122","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein","category-blog","category-python","category-qt","tag-pyside","tag-python","tag-qt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/12122","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/users\/23"}],"replies":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/comments?post=12122"}],"version-history":[{"count":26,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/12122\/revisions"}],"predecessor-version":[{"id":12159,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/12122\/revisions\/12159"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media\/12155"}],"wp:attachment":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media?parent=12122"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/categories?post=12122"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/tags?post=12122"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}