Using variable as text in Text element immediately activates onTextChanged - qt

If I have the following code:
import QtQuick 2.10
import QtQuick.Window 2.10
Window {
id: app
visible: true
width: 640
height: 480
property bool txt: false
Text {
text: app.txt
onTextChanged: { console.debug("Text changed") }
}
}
I get “Text changed” displayed in my console as soon as the app loads, however if I set the text manually to something like
Text {
text: "Some text"
onTextChanged: { console.debug("Text changed") }
}
I don’t get the “Text changed” display in my console unless I actually have something that changes the text after the app loads.
Is this normal behaviour? Is there a way to use the variable as the text but not have onTextChanged activate as soon as the app loads?

Yes, it's normal behaviour for qml, becuase your first text property is "" (nothing), and when you give it a variety it changes from "" to app.txt. If you set manually text, in this case, there is nothing to change.

Related

TextArea, process each character one at a time

I have a text area and I want to listen to every characters inputted on it,
What I am trying to do is when a spacebar is pressed, I will perform an operation or processing such as replacing some characters on it.
I have tried and listen to onTextChanged however, it may cause recursion as setting my TextArea.text to new value triggers a call to onTextChange event thus causing Stack Overflow.
I have tried to listen to Keys.onPressed event as well and listen to space key but the space key is processed as well and when I set a new text value, the space value is forcedly inserted.
How and where should I handled and listen to these events?
How can I listen to each inputted characters in QML text area, catch the space bar, process, and replace the text with the new processed text? Also ignore the last spacebar entry?
How do I reject a chracter when inputted in the TextEdit or TextArea?
Can you use TextInput which helps you differ whether text is changed by user or programmatically ?
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TextInput
{
width: 200
height: 100
onTextEdited:
{
if( text[text.length-1] === " " )
{
text = process(text)
}
}
anchors.centerIn: parent
}
function process(textinput)
{
return textinput.toUpperCase()
}
}

Qt5-QML: How to handle change of color and text of a button after click event

I am writing a small application that is working as follows:
1) I launch the application and I select a robot to which I will connect. See print screen below of the small app:
2) That will lead me to another page where I can actually choose the robot to connect to as shown in the print screen below:
3) Finally after selecting the robot the application brings me back to the initial screen that will show me an additional Button showing the chosen robot.
The problem: I have is that after I choose the robot and I am back to the initial screen and I push the button the color of the button should turn into a (for example) green color and changing the text into (for example) Connecting...
The code I am using is the following for which I am only putting the related part:
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Styles 1.4
Page {
property int dialogId: -1
signal selectDialog()
ColumnLayout {
anchors.fill: parent
spacing: 5
Button {
id: button1
text: "Select Robot"
onClicked: selectDialog()
Layout.fillWidth: true
font.pointSize: 20
}
Button {
id: dialogA
text: "FreddieMercury: Connect";
visible: dialogId === 1
Layout.fillWidth: true
font.pointSize: 20
function buttonClick()
{
console.log("Button "+ dialogA.text +" is clicked!")
}
Rectangle {
id: button
color: "red"
width: 96; height: 24; anchors.centerIn: parent
MouseArea {
id: region
anchors.fill: parent;
onClicked: console.log("clicked()")
onPressed: dialogA.color = "green"
onReleased: dialogA.color = "red"
}
Text {
id: st_text
anchors.centerIn: parent
text: "Connecting..."
font.bold: true
font.pointSize: 20
color: "green"
}
}
}
// Other Buttons
}
}
What I tried so far
I went through this source and also this post which I followed. As you can see from the point 3) I am close to the good functioning but there is clearly something I am not doing right.
Also this was useful and in fact I used the MouseArea option exactly from that post.
However I still don't see the whole color extended into the button.
Finally the text changed after the click event happened I included it in the Button as shown and thought that the property text: "Connecting..." was enough to overwrite the existing text but without success.
Please advise on what I am missing that is keeping me from a full working example.
I think the base issue is that you're trying to use examples for QtQuick Controls 1 with QtQuick Controls 2. They're completely different animals and you cannot style the v2 controls using QtQuick.Controls.Styles.
For customizing Controls 2 styles, like Button, see here. I also find it useful to look at the source code for the included controls (they're in your Qt library install folder inside /qml/QtQuick/Controls2/ directory). Though personally I find needing to re-create a whole new Button (or whatever) just to change a color or font is a bit much, especially if I want it to work across all the included QtQuick Controls2 Styles.
An alternative is to "hack" the properties of the built-in Control styles. This certainly has some drawbacks like if you want to be able to reset the control style back to default bindings, you'd have to save the original bindings and re-create them to reset the style. OTOH it beats creating customized controls for each style. YMMV.
Anyway here's an example of what i think you're looking for. This is based on our previous exercise with the buttons. :) Specifically, I just modified the Page1.qml code and the other 2 files are exactly the same as before. In this page I added buttonClick() handler and the Button::onClicked calls to trigger it from each button (and the button texts of course :).
Page1.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12 // for IconLabel
import QtQuick.Layouts 1.12
Page {
property int dialogId: -1;
signal selectDialog()
function buttonClick(button)
{
button.text = qsTr("Connecting to %1...").arg(button.text);
button.enabled = false; // prevent repeat clicks
// If Button has a background Rectangle object then we can set properties on it.
// note: `instanceof` was added in Qt 5.10
if (button.background && button.background instanceof Rectangle) {
button.background.color = "red"; // override style color
button.background.gradient = null; // some styles use a gradient
button.background.visible = true; // some styles may hide it in some situations
}
// Similar with the label element, IconLabel is used by all included QML styles.
if (button.contentItem && button.contentItem instanceof IconLabel) {
button.contentItem.color = "blue"; // override style color
button.contentItem.font.bold = true;
button.contentItem.font.pointSize = 20;
}
}
ColumnLayout {
anchors.fill: parent
spacing: 5
Button {
id: button1
text: "Select"
onClicked: selectDialog()
Layout.fillWidth: true
}
// These buttons should appear only after the user selects the choices on `Page2`
Button {
id: dialogA
text: "Freddie Mercury"
visible: dialogId === 1
Layout.fillWidth: true
onClicked: buttonClick(this)
}
Button {
id: dialogB
text: "David Gilmour"
visible: dialogId === 2
Layout.fillWidth: true
onClicked: buttonClick(this)
}
Button {
id: dialogC
text: "Mick Jagger"
visible: dialogId === 3
Layout.fillWidth: true
onClicked: buttonClick(this)
}
}
}
If you had a customized Button (like in the Qt docs example) then you could still do basically the same thing in buttonClick() but probably w/out worrying about the if (button.background ...) stuff (since you'd be sure your button has valid background/contentItem Items).
A better implementation of a "default" (Style-specific) Button but with custom colors/text properties would involve a subclass which uses Binding and/or Connections QML elements to control the properties and be able to reset them back to the current QtQuick Style defaults.

Text Selection in QtQuick

I am writing a dialog box for a software plugin for Cura, a 3D printing slicer. When the user slices their file it brings up a dialog box to name the file before uploading it to the 3D printer. A python script generates the name in the format "print name - material-otherinformation.gcode" Right now, when the dialog loads, it highlights the whole text field except for the .gcode extension at the end. I would like it to only highlight a portion of that text field by default, namely the print name part. It is easy for me to return the length of that section as an integer and pass it to the QML file. I am definitely an amateur with QML, but it seems like the select function should be able to handle that but I can't figure out the usage. Any assistance or pointers would be much appreciated!
Here's a simplified version of the code. What I would like to do is add something to this so that just "word 1" is highlighted when the dialog box appears.
import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.1
import UM 1.1 as UM
UM.Dialog
{
id: base;
minimumWidth: screenScaleFactor * 400
minimumHeight: screenScaleFactor * 120
Column {
anchors.fill: parent;
TextField {
objectName: "nameField";
id: nameField;
width: parent.width;
text: "word1 - word2 - word3.gcode";
maximumLength: 100;
}
}
}
It's just a matter of when to use the TextInput::select() method. This could be in Component.onCompleted: of either the dialog or the text field, for example:
UM.Dialog
{
...
property int selectionLength: 0
Component.onCompleted: nameField.select(0, selectionLength);
...
TextField {
id: nameField;
text: "word1 - word2 - word3.gcode";
}
...
}
If selectionLength could change after dialog is created, then I'd create a separate function which can be called from different events or even directly:
UM.Dialog
{
...
property int selectionLength: 0
Component.onCompleted: select(selectionLength);
onSelectionLengthChanged: select(selectionLength);
function select(len) { nameField.select(0, len); }
...
TextField {
id: nameField;
text: "word1 - word2 - word3.gcode";
}
...
}
Obviously if the selection isn't going to be from the first character then some adjustment to this strategy will be needed.

QML MenuItem trigger by Enter and keyboard events forwarding don't work

Here I have a minimal example:
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Component.onCompleted: {
menu.open();
}
Menu {
id: menu
Keys.onPressed: {
console.log("pressed")
}
MenuItem {
text: "test"
Keys.forwardTo: [menu]
focus: highlighted
onTriggered: {
console.log("triggered 1")
}
}
MenuItem {
text: "test2"
focus: highlighted
Keys.forwardTo: [menu]
onTriggered: {
console.log("triggered 2")
}
}
MenuItem {
text: "test3"
focus: highlighted
Keys.forwardTo: [menu]
onTriggered: {
console.log("triggered 3")
}
}
}
}
Navigating through Menu Items with keyboard works as expected. But triggering with Enter key doesn't work. Also as a workaround I tried to forward key events to the parent menu item, and it didn't work as well.
The only way to fix it I see right now: place Keys.onReturnPressed to every MenuItem and make it call triggered() signal. But it feels wrong.
Is it a bug of Qt?
What is a right workaround for it?
I'm using Qt 5.10.1
The Menu is not Item, so it could not set into Keys.forwardTo.
The navigation of Menu via the Forward/Backward key is enabled by default, Keys.forwardTo is not necessary.
Why Enter key does not work because to select the MenuItem is using Space key not the Enter key. [↑] and [↓] is for Navigation(focus) and [Space] is for Selection
I think if you want to listen the [Enter] key for the Selection, add Keys.onReturnPressed: triggered(); is the right way.
Update:
The [Enter] key was worked in Qt 5.6 (verified). check the source code. But looks like it is removed in the current version.

QML - Dynamically swap the visibility/opacity between overlapping Text and TextArea

I want to have a widget in QML which has combination of the following behaviors:
1) Multi line edit
2) Auto scroll the content as and when I hit newline. (The content on top of the page keeps going up as and when I enter new content at the bottom)
3) Have a placeholder text functionality.
As far as I know, only Text and TextField are having placeholder text property and only TextArea is having a multi line edit plus auto scroll. But If there is any such widget with all the combinations then, my question “Dynamically swap the visibility/opacity between overlapping Text and TextArea “ would be invalid.
In case there is no such widget (I wonder why), I am thinking to have a rectangle which has both Text and TextArea overlapping and based on the below logic I want to have the visibility/opacity/focus on one of them:
If the Text Area is empty (0 characters), then have the Text in the foreground with focus and with the placeholder text “Enter some text”. But as soon as the user starts typing, the Text should lose the focus, opacity and go to background and the TextArea should gain the focus and come to the foreground and start accepting multi line input. Similarly, when TextArea is in the foreground and is empty (0 characters) and when the user click on any other widget outside my Rectangle, the Text should again gain the focus, come to the foreground and display the placeholder again.
I tried to get inspiration from this code, but failed miserably, it would be helpful if anyone can help me with a few lines of code or give me some pointers on how to solve this.
You can implement placeholderText for TextArea the same way Qt does in TextField. The source can be found here: TextField.qml
When you remove all the comments and properties, you basically have a background and on top of that a MouseArea, the placeholderText Text and a TextInput. Since you need to have the placeholder visually below the TextArea, you must have a transparent background:
PlaceholderTextArea.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
Rectangle {
property alias placeholderText: placeholder.text
id: background
width: 640
height: 480
color: "#c0c0c0"
Text {
id: placeholder
anchors.fill: parent
renderType: Text.NativeRendering
opacity: !textArea.text.length && !textArea.activeFocus ? 1 : 0
}
TextArea {
id: textArea
anchors.fill: parent
backgroundVisible: false
}
}
and use your component:
PlaceholderTextArea {
placeholderText: qsTr("Hello World")
anchors.fill: parent
}
Here's an alternative implementation, that works a bit better for me:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Item
{
property alias placeholderText: placeholder.text
property bool __shouldShowPlaceholderText:
!textArea.text.length && !textArea.activeFocus
// This only exists to get at the default TextFieldStyle.placeholderTextColor
// ...maybe there is a better way?
TextField
{
visible: false
style: TextFieldStyle
{
Component.onCompleted: placeholder.textColor = placeholderTextColor
}
}
TextArea
{
id: placeholder
anchors.fill: parent
visible: __shouldShowPlaceholderText
activeFocusOnTab: false
}
TextArea
{
id: textArea
anchors.fill: parent
backgroundVisible: !__shouldShowPlaceholderText
}
}

Resources