Why does setting implicitHeight on QML TableView's syncView degrade its performance drastically? - qt

I want to create a simple TableView that uses another TableView to display its headers using the syncView property. I layout the header above the main content in a column:
import QtQuick 2.14
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import Qt.labs.qmlmodels 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ColumnLayout {
anchors.fill: parent
TableView {
Layout.fillWidth: true
id: headerView
implicitHeight: contentHeight //comment this line out and it'll be much faster
model: TableModel {
TableModelColumn {display: "index"}
rows: [{index: "index"}]
}
delegate: Label {
text: model.display
}
}
TableView {
Layout.fillWidth: true
Layout.fillHeight: true
syncView: headerView
syncDirection: Qt.Horizontal
model: TableModel {
TableModelColumn {display: "index"}
rows: Array(100000).fill().map((_, index) => ({index}))
}
delegate: Label {
text: model.display
}
ScrollBar.vertical: ScrollBar {}
}
}
}
Unfortunately, setting a non-zero implicitHeight on the headerView causes the performance to be much slower. This can be felt by dragging the vertical scrollbar up and down rapidly. What is going on here? Am I using syncView the wrong way?

Related

Why does my SwipeView page not change when I change the Tab in the TabBar in QML?

So, I'm trying to create a traditional look for my QML application which simply consists of a TabBar and a SwipeView. There's also another component which essentially boils down to a button click which should result in a new tab in the TabBar and a new page in the SwipeView. This works, except that when I click on the previous tab (there's a static tab and page when the app starts up), the page in the view doesn't change.
Here's my QML file -
import QtQuick 2.12
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.12
import QtQuick.Controls 2.13
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
Component {
id: tabButton
TabButton {
width: implicitWidth
}
}
Component {
id: resultListView
Rectangle {
anchors.fill: parent
color: "green"
}
}
SwipeView {
id: tabBarLayout
Layout.fillHeight: true
Layout.fillWidth: true
interactive: false
currentIndex: 0
LogArea {
id: logArea
}
}
TabBar {
id: tabBar
currentIndex: tabBarLayout.currentIndex
Layout.fillWidth: true
font.capitalization: Font.MixedCase
TabButton {
text: qsTr("log")
width: implicitWidth
}
Connections {
target: qmlBridge
onLoadResultTab: {
tabBar.addItem(tabButton.createObject(tabBar, {text: qsTr("search - " + term)}))
tabBarLayout.addItem(resultListView.createObject())
tabBarLayout.incrementCurrentIndex()
console.log("Added new item...")
}
}
}
}
The onLoadResultTab signal is invoked when the button is clicked. As you can see, I'm adding a new TabButton and incrementing the current index and then creating another component which is just a simple Rectangle for now. Now, when I click on the "log" button, the SwipeView doesn't change its page to the one corresponding to the log tab. Could anyone point out what's the issue here?

Qt QML binding child property to parent property

I want to set the ApplicationWindow to minimum width and height by the minimum width and height of the child element "mainLayout". I am having trouble to use the property of "mainLayout" in the parent QML ApplicationWindow. I tried to make the property viewable by making an alias. Not sure if it is the right solution. It does not work. But there is also no Error when I run.
My code looks like this:
main.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
ApplicationWindow {
visible: true
width: 1500
height: 1200
property int margin: 11
minimumWidth: serial.mainLayout.minimumWidth + 2 * margin //this one is not working
minimumHeight: serial.mainLayout.minimumHeight + 2 * margin //this one is not working
Serial {
id: serial
anchors.fill: parent
}
}
Serial.qml
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import io.qt.serialComm 1.0
Item {
anchors.fill: parent
id: item
property alias mainLayout: mainLayout
ColumnLayout {
id: wrapper
width: parent.width/2
height: parent.height/2
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.margins: margin
GroupBox {
id: rowBox
title: "Row layout"
Layout.fillWidth: true
RowLayout {
id: rowLayout
anchors.fill: parent
TextField {
placeholderText: "This wants to grow horizontally"
Layout.fillWidth: true
}
Button {
text: "Button"
}
}
}
GroupBox {
id: gridBox
title: "Grid layout"
Layout.fillWidth: true
GridLayout {
id: gridLayout
rows: 3
flow: GridLayout.TopToBottom
anchors.fill: parent
Label { text: "Line 1" }
Label { text: "Line 2" }
Label { text: "Line 3" }
TextField { }
TextField { }
TextField { }
TextArea {
text: "This widget spans over three rows in the GridLayout.\n"
+ "All items in the GridLayout are implicitly positioned from top to bottom."
Layout.rowSpan: 3
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}
TextArea {
id: t3
text: "This fills the whole cell"
Layout.minimumHeight: 30
Layout.fillHeight: true
Layout.fillWidth: true
}
GroupBox {
id: stackBox
title: "Stack layout"
implicitWidth: 200
implicitHeight: 60
Layout.fillWidth: true
Layout.fillHeight: true
StackLayout {
id: stackLayout
anchors.fill: parent
function advance() { currentIndex = (currentIndex + 1) % count }
Repeater {
id: stackRepeater
model: 5
Rectangle {
color: Qt.hsla((0.5 + index)/stackRepeater.count, 0.3, 0.7, 1)
Button { anchors.centerIn: parent; text: "Page " + (index + 1); onClicked: { stackLayout.advance() } }
}
}
}
}
}
}
}
When I put the code in one file, it works and the ApplicationWindow does not get smaller than the minimum height and width of the child element "mainLayout". But splitting into 2 files does not work..
The reason why you are not able to use the property minimumWidth of your QML element with the id mainLayout like serial.mainLayout.minimumWidth is that it doesn't have one.
However, the QML element in question does have an attached property Layout.minimumWidth because it's an item in a ColumnLayout with the id wrapper. You already found out that you could access it through serial.mainLayout.Layout.minimumWidth.
Attached property mechanism that enables the minimumWidth for mainLayout is not the easiest one to understand. In short, it enables objects to be annotated with extra properties that are otherwise unavailable to the object but are relevant in certain circumstances. In this case minimumWidth is considered relevant for child items of ColumnLayout. Items in a ColumnLayout support these attached properties.

Adding TabButton dynamically to TabBar in QML

I am trying to add a tabButton to TabBar dynamically on pressing a button but i have spent a lot of time searching but i am not getting how to add, below is the code which i am working on :
MyTabButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
Item
{
property int BtnWidth:0
property int BtnHeight:0
property string BtnText: ""
property bool isChecked : false
TabButton
{
id:tabBtn
text:BtnText
width:BtnWidth
height:BtnHeight
}
}
MainForm.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
Rectangle
{
Button
{
id:button
width:100
height:100
anchors.top:parent.top
text:qStr("Add")
onClicked{
//How to add logic here to add tab in below tabBar.
}
}
TabBar
{
id:tabBar
anchors.top:button.bottom
width:500
height:500
}
}
Example:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
width: 360
height: 630
visible: true
header: TabBar {
id: tabBar
}
Component {
id: tabButton
TabButton { }
}
Button {
text: "Add"
anchors.centerIn: parent
onClicked: {
var tab = tabButton.createObject(tabBar, {text: "Tab " + tabBar.count})
tabBar.addItem(tab)
}
}
}
You need to have something like a Component that is a TabButton. Your file MyTabButton.qml won't result in a TabButton, but instead an Item containing a TabButton, but with this, your TabBar does not know what to do.
So your file will need to have TabButton as root element
//MyTabButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
TabButton
{
id: tabBtn
// customize as you like
}
Then you create a Component of this in your file where you want to use it. (e.g. main.qml)
import QtQuick 2.4
import QtQuick.Controls 2.0
ApplicationWindow {
width: 800
height: 600
visible: true
TabBar {
id: tabBar
width: 800
height: 50
}
// The component is like a factory for MyTabButtons now.
// Use myTabButton.createObject(parent, jsobject-with-property-assignments) to create instances.
Component {
id: myTabButton
MyTabButton {
/// EDIT ACCORDING TO YOUR COMMENTS ***
Connections {
target: tabBar
onCurrentIndexChanged: doSomething()
}
/// EDIT OVER
}
}
Button {
anchors.centerIn: parent
// Create a object out of the component, and add it to the container
onClicked: tabBar.addItem(myTabButton.createObject(tabBar /*, { object to set properties }*/))
}
}
TabBar inherits Container, which has addItem().
Try it in Window
Row {
anchors.fill: parent
TabBar {
id: tabBar
currentIndex: 0
width: parent.width - addButton.width
TabButton { text: "TabButton" }
}
Component {
id: tabButton
TabButton { text: "TabButton" }
}
Button {
id: addButton
text: "+"
flat: true
onClicked: {
tabBar.addItem(tabButton.createObject(tabBar))
console.log("added:", tabBar.itemAt(tabBar.count - 1))
}
}
}

Spacer Item in QML Layouts

I want to create a layout in QML and I'd like to add a spacer item (the bottom selected item from the image below) just as you do using widgets like so:
But I couldn't find anything to suit this on the QtQuick side of things...is it possible to have this kind of layout in QML w/o using the anchoring system?
I'd prefer the layouts approach...
You can simply use an Item with Layout.fillHeight: true :
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
ColumnLayout {
anchors.fill: parent
Button {
Layout.fillWidth: true
text: "PushButton"
}
Button {
Layout.fillWidth: true
text: "PushButton"
}
Label {
Layout.fillWidth: true
text: "TextLabel"
}
Item {
// spacer item
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle { anchors.fill: parent; color: "#ffaaaa" } // to visualize the spacer
}
}
}
EDIT: Alternatively here, you could have used a Column with no spacer item since a Column just positions its children from top to bottom and don't spread them to take all the available space.
For those coming from Qt widgets and for comparison: the intended solution in QML for this situation is the anchoring system that the question mentions. In this case, it would look as follows, and I think it's not so bad :)
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
ColumnLayout {
// anchors.fill sets all four directional anchors.
// Loosening one yields the space at the bottom.
anchors.fill: parent
anchors.bottom: undefined
// Alternative approach: only set the three anchors we want.
// anchors.top: parent.top
// anchors.left: parent.left
// anchors.right: parent.right
Button {
Layout.fillWidth: true
text: "PushButton"
}
Button {
Layout.fillWidth: true
text: "PushButton"
}
Label {
Layout.fillWidth: true
text: "TextLabel"
}
}
}

ComboBox disable an item at a particular index

I have a combobox in qml in a as a TableViewColummn and I define it as follows:
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
ListModel {
id: comboModel
ListElement {
text: ""
Index: -1
Dims: -1
}
}
TableViewColumn {
id: imageTypeList
role: "ImageType"
title: "Image Type"
width: 100
delegate: Rectangle {
ComboBox {
anchors.verticalCenter: parent.verticalCenter
anchors.margins: 2
model: comboModel
onActivated : {
console.log(comboModel.get(index).Index)
}
}
}
}
My question is that if it is possible to disable a combobox menu item given a index to the item in the ComboBox. So, I would not like to change the underlying model but actually simply disable the item and not allow the user to select it.
Is it possible to disable a ComboBox menu item ... and not allow the user to select it?
Sure, it is possible.
To do it using Quick Controls 2 you need to create ComboBox delegate this way:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 200
title: qsTr("Let's disable some items in ComboBox")
ComboBox {
id: control
currentIndex: 0
anchors.centerIn: parent
model: [
{ text: "Enabled item.", enabled: true },
{ text: "Supposed to be disabled. Can't click on it.", enabled: false},
{ text: "Last, but enabled item.", enabled: true}
]
width: 500
textRole: "text"
delegate: ItemDelegate {
width: control.width
text: modelData.text
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
highlighted: ListView.isCurrentItem
enabled: modelData.enabled
}
}
}
If you are using Quick Controls 1, you should provide your own implementation of ComboBox component.

Resources