{"id":11858,"date":"2025-05-08T10:02:20","date_gmt":"2025-05-08T08:02:20","guid":{"rendered":"https:\/\/www.basyskom.de\/?p=11858"},"modified":"2025-05-08T10:59:13","modified_gmt":"2025-05-08T08:59:13","slug":"modern-tableview-in-qml-whats-new-in-qt-6-8-and-beyond","status":"publish","type":"post","link":"https:\/\/www.basyskom.de\/en\/modern-tableview-in-qml-whats-new-in-qt-6-8-and-beyond\/","title":{"rendered":"Modern TableView in QML: What\u2019s New in Qt 6.8 and Beyond"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"11858\" class=\"elementor elementor-11858\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ef7d4b5 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-parent\" data-id=\"ef7d4b5\" 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-035c753 elementor-widget elementor-widget-heading\" data-id=\"035c753\" 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\">Some history on TableView in QML<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-99246f5 elementor-widget elementor-widget-text-editor\" data-id=\"99246f5\" 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>When implementing applications for our clients, one question that comes up consistently is whether some data could be displayed in a table-like format. Since the majority of our Qt-based projects make use of QtQuick for the UI, the answer often has been &#8220;yes, but&#8230;&#8221;.<\/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-24c879e elementor-widget elementor-widget-text-editor\" data-id=\"24c879e\" 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>The complexity of such a task has changed quite a lot over the years. With the flexibility of QML creating something like a table has been possible from the very start, it has mostly been a question of how much time one was willing to spend on implementing it. It made sense that at some point something like a TableView, similar to the existing view items, would be made available as part of the framework itself. So with Qt 5.1 (released in 2013) a first implementation of TableView was included in the QtQuickControls1 module (QtQuickControls2 didn&#8217;t exist until Qt 5.7). Back then the focus was more on providing appropriate alternatives to the objects that QtWidgets provided. Performance issues prevented or at least limited its usage for embedded or mobile uses cases, requiring potentially time consuming custom implementations.<\/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-ba7dcd7 elementor-widget elementor-widget-text-editor\" data-id=\"ba7dcd7\" 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>When Qt 5.12 was released five years later, TableView finally became part of the QtQuick module and an addition to existing views like ListView and GridView. Although limited in its capabilities, it was a reasonable starting point for implementing common use cases like selecting cells, highlighting, resizing or moving rows or columns. With some additional effort one could also implement cell editing, since naively using TextInput elements for each cell slowed things down quite noticeably, especially for larger models. The overall performance also increased significantly, due to the TableView only creating the delegates that were currently visible.<\/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-faf1568 elementor-widget elementor-widget-text-editor\" data-id=\"faf1568\" 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>Two years later Qt 5.15 added two essential view items to the QtQuickControls2 module: <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-controls-horizontalheaderview.html\" target=\"_blank\" rel=\"noopener\">HorizontalHeaderView<\/a> and <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-controls-verticalheaderview.html\" target=\"_blank\" rel=\"noopener\">VerticalHeaderView<\/a>. They allowed adding appropriate horizontal and vertical headers to an existing TableView, without the need for custom implementations. Both of these views are TableViews themselves and can be easily synched with the actual table, resulting in the expected behavior when scrolling larger tables.<\/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-bb027e5 elementor-widget elementor-widget-heading\" data-id=\"bb027e5\" 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\">The current state<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-dab2feb elementor-widget elementor-widget-text-editor\" data-id=\"dab2feb\" 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>With Qt 6 several years later, further enhancements have found their way into the TableView. Selection is now customizable in several ways, allowing selecting by row, column or by individual cells by setting a <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-tableview.html#selectionBehavior-prop\" target=\"_blank\" rel=\"noopener\">selectionBehavior<\/a> (Qt 6.4). This behavior can be further adapted by setting <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-tableview.html#selectionMode-prop\" target=\"_blank\" rel=\"noopener\">selectionMode<\/a> to single, contiguous or extended mode (Qt 6.6). Rows and columns can also be <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-tableview.html#resizableColumns-prop\" target=\"_blank\" rel=\"noopener\">resized<\/a> (Qt 6.5), and, when used in combination with HorizontalHeaderView or VerticalHeaderView, even <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-controls-horizontalheaderview.html#movableColumns-prop\" target=\"_blank\" rel=\"noopener\">moved<\/a> (Qt 6.8). One very significant enhancement was made regarding editing (Qt 6.5). A common <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-tableview.html#editDelegate-attached-prop\" target=\"_blank\" rel=\"noopener\">editDelegate<\/a> can now be specified, which will be instantiated when needed, according to the configured <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-tableview.html#editTriggers-prop\" target=\"_blank\" rel=\"noopener\">editTriggers<\/a>. The purpose of the default delegate of the TableView is to only display the current cell value, which can now be done by a lightweight component. No need to use a TextInput for each cell just to make it editable or come up with a mechanism to track the cell that a user currently wants to edit. And if you&#8217;re going to use the recently released Qt 6.9, it becomes even simpler: the new <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qtquick-controls-tableviewdelegate.html\" target=\"_blank\" rel=\"noopener\">TableViewDelegate<\/a> component does most of the work for you and provides suitable editing functionality and visualization.<\/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-87759de elementor-widget elementor-widget-text-editor\" data-id=\"87759de\" 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>With these relatively new capabilities it has become much easier to implement a full-fledged table with almost all of the expected functionality. Very few things remain that require a custom implementation, sorting and filtering for example. Although the Qt.labs.qmlmodels module provides a <a href=\"https:\/\/doc.qt.io\/qt-6\/qml-qt-labs-qmlmodels-tablemodel.html\" target=\"_blank\" rel=\"noopener\">TableModel<\/a> type that serves its purpose for some very simple use cases, you&#8217;re most probably\u00a0going to write your own model based on QAbstractTableModel, which you can then combine with a QSortFilterProxyModel, that can take care of sorting and filtering the data accordingly.<\/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-e7c35db elementor-widget elementor-widget-heading\" data-id=\"e7c35db\" 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-4a57b05 elementor-widget elementor-widget-text-editor\" data-id=\"4a57b05\" 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>As we can see a lot has happened, especially with Qt 6, to allow implementing useful table representations with QML. If you want to try it out for yourself you can check out <a style=\"font-family: pt-sans-v17-latin-regular, pt-sans-narrow; font-size: 18px; font-weight: normal; background-color: #ffffff;\" href=\"https:\/\/github.com\/basysKom\/example_tableview\" target=\"_blank\" rel=\"noopener\">a small example project<\/a>.<\/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-b83e67a pa-icon-pos-before premium-lq__none elementor-widget elementor-widget-premium-addon-button\" data-id=\"b83e67a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"premium-addon-button.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\n\n\t\t<a class=\"premium-button premium-button-none premium-btn-lg premium-button-none\" href=\"https:\/\/github.com\/basysKom\/example_tableview\" target=\"_blank\" rel=\"noopener\">\n\t\t\t<div class=\"premium-button-text-icon-wrapper\">\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg class=\"svg-inline--fab-fa-github premium-drawable-icon premium-svg-nodraw\" aria-hidden='true' xmlns='http:\/\/www.w3.org\/2000\/svg' viewbox='0 0 496 512'><path d=\"M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z\"><\/path><\/svg>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t<span >\n\t\t\t\t\t\tCheckout the Example\t\t\t\t\t<\/span>\n\t\t\t\t\t\t\t<\/div>\n\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t<\/a>\n\n\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>Over the years, the capabilities of QtQuick&#8217;s TableView have evolved dramatically-from early custom implementations to well supported feature in Qt 6.8 and newer. In this article we explore the progression of QtQuick\/QML&#8217;s TableView, outline the limitations of early versions, and highlight newer features such as custom selection modes, header synchronization, and lightweight editing delegates. Check it out.<\/p>","protected":false},"author":25,"featured_media":4011,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[2,8],"tags":[126,15],"class_list":["post-11858","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","category-qt","tag-qml","tag-qt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/11858","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\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/comments?post=11858"}],"version-history":[{"count":55,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/11858\/revisions"}],"predecessor-version":[{"id":11915,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/11858\/revisions\/11915"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media\/4011"}],"wp:attachment":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media?parent=11858"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/categories?post=11858"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/tags?post=11858"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}