QML: QtQuick.Controls Tabs with Icons - qt

I have been learning how to use QT Creator Tool so that I can build UI's quickly and easily. For my current project, I have to use QML to build my UI. I want to have tabs in my display. I would like to use an image in place of text in my tab. My code is below. I have tried to add a source but that did not help me add an icon. Does anyone know how to do this? All help would be greatly appreciated.
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.2
Window {
visible: true
width: 640
height: 400
opacity: 1
TabView {
height: 300
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
anchors.fill: parent
Tab {
title: "Robot"
component: Qt.createComponent("RobotControls.qml")
}
Tab{
title: "Tab #2"
}
}
}

Elaborating on the answer from Simon Warta in Extending TabViewStyle styleData, here is what you could do :
Define a custom IconTab
You want to extend the Tab item so that you can specify an icon to display.
So add a new file to your project, called IconTab.qml:
IconTab.qml
import QtQuick.Controls 1.4
Tab{
property string icon
}
Define a custom TabViewStyle.
In order to use this new property, you must create your own TabViewStyle. You may have to redefine background and text size and colors so that it fits your app theme, but something like this could work:
MyStyle.qml
import QtQuick 2.5
import QtQuick.Controls.Styles 1.2
TabViewStyle {
tab: Item {
implicitWidth: Math.round(textitem.implicitWidth + image.width + 20)
implicitHeight: Math.round(textitem.implicitHeight + 10)
Rectangle
{
anchors.fill: parent
anchors.bottomMargin: 2
radius: 1
border.width: 1
border.color: "#AAA"
color:"transparent"
}
Rectangle
{
anchors.fill: parent
anchors.margins: 1
anchors.bottomMargin: styleData.selected ? 0 : 2
radius: 1
gradient: Gradient{
GradientStop{position:0; color:styleData.selected?"#EDEDED":"#E3E3E3"}
GradientStop{position:1; color:styleData.selected?"#DCDCDC":"#D3D3D3"}
}
}
Text {
id: textitem
anchors.fill: parent
anchors.leftMargin: 4 + image.width
anchors.rightMargin: 4
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: styleData.title
elide: Text.ElideMiddle
}
Image
{
id: image
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 2
anchors.leftMargin: 4
fillMode: Image.PreserveAspectFit
source: control.getTab(styleData.index).icon
}
}
}
Note how you can make use of the control property and the styleData.index to get your icon's url: control.getTab(styleData.index).icon
Put the pieces together
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
Window {
visible: true
width: 640
height: 400
TabView {
id: tabView
anchors.fill: parent
style: MyStyle{}
IconTab {
title: "Tab #1"
icon: "icon.png"
}
IconTab{
title: "Tab #2"
}
IconTab{
title: "Tab #3"
icon: "icon.png"
}
}
}
Result

Related

How to use own objects in ColumnLayout in QML?

I would like to produce a table where in every Row is a Text() and a ComboBox(). I want the comboboxes are aligned right and also the left side of the text labels something like that:
I have the main qml:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
Window {
id: wind
width: 640
height: 480
visible: true
title: qsTr("Public Transport Searcher")
property string oz: "Odjezdová zastávka"
property string pz: "Příjezdová zastávka"
property string co: "Čas odjezdu"
Rectangle{
id: rect
x: wind.width/16
implicitHeight: parent.height/3*2
implicitWidth: wind.width/8*7
anchors.right: parent.right
anchors.left: parent.left
anchors.margins: 20
radius: 5
color: "lightblue"
ColumnLayout{
id: cl
spacing: 30
width: parent.width
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 20
anchors.fill: parent
Row{
id: r1
Layout.alignment: Qt.AlignTop
//Layout.alignment: Qt.AlignTop
Layout.margins: 15
Text{
id: r1t1
text: "Vyhledávač jízdních řádů"
font.pointSize: 16
font.bold: true
}
}
Line{
id: r2
tt2: oz
Layout.alignment: Qt.AlignLeft
//anchors.top: r1.bottom
}
Line{
id: r3
tt2: pz
Layout.alignment: Qt.AlignLeft
}
Line{
id: r4
tt2: co
Layout.alignment: Qt.AlignLeft
}
}
}
}
And a separate Line.qml item:
import QtQuick 2.15
import QtQuick.Controls 2.12
Item {
property string tt2: "v"
Text{
id: txt
text: tt2
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
ComboBox{
id: cb
anchors.verticalCenter: parent.verticalCenter
//anchors.fill: parent
anchors.left: txt.right
}
}
Now it runs but the all Rows are overwritting in the top left corner.
I know that I could use the GridLayout but I want to take advantage from the own objects for one row (in case I have much such rows) to avoid copy paste technic and initialise every object of the row separate.
Could you show me please how to do that in an elagant way?
The problem is that your Line items do not have any implicit size which the ColumnLayout can read from. That is, the Line's base is just an Item whose default implicitHeight and implicitWidth are both 0 - so the ColumnLayout renders them improperly. You have several options.
One option is to add implicitHeight/implicitWidth (or Layout.preferredWidth/Layout.preferredHeight) to your Line's Item:
import QtQuick 2.12
import QtQuick.Controls 2.12
Item {
implicitHeight: 30
implicitWidth: parent.width
property string tt2: "v"
Text{
id: txt
text: tt2
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
ComboBox{
id: cb
anchors.verticalCenter: parent.verticalCenter
//anchors.fill: parent
anchors.left: txt.right
}
}
A different way of accomplishing this would be to change the base of the Line to a RowLayout (and remove the interior anchors which would conflict):
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
RowLayout {
property string tt2: "v"
Text {
id: txt
text: tt2
Layout.preferredWidth: 200 // to align ComboBoxes, all text must have same width
// Layout.fillWidth: true // a different option for aligning ComboBoxes
}
ComboBox {
id: cb
}
}

QML - Using ListView with dynamically generated ListElement

I have this QML with the ListView:
import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.0
import smah.light 1.0
import smah.zone 1.0
Page {
id: page
property var lights: ({})
property var lightNames: ([])
title: "Device control"
ListView {
id: lightList
anchors.fill: parent
model: listModel
delegate:
Rectangle{
width: lightList.width
height: lightList.height / 8
}
}
ListModel {
id: listModel
dynamicRoles: true
Component.onCompleted: {
listModel.clear()
for (var i=0; i<lights.length; i++) {
var component = Qt.createComponent("LightControls.qml",listModel)
listModel.append(component.createObject(lightList, {light_name: lights[i].getName }))
}
}
}
}
The LightControls.qml is:
import QtQuick 2.0
import QtQuick.Controls 2.5
import smah.light 1.0
Rectangle {
id: rectangle
property int light_type: 0
property int light_id: 0
property var light_name: "_DEF"
width: parent.width
height: 50
color: "#040000"
Text {
id: txtName
color: "#fefdfd"
text: qsTr(light_name)
anchors.left: parent.left
anchors.leftMargin: 15
anchors.top: parent.top
anchors.topMargin: 8
font.pixelSize: 20
}
Slider {
id: slider
x: 179
y: 10
width: 302
height: 22
value: 0.5
}
Text {
id: txtValue
x: 517
width: 45
height: 15
color: "#ffffff"
text: qsTr("Text")
anchors.top: parent.top
anchors.topMargin: 8
font.pixelSize: 20
}
Button {
id: button
x: 694
y: 10
text: qsTr("Button")
}
}
I want a clean scrollable list with each of these items generated shown in it. I've looked at using a Repeater instead of the list, but I'll have more elements in the list than are desired on the screen. When running the program, everything is garbled into a single incoherent mess:
There are a few larger issues with your code:
You're trying to add a visual item to a ListModel, which expects ListElement objects. You can use append() to add rows to a ListModel.
Your root delegate item is a Rectangle, which doesn't manage the layout of its children. Use a RowLayout or Row instead.
Your delegate should be delegate: LightControls {}.

qt quick material style controls

I'm trying to design a qt quick application using qml. From what I gather in the documentation of qt is that these controls have support for styling automatically (e.g. there's a material light/material dark themes.
However, when I tried to apply it to the tabview control I can't get the styling to be applied. I can make a propery binding ala Rectangle { color: Material.color(Material.Red)} This works well for looking up any of the predefined colors, however I don't seem to be able to access attached (inherited) properties like Material.background
How can I best apply theming to controls that don't support it? It doesn't look like it's possible to property binding to an attached property- or at least qt/qml seems to get confused because Material. is also considered the namespace.
import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls.Material 2.1
ApplicationWindow {
id: mainWindow;
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Material.theme: Material.Dark
Material.accent: Material.Purple
TabView {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
tabPosition: Qt.BottomEdge
Material.theme: Material.Dark
Material.accent: Material.Purple
Tab {
title: "Red"
Material.theme: Material.Dark
Material.accent: Material.Purple
}
Tab {
title: "Blue"
Rectangle { color: "blue" }
}
Tab {
title: "Green"
Material.theme: Material.Dark
Material.accent: Material.Purple
}
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {
color: styleData.selected ? "steelblue" :"lightsteelblue"
border.color: "steelblue"
implicitWidth: Math.max(text.width + 4, 80)
implicitHeight: 20
radius: 2
Text {
id: text
anchors.centerIn: parent
text: styleData.title
color: styleData.selected ? "white" : "black"
}
}
frame: Item {
}
}
}
}
TabView is from Controls 1. You should use TabBar and TabButton, as the Material style is only available with Qt Quick Controls 2:
https://doc.qt.io/qt-5/qtquickcontrols2-differences.html#type-comparison-table

QT : render in release differs from debug

I'm having a strange issue with a C++ and QML app which render differently whereas i'm in debug or release :
The debug (left) render is the correct one.
In release (right) the accent colors is wrong and all the fonts are bigger. It's also seems to miss some shadows.
Both builds are done with visual 2015 after complete clean of the solution.
This how my window is set :
MainWindow::MainWindow(QMainWindow * parent) :QMainWindow(parent)
{
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
mQuickWidget = nullptr;
setAttribute(Qt::WA_DeleteOnClose, true);
this->setMinimumSize(640, 480);
mQuickWidget = new QQuickWidget(this);
QQuickStyle::setStyle("Material");
setCentralWidget(mQuickWidget);
this->setWindowTitle("Générateur de licence");
qmlRegisterType<mycompany::Licensor>("com.mycompany.licensor", 1, 0, "Licensor");
mQuickWidget->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
mQuickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
mQuickWidget->setAttribute(Qt::WA_AlwaysStackOnTop);
mQuickWidget->show();
}
And this part of the QML :
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.4 as Controls14
import QtQuick.Controls 2.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.1
import com.prynel.licensor 1.0
Item {
Material.theme: Material.Light
Material.accent: Material.DeepPurple
id: base
width : 900
height: 500
function twoDigit(n)
{
return n > 9 ? ""+n : "0"+n;
}
Licensor {
id: licensor
}
TabBar {
id: bar
width: parent.width
anchors.top: parent.top
anchors.left: parent.left
TabButton {
text: qsTr("1. Licence")
}
TabButton {
text: qsTr("2. Presets")
}
TabButton {
text: qsTr("3. Options")
}
TabButton {
text: qsTr("4. Finalisation")
}
}
StackLayout {
width: parent.width
anchors.bottom: parent.bottom
anchors.top: bar.bottom
currentIndex: bar.currentIndex
//LICENCE
Item {
id: licenceTab
Label {
id: labelacti
text: qsTr("Code d'activation :")
anchors.left: parent.left
anchors.leftMargin: 15
anchors.top: parent.top
anchors.topMargin: 35
}
TextField {
id: input_codeacti
anchors.left: parent.left
anchors.leftMargin: 230
anchors.verticalCenter: labelacti.verticalCenter
width: 201
antialiasing: true
placeholderText: qsTr("Code d'activation")
onTextChanged: {rect_result.visible = false;}
}
}
// Lot of other fields
}
Controls14.Calendar {
property var linkedItem
id: calendar
parent: base
visible: false
anchors.verticalCenter: base.verticalCenter
anchors.horizontalCenter: base.horizontalCenter
onClicked:
{
linkedItem.text = base.twoDigit(date.getDate()) + "/" + base.twoDigit((date.getMonth() + 1)) + "/" + base.twoDigit(date.getFullYear());
calendar.visible = false;
}
}
}
I'm not putting everything as there is lot of field which should not be the source of the problems.
What can cause this different result in the UI ?
Maybe are you providing a Qt Quick Controls special configuration file qtquickcontrols2.conf ?
The configuration file is usually embedded into the application's resources, but it can also be located in the directory specified by the environment variable QT_QUICK_CONTROLS_CONF. There are some more environment variables that can affect styles. Look to your QtCreator's project settings and check the environment differences between the Debug and the Release run settings.

How do I show a message box with Qt Quick Controls?

What is the equivalent of QMessageBox::information() when one wishes to write a QML application using Qt Quick Controls?
In Qt 6.3 and later you can use MessageDialog from QtQuick.Dialogs:
MessageDialog {
text: "The document has been modified."
informativeText: "Do you want to save your changes?"
buttons: MessageDialog.Ok | MessageDialog.Cancel
onAccepted: Qt.quit()
}
In Qt 6.2 and earlier you can use MessageDialog from Qt.labs.platform (using the same example code as above).
In Qt 5 you can use MessageDialog from QtQuick.Dialogs 1.x:
import QtQuick 2.2
import QtQuick.Dialogs 1.1
MessageDialog {
id: messageDialog
title: "May I have your attention please"
text: "It's so cool that you are using Qt Quick."
onAccepted: {
console.log("And of course you could only agree.")
Qt.quit()
}
Component.onCompleted: visible = true
}
You Can use Popup In QtQuick Controls 2 :
import QtQuick.Window 2.2
import QtQuick.Controls 2.0 // or import Qt.labs.controls 1.0
Window {
id: window
width: 400
height: 400
visible: true
Button {
text: "Open"
onClicked: popup.open()
}
Popup {
id: popup
x: 100
y: 100
width: 200
height: 300
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
}
}
Ok this does the job (badly). Import the Window object:
import QtQuick.Window 2.1
Then add this to your main window (or you could put it in another file I guess):
function showMessage(text, title)
{
messageBox.text = text;
messageBox.title = title;
messageBox.visible = true;
}
Window {
id: messageBox
modality: Qt.ApplicationModal
title: ""
visible: false
property alias text: messageBoxLabel.text
color: parent.color
minimumHeight: 100
minimumWidth: 300
Label {
anchors.margins: 10
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: messageBoxButton.top
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
id: messageBoxLabel
text: ""
}
Button {
anchors.margins: 10
id: messageBoxButton
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: "Ok"
onClicked: messageBox.visible = false
}
}
Problems with it:
The window can be shrunk so that the text and button overlap.
The minimum window size is hard-coded rather than calculated from the text size.
You can't select the text.
Looks a bit shit.
Unfortunately, there isn't one, at least not in the shipping Qt Quick Controls as of Qt 5.1.1 :(
You need to add it to your project via a QObject wrapper.
// CenteredDialog.qml
import QtQml 2.2
import QtQuick 2.9
import QtQuick.Controls 2.2
Dialog {
parent: ApplicationWindow.overlay
x: (parent.width - width) / 2
y: (parent.height - height) / 2
focus: true
modal: true
property alias text: messageText.text
Label {
id: messageText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.fill: parent
}
standardButtons: Dialog.Ok
}
You can declare CenteredDialog { id: centeredDialog } somewhere, then in some event handler you may call:
centeredDialog.title = qsTr("Error!")
centeredDialog.text = qsTr("Access violation")
centeredDialog.visible = true

Resources