I'm trying to add a header to a TableView in QML, but it is not working as expected. Here's a sample code:
Main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
QML:
import QtQuick 2.12
import QtQuick.Window 2.12
import Qt.labs.qmlmodels 1.0
import QtQuick.Controls 2.15
Window {
width: 600
height: 600
visible: true
title: "Player"
TableView {
id: tableView
anchors.fill: parent
columnSpacing: 1
rowSpacing: 1
boundsBehavior: Flickable.StopAtBounds
model: TableModel {
id: tabela
TableModelColumn { display: "type" }
TableModelColumn { display: "speed" }
TableModelColumn { display: "ammunition" }
TableModelColumn { display: "active" }
TableModelColumn { display: "coordinates" }
rows: [
{
type: "1",
speed: "10",
ammunition: "30",
active: "True",
coordinates: "4x2"
},
{
type: "3",
speed: "0",
ammunition: "3",
active: "False",
coordinates: "10x2"
},
]
}
delegate: Text {
text: model.display
padding: 20
Rectangle {
anchors.fill: parent
color: "#efefef"
z: -1
}
}
}
HorizontalHeaderView {
id: horizontalHeader
syncView: tableView
anchors.left: tableView.left
model: TableModel {
TableModelColumn { display: "type"}
TableModelColumn { display: "speed" }
TableModelColumn { display: "ammunition" }
TableModelColumn { display: "active" }
TableModelColumn { display: "coordinates" }
}
}
}
Here's the output I get from this code:
Why is the header overlapped with the table? I've provided the header with a a sync view (tableView) in order to sync the header to table. According to the documentation:
Once this property is bound to another TableView, both header and table will synchronize with regard to column widths, column spacing, and flicking horizontally.
The header's label also seems wrong. I was expecting to get "Type", "Speed", etc. I haven't found much documentation about TableModelColumn, which is used to build the header.
You can set the topMargin property of TableView to the height of the header to keep the header row above the content rows.
TableView {
id: tableView
topMargin: horizontalHeader.implicitHeight
model: TableModel {
}
}
HorizontalHeaderView {
id: horizontalHeader
syncView: tableView
anchors.left: tableView.left
}
And syncView means the HorizontalHeaderView will use the model of the TableView, to my understanding you shouldn't need to specify the model property in the HorizontalTableView.
Related
I'm looking for how to input for Loader inside Component.
Here is my problem.
// CustomSplitView.qml
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Pane {
function addItem(text, item) {
/**
Here is the problem part I think.
"headerText" is correctly applied to the 'template' object,
but "content" is not.
**/
splitView.addItem(template.createObject(null, {"headerText": text, "content": item}))
}
Component {
id: template
Pane {
property alias headerText: label.text
property alias content: loader.sourceComponent
ColumnLayout { anchors.fill: parent
Label { id: label }
Loader {
id: loader
Layout.fillWidth: true; Layout.fillHeight: true
sourceComponent: Rectangle { color: "Green" }
} // Loader
} // ColumnLayout
} // Pane
} // Component
SplitView {
id: splitView
anchors.fill: parent
} // SplitView
} // Pane
// Usage
Pane {
Component {
id: redRect
Rectangle { color: "Red" }
} // Component
Column { anchors.fill: parent
Button {
text: "add"
onClicked: customSplitView.addItem("RED", redRect.createObject())
} // Button
CustomSplitView { id: customSplitView }
} // Column
} // Pane
Result: When "add" button clicked, adding item inside of the split view has "RED" text but green rectangle appears, not red.
It's not a size issue. (detailed resizing code has been omitted for code simplicity)
Any advise will helps a lot, for what I missed, or other approaches.
Whenever you want to dynamically create (and, possibly destroy) components, I would highly recommend using a model with a delegate. Because your delegate appears to be dynamic, I recommend using DelegateChooser. This will remove the pain of managing the dynamic creation and destruction of your objects.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
header: Frame {
RowLayout {
Button {
text: "add"
onClicked: customSplitView.addRandomItem()
}
Button {
text: "clear"
onClicked: customSplitView.clear()
}
}
}
CustomSplitView {
id: customSplitView
}
}
// CustomSplitView.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.qmlmodels
Pane {
anchors.fill: parent
SplitView {
anchors.fill: parent
Repeater {
id: repeater
model: ListModel {
id: listModel
}
delegate: DelegateChooser {
role: "typ"
DelegateChoice {
roleValue: "label"
delegate: ColumnLayout {
SplitView.preferredWidth: w
SplitView.preferredHeight: h
Label {
Layout.fillWidth: true
text: txt
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: lbl
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
}
}
DelegateChoice {
roleValue: "rect"
delegate: ColumnLayout {
SplitView.preferredWidth: w
SplitView.preferredHeight: h
Label {
Layout.fillWidth: true
text: txt
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Qt.rgba(r, g, b, 1)
}
}
}
}
}
}
function addRandomItem() {
if (Math.random() < 0.5) {
listModel.append( {
typ: "label",
txt: "Label",
lbl: "Label " + Math.random(),
w: 200,
h: 50,
r: 0,
g: 0,
b: 0
} );
} else {
listModel.append( {
typ: "rect",
txt: "Rect",
lbl: "",
w: 50,
h: 50,
r: Math.random(),
g: Math.random(),
b: Math.random()
} );
}
}
function clear() {
listModel.clear();
}
Component.onCompleted: addRandomItem()
}
You can Try it Online!
If, on the other hand, you really want fully adhoc QML components controlled by a string. You should consider using createQmlObject or Loader with data-uri. Below is a demonstration of the latter:
import QtQuick
import QtQuick.Controls
Page {
id: page
Repeater {
model: [
{ qml: "Rectangle { }",
obj: { width: 100,
height: 100,
color: "red" } },
{ qml: "Rectangle { radius: height * 0.5 }",
obj: { width: 50,
height: 50,
color: "orange" } },
{ qml: "Button { }",
obj: { text: "Click Me" } }
]
delegate: Loader {
x: Math.random() * page.width
y: Math.random() * page.height
Component.onCompleted: setSource(
`data:text/plain,import QtQuick
import QtQuick.Controls
` + modelData.qml,
modelData.obj
);
}
}
}
You can Try it Online!
Thanks for insight of Stephen Quan, I could instantiate Component type as an argument of function.
// CustomSplitView.qml
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Pane {
function addItem(text, component) {
listModel.append( {text, component} )
} // addItem
SplitView { id: splitView
anchors.fill: parent
Repeater {
model: ListModel { id: listModel } // ListModel
delegate: Pane {
property string _headerText: text /* input "text" */
property var _component: component /* input "component" */
ColumnLayout { anchors.fill: parent
Label { text: _headerText }
Loader { Layout.fillWidth: true; Layout.fillHeight: true
sourceComponent: _component
} // Loader
} // ColumnLayout
} // Loader
} // Repeater
} // SplitView
} // Pane
// Usage
Window {
Component { id: rectComponent
Rectangle {
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
} // Rectangle
} // Component
Component { id: labelComponent
Label {
text: "Label " + Math.random()
} // Label
} // Component
Pane { anchors.fill: parent
ColumnLayout { anchors.fill: parent
CustomSplitView { id: customSplitView
Layout.fillWidth: true; Layout.fillHeight: true
orientation: Qt.Horizontal
} // customSplitView
Button {
text: "Add"
onClicked: {
customSplitView.addItem(
"Label", Math.random() < 0.5 ? rectComponent : labelComponent)
} // onClicked
} // Button
} // ColumnLayout
} // Pane
} // Window
Now the "add" button generates random color of rectangle or random label randomly.
I'm trying to do a menu that might have submenus for some of its options.
The behavior i want it to have is the same we see on most of websites.
When we hover an option that has a submenu, that submenu it will happear, if the mouse arrow goes anywhere else that is not the submenu the submenu will close.
I'm going to illustrate with images.
When we enter the submenu we have this:
Now we hover the Language option its submenu will happear
Now what is not done is the behavior i want. If we are hovering on the Languageoption the submenu is visible. If i go directly from language to the language's submenu it will remain there as intented.
The code of this example is provided below:
main.qml
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.3
import QtQuick.Window 2.3
ApplicationWindow {
visible: true
width: 640
height: 500
title: qsTr("Tabbars")
Button{
id: button
text: "Menu"
onClicked: contextMenu.open()
anchors.top:parent.top
anchors.left:parent.left
height: 20
width: 100
}
Menu {
id: contextMenu
y: button.height
padding: 1
background: Rectangle {
implicitWidth: 200
border.color: "#fff"
color: "#000"
}
Button {
id: languageMenuItem
text: qsTr("Language")
width:parent.width
height: 35
background: Item {
Rectangle {
anchors.fill: parent
color: "#555"
opacity: mouseArea1.pressed ? 1 : mouseArea1.containsMouse ? 0.6 : 0.0
MouseArea {
id: mouseArea1
anchors.fill: parent
hoverEnabled: true
onEntered: function() {
submenuLanguage.open()
}
onExited: function() {
}
}
}
}
contentItem: Text {
text: languageMenuItem.text
color: "#fff"
font.pointSize: 12
font.bold: true
}
Rectangle {
z: 1
color: "#000"
opacity: 0.5
anchors.fill: parent
visible: !parent.enabled
}
Component.onCompleted: {
mouseArea1.clicked.connect(clicked)
}
}
CMenuItem{
text: qsTr("Exit")
width: parent.width
onClicked: close()
}
}
Menu {
id:submenuLanguage
x: contextMenu.width
background: Rectangle {
implicitWidth: 200
border.color: "#fff"
color: "#000"
}
Connections {
target: mouseArea1
onExited: {
console.log("mouseArea leaving")
}
}
CMenuItem{
id:btlingen
width: parent.width
text: qsTr("English")
onClicked: {
contextMenu.close()
console.log("English")
}
}
CMenuItem{
id:btlingpt
width: parent.width
text: qsTr("Português")
onClicked: {
contextMenu.close()
console.log("Português")
}
}
CMenuItem{
id:btlinges
width: parent.width
text: qsTr("Español")
onClicked: {
contextMenu.close()
console.log("Español")
}
}
CMenuItem{
id:btlingit
width: parent.width
text: qsTr("Italiano")
onClicked: {
contextMenu.close()
console.log("Italiano")
}
}
CMenuItem{
id:btlingde
width: parent.width
text: qsTr("Deutsch")
onClicked: {
contextMenu.close()
console.log("Deutsch")
}
}
}
}
CMenuItem.qml
import QtQuick 2.0
import QtQuick.Controls 2.1
import QtQuick.Controls.Styles 1.4
MenuItem {
id: mainMenuItem
background: Item {
Rectangle {
anchors.fill: parent
color: "#555"
opacity: mouseArea.pressed ? 1 : mouseArea.containsMouse ? 0.6 : 0.0
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
}
}
}
contentItem: Text {
text: mainMenuItem.text
color: "#fff"
font.pointSize: 12
font.bold: true
}
Rectangle {
z: 1
color: "#000"
opacity: 0.5
anchors.fill: parent
visible: !parent.enabled
}
Component.onCompleted: {
mouseArea.clicked.connect(clicked)
}
}
How can i do this?
Use the cascade property to create a nested menu:
ApplicationWindow {
id: window
width: 320
height: 260
visible: true
menuBar: MenuBar {
Menu {
title: qsTr("&Foo")
Menu {
cascade: true // Nested menu
title: qsTr("&Bar")
Action { text: qsTr("A1") }
Action { text: qsTr("A2") }
Action { text: qsTr("A3") }
}
}
}
}
I am trying to run the following, but nothing happens when I run it.
How can I debug such an issue?
import QtQuick 2.0
import QtQml.Models 2.1
Item{
id: main
width: 1500
height: 1500
GridView {
id: root
width: 1500
height: 1500
cellWidth: 200; cellHeight: 200
visible: true
model: DelegateModel {
model: ListModel {
ListElement {
color: "blue"
}
ListElement {
color: "white"
}
ListElement {
color: "red"
}
ListElement {
color: "green"
}
ListElement {
color: "orange"
}
ListElement {
color: "yellow"
}
ListElement {
color: "grey"
}
}
delegate: MouseArea {
objectName: "mousearea"
implicitHeight: parent.height
implicitWidth: parent.width
Rectangle {
anchors.fill: parent
color: model.color
}
drag{
target: parent
}
}
}
}
}
What I intend from this code is the following:
create few rectangles inside a GridView and add a MouseArea to them and try dragging them around afterwards. I am not sure if my model structure is correct here.
EDIT:
Adding main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
QQmlApplicationEngine expects to have a Window as the root element as indicated by the docs:
...
Unlike QQuickView, QQmlApplicationEngine does not automatically create
a root window. If you are using visual items from Qt Quick, you will
need to place them inside of a Window.
...
So the solution is simple, change Item by Window:
main.qml
import QtQuick 2.0
import QtQuick.Window 2.11
import QtQml.Models 2.1
Window{
visible: true
id: main
width: 1500
height: 1500
GridView {
id: root
width: 1500
height: 1500
cellWidth: 200; cellHeight: 200
visible: true
model: DelegateModel {
model: ListModel {
ListElement {
color: "blue"
}
ListElement {
color: "white"
}
ListElement {
color: "red"
}
ListElement {
color: "green"
}
ListElement {
color: "orange"
}
ListElement {
color: "yellow"
}
ListElement {
color: "grey"
}
}
delegate: MouseArea {
objectName: "mousearea"
implicitHeight: parent.height
implicitWidth: parent.width
Rectangle {
anchors.fill: parent
color: model.color
}
drag{
target: parent
}
}
}
}
}
I want to set my demo layout is Right to Left, and I set this in main function, like this:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
app.setLayoutDirection(Qt::RightToLeft)
return app.exec();
}
Here is my qml file:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
id:root
Row {
spacing: 20
Repeater {
model: 5
Rectangle {
color: "red"
opacity: (5 - index) / 5
width: 70; height: 30
Text {
text: index + 1+" hello"
width:parent.width
}
}
}
}
}
However the layout result still is left to right:
How can I get the real RTL layout, all components are right to left, include the text, just like this:
Right-to-left User Interfaces says that you should use the LayoutMirroring attached properties. Taking the example from that page:
import QtQuick 2.0
Rectangle {
LayoutMirroring.enabled: true
LayoutMirroring.childrenInherit: true
width: 300; height: 50
color: "yellow"
border.width: 1
Row {
anchors { left: parent.left; margins: 5 }
y: 5; spacing: 5
Repeater {
model: 5
Rectangle {
color: "red"
opacity: (5 - index) / 5
width: 40; height: 40
Text {
text: index + 1
anchors.centerIn: parent
}
}
}
}
}
The problem is simple: window doesn't rendering and refresh, until the program is finished. It just doesn't show anything.
And I want to see the window even if the long cycle is not finished yet.
I will be very grateful for any help!
#include <QtGui>
#include <QtQml>
int main(int _nArgCount, char * _pArgValues[]) {
QApplication app(_nArgCount, _pArgValues);
//QMLblock
QString strQmlPath = "../main.qml";
QQmlApplicationEngine engine;
QQmlComponent pComponent(&engine, strQmlPath);
if( pComponent.status()==QQmlComponent::Error )
{ qDebug()<<"Error:"<<pComponent.errorString();
return app.exec();
}
QObject * pQmlObject = pComponent.create();
QObject * pWindow = pQmlObject->findChild<QObject*>("initStateGui");
QObject * pWindowNext = pQmlObject->findChild<QObject*>("searchRemovableGui");
pWindow->setProperty("visible","false");
pWindowNext->setProperty("visible","true");
QObject * pList = pQmlObject->findChild<QObject*>("devicesList");
QStringList s;
QString str;
s.append("3");
pList->setProperty("model",s);
for (int i=0; i<5; i++){
s.append(str.number(i));
pList->setProperty("model",s);
}
return app.exec();
}
And my QML (I don't think it's needed, but anyway):
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
QtObject {
property real defaultSpacing: 10
property SystemPalette palette: SystemPalette { }
property var controlWindow: Window {
width: 500
height: 500
color: palette.window
title: "Updater"
visible: true
//init state
Column {
id: initStateGui
objectName: "initStateGui"
anchors.fill: parent
anchors.margins: defaultSpacing
spacing: defaultSpacing
property real cellWidth: initStateGui.width / 3 - spacing
visible: true
Text { text: "Init state" }
Grid {
id: grid
columns: 3
spacing: defaultSpacing
width: parent.width
Button {
id: showButton
width: initStateGui.cellWidth
text: "Cancel"
onClicked: Qt.quit()
}
Button {
id: initStateContinue
objectName: "initStateContinue"
width: initStateGui.cellWidth
text: "Continue"
signal sigInitStateContinue()
onClicked: initStateContinue.sigInitStateContinue()
}
}
Text {
id: textLabel
text: "Welcome to the updater!"
}
Rectangle {
id: horizontalRule
color: "black"
width: parent.width
height: 1
}
}
//updater update state
Column {
id: updaterUpdateGui
objectName: "updaterUpdateGui"
anchors.fill: parent
anchors.margins: defaultSpacing
spacing: defaultSpacing
visible: false
property real cellWidth: initStateGui.width / 3 - spacing
Text { text: "UpdaterUpdate State" }
Grid {
id: grid1
columns: 3
spacing: defaultSpacing
width: parent.width
Button {
id: showButton1
width: initStateGui.cellWidth
text: "Cancel"
onClicked: Qt.quit()
}
Button {
id: updaterUpdateContinue
objectName: "updaterUpdateContinue"
width: initStateGui.cellWidth
text: "Continue"
signal sigUpdaterUpdateContinue()
onClicked: updaterUpdateContinue.sigUpdaterUpdateContinue()
}
}
Text {
text: "Update is started!"
}
Rectangle {
id: horizontalRule1
color: "black"
width: parent.width
height: 1
}
}
//removable Search gui
Column {
id:searchRemovableGui
objectName: "searchRemovableGui"
anchors.fill: parent
anchors.margins: defaultSpacing
spacing: defaultSpacing
visible: false
property real cellWidth: initStateGui.width / 3 - spacing
Text { text: "Removable search State" }
Grid {
id: grid2
columns: 3
spacing: defaultSpacing
width: parent.width
Button {
id: showButton2
width: initStateGui.cellWidth
text: "Cancel"
onClicked: Qt.quit()
}
}
Text {
text: "Searching for removable, please wait...!"
}
ListView {
id:devicesList
objectName:"devicesList"
width: 100; height: 500
model: myModel
delegate: Rectangle {
height: 15
width: 100
Text { text: modelData }
}
}
}
}
}
Addition: i don't need threads, i need to see the freezed window with the caption.
I see it if i add the button, and begin cycle after the button is pressed.
Without the button the window doesn't rendering, and i can't find how to do it.
It's impossible to realize in one thread.
Only moving long process to another thread allows to render GUI.