How to stretch the last column of a TableView in qml?
In C++ with Qt framework it is possible to use setStretchLastSection from QHeaderView but is there a way to do it in qml?
EDIT: To be more specific: On the screenshot below, I want the three column to fill the entire width of the TableView, in order to get rid that "ghost" 4th column.
Edit2:
I wanted to do something like that:
TableView{
model: ListModel{
ListElement {
name: "Apple"
cost: 2.45
}
ListElement {
name: "Orange"
cost: 3.25
}
ListElement {
name: "Banana"
cost: 1.95
}
}
TableViewColumn{
role: "name"
title: "Name"
}
TableViewColumn{
role: "cost"
title: "Cost"
}
Component.onCompleted: stretchLastSection /*<-----/*
}
But the way to do it for now seems to be by calculating the width of TableViewColumn as #folibis said.
Some related topic with QtDesigner and Qt framework:
QTableView / QTableWidget: Stretch Last Column using Qt Designer
Qt table last column not stretching to fill in parent
How to stretch QTableView last column header
Related
In Qt Quick Controls 1 we can style different columns from a model with TableViewColumn:
TableView {
anchors.fill: parent
TableViewColumn {title: "1"; role: "one"; width: 70 }
TableViewColumn {title: "2"; role: "two"; width: 70 }
TableViewColumn {title: "3"; role: "three"; width: 70 }
model: theModel
}
How can we achieve a similar result in Qt Quick 2 if there is no more TableViewColumn for the TableView?
Since Qt 5.12 you can use TableView QML type.
But to have everything you want you need to include Qt.labs.qmlmodels. It all is available in Qt 5.15 (use online installer).
Actual implementation will hardly depend on your requirements, but here is an example of how it could be done.
Let's say you are going to use TableView to show some data you have in JSON format. In this case TableModel will be a perfect fit, since it is designed to work with JavaScript/JSON data, where each row is a simple key-pair object without requiring the creation of a custom QAbstractTableModel subclass in C++.
You need to declare what columns do you need to have in your model by using TableModelColumn, e.g.: TableModelColumn { display: "checked" }.
For loading real data into model use its rows property; data should be in the form of an array of rows, e.g.:
rows: [
// Each property is one cell/column.
{
checked: false,
amount: 1,
type: "Apple",
price: 1.50
},
{
checked: true,
amount: 4,
type: "Orange",
price: 2.50
},
...
]
Most interesting part here is appliance of delegate -- here it be DelegateChooser, since it allows a view to use different delegates for different types of items in the model. So here you can do almost everything to tweak your cells. E.g.: you can use ProgressBar component as your delegate for a cell:
DelegateChoice {
column: 1
delegate: ProgressBar {
enabled: false
width: 100
from: 0.0
to: 10.0
value: model.display
}
}
As result you could easily get this application entirely in QML (whiteout need to use C++ and/or old QtQuick.Controls):
Please refer to this repository to get full application.
I have the following horizontal list:
BulletsListDelegate.qml
import QtQuick 2.0
Rectangle {
width: 8
height: 8
color: "#808080"
radius: width * 0.5
}
main.qml
import QtQuick 2.0
Item {
width: 256
height: 256
ListModel {
id: bulletsListModel
ListElement {
a: 'example'
}
ListElement {
a: 'example'
}
...
}
ListView {
id: bulletsList
spacing: 8
orientation: ListView.Horizontal
delegate: BulletsListDelegate {}
model: bulletsListModel
anchors.bottom: parent.bottom
width: parent.width
}
}
And the elements are shown like this (they are the grey bullets)
I want them to show in the horizontal center of the black box above them (its parent).
Is there any form of centralize or justify the items of the list?
So, if I understand your question correctly, you want to have alignment for instances of your items in a ListView. Using a ListView, that is not so easy to achieve. If you have an uneven number of items, you can do it by using preferredHighlightBegin and preferredHighlightEnd to have a 1-item sized region in the center of your ListView, and then setting hightlightRangeMode to ListView.StrictlyEnforceRange. You can set the currentIndex to point to index so that the middle item will be considered current. That puts it within the range you defined, and thus in the center. That does not work when you have an even number of items though, so it's a hack with limited value.
My question is: do the items have to be positioned using a ListView? It looks like you don't actually need much of the functionality of the ListView at all? If you don't need the other features from ListView (like scrolling), you can just use a Repeater instead. That allows you to simply put the items in a Row positioner, which you can width of count*(delegateWidth+spacing)-spacing and a height equal to your delegate height. Then, you can use acnhors to position the Row centered to whatever you like.
André's answer suggested me to use Repeater and Rows instead of ListView, and it completely solved my problem. But... didn't find a way to align an actual ListView yet.
Row {
spacing: 8
Repeater {
id: bulletsRepeater
model: 5
BulletsDelegate { }
}
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
I have 2 static ListModel's in this example, in reality I use LocalStorage to fill the ListModel, but to keep it simple, I added 2 buttons to change the Models, but I want to tie it to the TableView's Header Column click event, and can not figure out how to do that from other examples of trying to sort, I do not know if it is possible to have a sort using ListModel, I could not find any example, so can someone explain this or show an example, of how to replace the buttons with column click events, I can then use this to pass the sort by argument to my LocalStorage sql statement to update the ListModel.
Update: I forgot to mention I was looking for a Qml / Qml JavaScript solution, for some reason I thought if I left off the C++ tag, I would avoid this issue, I will use this method as a last resort, since I decided to write this App using only Qml, with no C++ back end, but I do have that version now, since I had issues with how I was importing JavaScript written for the Web, as opposed to Qml JavaScript, which is not the same.
To be clear, I am trying to change Models and not Sort the rows, those are not the same question, the difference is in how the click event is used, and all I want to do is change the name of a query in the back end, which is Qml JavaScript, the reason I do not want C++ solutions is because I am doing this in Felgo, but this is not a Felgo quesiton, it works fine with C++, but you have to set up Live to work with it, and in reality this is going to be source I open up to github, and want it to be able to work without C++, and it seems there should be a way to hook this, and Mouse did work for me, capturing it in the even keep the header from loading, since it hooks at the beginning and waits for input, but if I have to, I am sure your solution will work, then I will accept it, sorry about that confusion, I get confused about what tags to use, so originally I only included qml, and qt was added to it, which I though was a great idea, because this really is a Qt question, that relates to Qml only, and not C++, that is another tag, this is a trend that Felgo is pushing, and they have good reasons, its easier for JavaScript or C/C++ Programmers to use, and Live Debugging works faster when used without a C++ back end, so now I gave more information, when Originally I though this was a simple question, that only related to Qml, if not then the answer has been given for C++, unless there is a better way, seeing how all I want to do is click on the header column the same way I would like on the button, can I embed the button into the column? If so how? I could not find an example of this, only ones that would effect text properties, and would sort the rows, which is not what I was trying to do, only update the model.
import QtQuick 2.11
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.3
import QtQuick.Window 2.11
Window {
visible: true
width: 640
height: 480
title: qsTr("TableView Sort")
Column {
id: column
spacing: 9
anchors.fill: parent
TableView {
id: tableView
anchors.left: column.left
anchors.leftMargin: 6
anchors.right: column.right
anchors.rightMargin: 273
highlightOnFocus: true
model: myListModel1
sortIndicatorVisible: true
TableViewColumn {
role: "title"
title: "Column 1"
//width: 133
}
TableViewColumn {
role: "description"
title: "Column 2"
//width: 166
}
}
Button {
id: button1
text: qsTr("Model 1")
anchors.left: column.left
anchors.leftMargin: 6
onClicked: {
tableView.model = myListModel1
}
}
Button {
id: button2
text: qsTr("Model 2")
anchors.left: column.left
anchors.leftMargin: 6
onClicked: {
tableView.model = myListModel2
}
}
}
ListModel {
id: myListModel1
ListElement {
title: "Orange"
description: "Orange is Orange"
}
ListElement {
title: "Banana"
description: "Yellow"
}
ListElement {
title: "Apple"
description: "Red"
}
}
ListModel {
id: myListModel2
ListElement {
title: "Apple"
description: "Red"
}
ListElement {
title: "Banana"
description: "Yellow"
}
ListElement {
title: "Orange"
description: "Orange is Orange"
}
}
}
Update: This worked
onSortIndicatorColumnChanged: tableView.model = (sortIndicatorColumn == 0) ? myListModel1 : myListModel2
onSortIndicatorOrderChanged: tableView.model = (sortIndicatorColumn == 0) ? myListModel1 : myListModel2
Thanks for any help.
You could use a proxy model to sort your model. But, there is no QML component and you have to use QSortFilterProxyModel.
It's quite easy to do. But, QSortFilterProxyModel is not made to be used with a QML tableview (your table uses role names to display the columns and the proxy model will attempt to sort on index).
A quick example:
class SortProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
SortProxyModel(): QSortFilterProxyModel ()
{
}
// Define the way you want to sort data
bool lessThan(const QModelIndex& left, const QModelIndex& right) const
{
int role = sourceModel()->roleNames().key(roleName.toLocal8Bit(), 0);
return left.data(role) < right.data(role);
}
Q_INVOKABLE void setSortRole(QString const& roleName) // Used to select the sort role
{
this->roleName = roleName;
}
Q_INVOKABLE virtual void sort(int /*column*/, Qt::SortOrder order = Qt::AscendingOrder)
{
QSortFilterProxyModel::sort(0, order); // Always use the first column.
}
private:
QString roleName; // Role used to sort the model
};
// main.cpp
// Declare your type to use it in QML
qmlRegisterType<SortProxyModel>("SortProxyModel", 0, 1, "SortProxyModel");
// Main.qml
import SortFilterProxyModel 0.1;
TableView {
id: tableView
model: proxy // Use the proxy model rather than the model itself
sortIndicatorVisible: true
onSortIndicatorColumnChanged: { // Called when you click on the header
if (sortIndicatorColumn == 0) // Set the role used to sort data
model.setSortRole("title");
else
model.setSortRole("description");
model.sort(sortIndicatorColumn, sortIndicatorOrder)
}
onSortIndicatorOrderChanged: { // Called when you click on the header
if (sortIndicatorColumn == 0) // Set the role used to sort data
model.setSortRole("title");
else
model.setSortRole("description");
model.sort(sortIndicatorColumn, sortIndicatorOrder)
}
SortProxyModel {
id: proxy
objectName: "proxy"
sourceModel: myListModel1
}
It's just a quick example and you should improve the code. But, I think it will be a good start...
This question is basically asking the same thing - Is it possible to use QAbstractTableModel with TableView from QtQuick.Controls?
I have a QAbstractTableModel with data in the rows and columns. I want to be able to specify which column in my model the view should be displaying. This is not at all obvious to do, any help would be appreciated.
Basic examples rely on unique role names to ensure the correct data arrives in a given column, eg:
TableView {
TableViewColumn { role: "title"; title: "Title"; width: 100 }
TableViewColumn { role: "author"; title: "Author"; width: 200 }
model: libraryModel
}
The only hack I can think of is to assign each column in my model a unique role name, but this seems like a code smell, is there another simple way to specify the column index of my TableViewColumn?
I have choose an item in combobox. Item's position is 300 for example. If I want to choose new element from combobox . Popup shows from beginning. I want to popup opened from current item position.
ComboBox {
id: control
model: ["First", "Second", "Third","MERHABA","NASILSIN","SELAM","IYIMISIN","DOSTUM","SUAN","BIR","DENEME YAPILIYOR"]
//width: 350
//font.pixelSize: 20
delegate: ItemDelegate {
width: 350
text: modelData
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
font.pixelSize: 30
highlighted: control.highlightedIndex == index
}
For QtQuick.Controls 2, There is a 'popup' property, so we can set the popup position with it's own property 'y', like this:
ComboBox {
model: ["First", "Second", "Third"]
delegate: ItemDelegate {
text: modelData
}
popup.y: y + height // popup just below ComboBox
}
The ComboBox will work the way you want it if the conditions allow for it, that is, if you have enough elements to fill the entire drop down list after the current index item it will show from that item rather than the beginning.
The stock ComboBox however doesn't seem to allow you to specify the drop down list height, so it will take significantly more elements than you have in your example. Or significantly taller elements.
Furthermore, if the current index is the last element, how do you imagine this will show? The list would show only the last element plus a whole bunch of nothing, which is not even possible, the last item cannot move up from the end of the list.
If you really want that behavior, you will have to implement your own combo box element from scratch.
I was facing the same issue and found out that if you place the popup onOpened, it works perfectly:
ComboBox {
id: yearDropdown
model: yearModel
onActivated: updateVisibleDate()
popup: Popup {
id: comboPopup
clip: true
contentItem: ListView {
id: listView
implicitHeight: contentHeight
model: yearDropdown.popup.visible ? yearDropdown.delegateModel : null
onModelChanged: if(model) positionViewAtIndex(yearDropdown.currentIndex, ListView.Center);
ScrollIndicator.vertical: ScrollIndicator { }
}
onOpened: {
x = yearDropdown.x //Set the position you want
y = yearDropdown.y + yearDropdown.implicitHeight //Set the position you want
}
}
}