QtQuick and KDE theming issues - qml

I've made some QtQuick programs, and I thought it was all working fine, till I tried them out on my laptop, which is running KDE, with the Breeze Dark theme.
A simple program with a label is barely readable:
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window
{
visible: true
Label
{
font.pointSize: 20
text: "Hello world!"
}
}
With KDE Breeze Dark:
With XFCE:
I have a customised combobox too, and it looks terrible under KDE. It has the same issue on the default KDE theme, just with the colours inverted:
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
import QtQuick 2.7
Window
{
visible: true
minimumWidth: 200
minimumHeight: 200
ComboBox
{
id: combo
model: ["Apple", "Banana", "Orange"]
width: 150
height: 50
delegate: ItemDelegate
{
id: item
width: parent.width
contentItem: Text
{
text: modelData
font.pointSize: 15
}
MouseArea
{
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onClicked: mouse.accepted = false
onPressed: mouse.accepted = false
onReleased: mouse.accepted = false
onDoubleClicked: mouse.accepted = false
onPositionChanged: mouse.accepted = false
onPressAndHold: mouse.accepted = false
onEntered:
{
item.highlighted = true
}
onExited:
{
item.highlighted = false
}
}
}
contentItem: Text
{
id: dropDown
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pointSize: 15
text: combo.displayText
}
}
}
With KDE Breeze Dark:
With XFCE:
So, I'm not sure if this is a KDE issue, or a QtQuick issue.
I guess I want to know how I can either disable KDE theming of certain programs, or modify my Qml to display my programs nicely when themed.
I couldn't find barely any literature on QtQuick theming, aside from some proposals or ways to manually implement it.

You can use SystemPalette. For example:
Window {
visible: true
color: palette.window
Label {
font.pointSize: 20
text: "Hello world!"
}
SystemPalette {
id: palette
colorGroup: SystemPalette.Active
}
}

Related

transferring the same images logic to another file

Is it possible to transfer the same code logic in another page, in which these two images are taken as alias or something similar and they behave in the same pattern of "if statement" logic on the other QML file(2) when this button is pressed from the first QML file(1)?
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Studio.Components 1.0
import QtQuick.Timeline 1.0
Item {
id: root
width: 500
height: 500
property alias locking: locking
Text {
id: confirmSign
text: qsTr("")
anchors.left: parent.left
anchors.top: parent.top
font.styleName: "Bold"
anchors.leftMargin: 70
anchors.topMargin: 55
color: "white"
font.pointSize: 10
font.family: "Tahoma"
}
RoundButton {
id: locking
y: 70
width: 90
height: 90
anchors.left: password_field.right
anchors.leftMargin: 15
Image {
id: unlck
anchors.centerIn: parent
source: "Images/Unlocked.png"
visible: false
}
Image {
id: lcked
anchors.centerIn: parent
source: "Images/Locked.png"
visible: true
}
onClicked: {
passBlocker.visible = true
confirmSign.text = qsTr("The ToolBar is 'Locked' ");
confirmSign.color = "#ffffff";
lcked.visible = true
unlck.visible = false
}
}
}
You can create a new file just for the lock image and use it wherever you want.
Something like this:
File LockImage.qml
Item {
property bool locked: false
Image {
id: lcked
anchors.centerIn: parent
source: locked? "Images/Locked.png": "Images/Unlocked.png"
visible: true
}
}
Side note: you don’t need to change properties using clicked handler. Prefer using the checked property of Button

Opacity change on button hovered glitch QML

I have created a custom virtual keyboard where the components are buttons, whose attributes I have defined in the code below . When I rapidly move the mouse across the buttons, I find that there is a 'flashing' effect whereby the button opacity appears to momentarily drop to 0 and then return. How can I prevent this flashing effect?
import QtQuick 2.15
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
import "definitions.js" as Defs
Button {
id: keyboardButton
property bool specialButton: false
contentItem: Text {
text: keyboardButton.text
font.family: Defs.font
font.pointSize: Defs.defaultTextSize
color: Defs.ivory
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
id: background
property double op: specialButton ? 0.7 : 0.5
color: Defs.b_blue
opacity: op
radius: 0
OpacityAnimator on opacity{
running: keyboardButton.hovered
from: background.opacity
to: background.op + 0.05
duration: 100
}
OpacityAnimator on opacity{
running: !keyboardButton.hovered
from: background.op + 0.05
to: background.op
duration: 100
}
}
}
Using one animator fixes it for me:
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
width: 640
height: 480
visible: true
Flow {
anchors.fill: parent
Repeater {
model: 100
Button {
id: keyboardButton
property bool specialButton: false
contentItem: Text {
text: keyboardButton.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
color: "steelblue"
opacity: hovered ? baseOpacity + 0.15 : baseOpacity
radius: 0
property double baseOpacity: keyboardButton.specialButton ? 0.7 : 0.5
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
}
}
}
}
}

Flickable scrollbar malfunctioning

I have created a TextArea inside a flickable, and when I add lots of newlines the area scrolls as expected. However, although a scrollbar appears when I type more lines than the available height, if I try to drag the scrollbar it resets to fully colored (nothing to drag) and just generally misbehaves.
What is wrong with my scrollbar?
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id: box
width: 640
height: 180
visible: true
title: qsTr("ScrollBar Mystery")
Flickable {
id: inputWrapper
anchors.fill: parent
ScrollBar.vertical: ScrollBar {
id: scrollBar
policy: ScrollBar.AlwaysOn
anchors.left: box.right
}
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
clip: true
flickableDirection: Flickable.VerticalFlick
function ensureVisible(r)
{
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
if (contentY >= r.y)
contentY = r.y;
else if (contentY+height <= r.y+r.height)
contentY = r.y+r.height-height;
}
TextEdit {
id: input
anchors.fill: parent
text: ""
focus: true
wrapMode: TextEdit.Wrap
onCursorRectangleChanged: inputWrapper.ensureVisible(cursorRectangle)
} // TextEdit
} // Flickable
} // Window
you forget to set contentHeight and contentWidth
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id: box
width: 640
height: 180
visible: true
title: qsTr("ScrollBar Mystery")
Flickable {
id: inputWrapper
anchors.fill: parent
contentHeight: input.implicitHeight
contentWidth: input.implicitWidth
ScrollBar.vertical: ScrollBar {
id: scrollBar
policy: ScrollBar.AlwaysOn
anchors.left: box.right
}
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
clip: true
flickableDirection: Flickable.VerticalFlick
function ensureVisible(r)
{
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
if (contentY >= r.y)
contentY = r.y;
else if (contentY+height <= r.y+r.height)
contentY = r.y+r.height-height;
}
TextEdit {
id: input
anchors.fill: parent
text: ""
focus: true
wrapMode: TextEdit.Wrap
onCursorRectangleChanged: inputWrapper.ensureVisible(cursorRectangle)
} // TextEdit
} // Flickable
} // Window

QML ProgressBar's vertical orientation does not fill to top at 100%

Inside QML MainWindow, I have Slider and ProgressBar:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
ApplicationWindow
{
visible: true
width: Screen.width/2
height: Screen.height/2
opacity: 1.00
color: "black"
RowLayout
{
anchors.fill: parent
Slider
{
id: ueSlider
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 8
Layout.alignment: Qt.AlignLeft|Qt.AlignVCenter
activeFocusOnPress: false
tickmarksEnabled: false
minimumValue: 0
maximumValue: 100
stepSize: 1
orientation: Qt.Vertical
onValueChanged:
{
ueProgressBar.value=value;
} // onValueChanged
} // Slider
ProgressBar
{
id: ueProgressBar
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 8
Layout.alignment: Qt.AlignRight|Qt.AlignVCenter
orientation: Qt.Vertical
indeterminate: false
onValueChanged:
{
print("ProgressBar value: "+value);
} // onValueChanged
Component.onCompleted:
{
minimumValue=ueSlider.minimumValue;
maximumValue=ueSlider.maximumValue;
value=ueSlider.value;
} // Component.onCompleted
} // ProgressBar
} // RowLayout
} // ApplicationWindow
Now, the ProgressBar is "connected" to Slider - when I change value of Slider, the ProgressBar's value changes. If I change ProgressBar's orientation to Qt.Horizontal, everything works fine, but if I change its orienation to Qt.Vertical, run example and push Slider to top (to its maximumValue), the ProgressBar does not go to 100%:
Why the hell?!

Reusable confirmation prompt in QML

I need a confirmation or alert dialog when user presses a button. Based on if they choose 'yes' or 'no', different actions are triggered. The challenge is that I have two buttons which pops such a dialog and it's not quite straightforward how to do that in QML. Here is the code (my demo application):
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
function areYouSure()
{
prompt.visible = true
}
MainForm {
anchors.fill: parent
Button {
id: buttonA
anchors.left: parent.left
anchors.top: parent.top
text: "Button A"
onClicked: areYouSure() // based on yes or no, different actions but how to tell what was pressed?
}
Button {
id: buttonB
anchors.right: parent.right
anchors.top: parent.top
text: "Button B"
onClicked: areYouSure() // based on yes or no, different actions but how to tell what was pressed?
}
}
Prompt {
anchors.fill: parent
id: prompt
visible: false
onCancelled: {
console.log("Cancel was pressed")
// but how can I tell which button's cancel as pressed?
}
onAccepted: {
console.log("Accept was pressed")
// same for which button's Ok?
}
}
}
Prompt.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Rectangle
{
id: root
width: parent.width
property string message: "Are you Sure?"
signal cancelled;
signal accepted;
Text{
id: messagetxt
text:root.message
horizontalAlignment: Text.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: cancelButton
anchors.bottom: parent.bottom
anchors.left: parent.left
width: 50
height: 40
Text {
anchors.centerIn: parent
text: "Cancel"
}
color: "red"
MouseArea {
anchors.fill: parent
onClicked: {
root.visible = false
cancelled()
}
}
}
Rectangle {
id: okButton
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 50
height: 40
Text {
anchors.centerIn: parent
text: "Ok"
}
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
root.visible = false
accepted()
}
}
}
}
In traditional programming, an individual dialog pops up which respond exactly to that question and than we respond to its cancelled() or accepted() signals. In QML we can't really do that, right? What is the best way to know which button was pressed? The irony is that even the right signals are emitted, we just can't seem to act on them.
Well, first and foremost you should really have a look at Dialogs module since it provides what would be a ready made solution for you, i.e. MessageDialog.
That said, you can achieve a customisation in different ways, including redefining the handlers or passing the ids. If the action to perform are simple (e.g. a function call) you can dynamically create even the dialog and bind the signals with the desired behaviour. Customisation can obviously go further, changing title and text.
Here is a simple example which follows the last approach and prints different texts depending on the pressed button. Once the dialog is set to not visible it is destroyed via the destroy function.
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.0
ApplicationWindow {
id: win
title: qsTr("MultiDialog")
visible: true
RowLayout {
anchors.fill: parent
Button {
text: "Button 1"
onClicked: {
var d1 = compDialog.createObject(win)
// change "title" & "text"?
d1.accepted.connect(function(){
console.info("accepted: " + text)
})
d1.rejected.connect(function(){
console.info("rejected: " + text)
})
d1.visible = true
}
}
Button {
text: "Button 2"
onClicked: {
var d2 = compDialog.createObject(win)
// change "title" & "text"?
d2.accepted.connect(function(){
console.info("accepted: " + text)
})
d2.rejected.connect(function(){
console.info("rejected: " + text)
})
d2.visible = true
}
}
}
Component {
id: compDialog
MessageDialog {
title: "May I have your attention please"
text: "It's so cool that you are using Qt Quick."
onVisibleChanged: if(!visible) destroy(1)
standardButtons: StandardButton.Cancel | StandardButton.Ok
}
}
}
If you want to use Rectangle or are forced to use it, then you can still use this approach. Dynamic creation of objects is NOT related to the usage of MessageDialog and can be used (and should be used) to reduce the number of objects kept instanced throughout application lifetime. Have a look here for more details about that.
The following example uses the very same dialog component you defined (with some small modifications. As you can see the code is almost identical. I've just moved the destruction of the object at the end of the signal handlers. In this case I've also changed the value of the unique property defined in the component, i.e. message, to show you complete customization.
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Window {
id: win
visible: true
RowLayout {
anchors.fill: parent
Button {
text: "Button 1"
Layout.alignment: Qt.AlignCenter
onClicked: {
var d1 = prompt.createObject(win)
d1.message = text + " - Are you Sure?"
d1.accepted.connect(function(){
console.info("accepted: " + text)
d1.destroy()
})
d1.rejected.connect(function(){
console.info("rejected: " + text)
d1.destroy()
})
}
}
Button {
text: "Button 2"
Layout.alignment: Qt.AlignCenter
onClicked: {
var d2 = prompt.createObject(win)
d2.message = text + " - Are you Sure?"
d2.accepted.connect(function(){
console.info("accepted: " + text)
d2.destroy()
})
d2.rejected.connect(function(){
console.info("rejected: " + text)
d2.destroy()
})
}
}
}
Component {
id: prompt
Rectangle {
id: root
anchors.fill: parent
property string message: "Are you Sure?"
signal rejected()
signal accepted()
Text{
id: messagetxt
text:root.message
horizontalAlignment: Text.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: cancelButton
anchors.bottom: parent.bottom
anchors.left: parent.left
width: 50
height: 40
Text {
anchors.centerIn: parent
text: "Cancel"
}
color: "red"
MouseArea {
anchors.fill: parent
onClicked: rejected()
}
}
Rectangle {
id: okButton
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 50
height: 40
Text {
anchors.centerIn: parent
text: "Ok"
}
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: accepted()
}
}
}
}
}
If your component is not inlined as I did with Component but it's kept in another file you can use createComponent as depicted in the link provided above. The code of your main window would look like this:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Window {
id: win
visible: true
property var prompt
RowLayout {
anchors.fill: parent
Button {
text: "Button 1"
Layout.alignment: Qt.AlignCenter
onClicked: {
var d1 = prompt.createObject(win)
d1.message = text + " - Are you Sure?"
d1.accepted.connect(function(){
console.info("accepted: " + text)
d1.destroy()
})
d1.rejected.connect(function(){
console.info("rejected: " + text)
d1.destroy()
})
}
}
Button {
text: "Button 2"
Layout.alignment: Qt.AlignCenter
onClicked: {
var d2 = prompt.createObject(win)
d2.message = text + " - Are you Sure?"
d2.accepted.connect(function(){
console.info("accepted: " + text)
d2.destroy()
})
d2.rejected.connect(function(){
console.info("rejected: " + text)
d2.destroy()
})
}
}
}
Component.onCompleted: prompt = Qt.createComponent("Prompt.qml");
}
You should always check that component creation is correcly carried out (I didn't do it for the sake of brevity). That said, the code is identical to the previous one.
Last but not least, I've noticed an error in your code: signals must always be declared with parenthesis, even when no parameter is emitted. It should be signal accepted(), not signal accepted, same goes for the other signal and any other signal declaration.