I want to programmatically read a string generated in runtime by a basic Qt GUI application.
The string appears on the screen but I don't have the source and I want to pass this string to another script.
Here's the relevant .qml file:
import QtQuick 1.0
Rectangle {
id: tagCloud
SystemPalette { id: palette } //we get the system default colors from this
//public API
property variant model
property color baseColor: palette.base
property color textColor: palette.text
property int textFontSize: 16
color: baseColor
Flow {
id: flow
width: parent.width
spacing: 15
anchors.margins: 4
anchors.verticalCenter: parent.verticalCenter
//property int maxHeight:0
Repeater {
id: repeater
model: tagCloud.model
Text {
id: textBlock
text: category
font.pointSize: tagCloud.textFontSize;
}
}
}
}
Is there an easy way to get the "category" string as it's being generated?
Edit: This is the link to the application,
http://cybertron.cg.tu-berlin.de/eitz/projects/classifysketch/sketchpad_win.zip
Related
how can i get gridView.itemAtIndex(index).color?
what is i have tried:
contentrepeater.itemAt(5).gridView.model.color;
contentrepeater.itemAt(5).gridView.itemAtIndex(5).color;
But it doesn't work
Rectangle {
anchors.top: bar.bottom
Layout.fillHeight: true
Layout.fillWidth: true
Repeater {
id: contentrepeater
model: 11
Rectangle {
anchors.fill: parent
color: 'red'
visible: false
GridView {
id: gridView
anchors.fill: parent
anchors.topMargin: 10
anchors.leftMargin: 10
cellWidth: 150
cellHeight: 170
clip: true
model: 11
delegate: Rectangle{
height: 160
width: 140
color: '#333333'
}
}
}
}
}
Ultimately you probably don't want to do it that way. It will be hackish and error-prone. For example GridView only provides item access based on position coordinates, not indexes. So you'd need to dive into its children which are going to be created dynamically... it's possible but very messy and not really supported API.
You are better off defining your item models first, then using the GridView (or whatever) to display them. That way you can manipulate objects in the models and changes will be reflected in the view (instead of the other way around like you're trying now).
This example (based on your posted code) creates 4 layouts with 11 squares each and animates the color in each square using a timed script. Note that we need separate instances of the models for each of the GridViews within contentrepeater (otherwise it is only shown in the last view). So the example is a bit more convoluted since the item models are being created dynamically.
I should add that in a "real" application I'd use a different method of tracking the created item models instead of looking them up in the display hierarchy like I have it here. The main point this is trying to demonstrate is to manipulate the displayed items (delegates) via changes to the model data.
import QtQuick 2.9
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import QtQml.Models 2.3
Pane {
id: root
width: 400
height: 650
padding: 9
// rectangle items to create per model
property int itemsPerModel: 11
// prototype object model
property Component itemModel: ObjectModel {}
// prototype model item
property Component delegate: Rectangle {
height: 30
width: 30
color: '#333333'
}
// Creates a new ObjectModel with some Rectangle items as children
function newItemModel() {
var model = itemModel.createObject(root);
for (var i=0; i < itemsPerModel; ++i)
model.append(delegate.createObject(root));
return model;
}
SequentialAnimation {
id: animate
running: true
loops: Animation.Infinite
ScriptAction {
property string nextColor: "blue"
property int nextSet: 0
property int nextItem: 0
script: {
contentrepeater.itemAt(nextSet) // the Rectangle within the GridLayout
.children[0] // the gridView within the Rectangle
.model.get(nextItem) // the model's delegate item (a Rectangle)
.color = nextColor; // set the new color on it.
// advance to next item or set of items.
nextItem = (nextItem+1) % root.itemsPerModel;
if (!nextItem)
nextSet = (nextSet+1) % contentrepeater.count;
nextColor = (nextColor === "blue" ? "orange" : nextColor === "orange" ? "white" : "blue");
}
}
PauseAnimation { duration: 100 }
}
GridLayout {
columns: 2
anchors.fill: parent
Repeater {
id: contentrepeater
model: 4
Rectangle {
color: 'red'
width: 150
height: 170
GridView {
id: gridView
anchors.fill: parent
anchors.topMargin: 10
anchors.leftMargin: 10
cellWidth: 40
cellHeight: 40
clip: true
// here we need a unique instance of the ObjectModel
model: root.newItemModel()
}
}
}
}
}
I'm creating a custom text input in QML. One of its configuration is that it's a field that should only accept digits I did it like this:
import QtQuick 2.6
Item {
property string vmFont: "Mono"
property string vmPlaceHolder: "Some text ..."
property bool vmNumbersOnly: false
// Qt Quick approach to make internal variables.
Item {
id: own
property string enteredText: ""
}
Rectangle {
id: lineEditRect
anchors.fill: parent
color: "#e4f1fd"
radius: 2
}
TextInput {
id: lineEdit
text: vmPlaceHolder
color: "#5499d5"
font.family: vmFont
font.pixelSize: 13
anchors.bottom: parent.bottom
//inputMethodHints: vmNumbersOnly ? Qt.ImhDigitsOnly : Qt.ImhNone
inputMethodHints: Qt.ImhDigitsOnly
verticalAlignment: TextInput.AlignVCenter
leftPadding: 10
width: lineEditRect.width
height: lineEditRect.height
onActiveFocusChanged: {
if (activeFocus){
if (own.enteredText === ""){
// Removing the placeholder
lineEdit.text = "";
}
}
}
onEditingFinished: {
own.enteredText = lineEdit.text;
if (lineEdit.text === ""){
lineEdit.text = vmPlaceHolder
}
}
}
}
However, even though that the inputMethodHits is set to Qt.ImhDigitsOnly, the text input still accepts all kinds of keypresses. What am I doing wrong?
I think that inputMethodHints is for virtual keyboards (e.g. mobile phone software keyboards, Qt Virtual Keyboard, etc.). For restricting input when a physical keyboard is in use (though it can also be used when a virtual keyboard is in use), you can use inputMask and validator. For example, the following code would allow only four digits from 0 to 9 to be entered:
TextInput {
inputMask: "9999"
}
Think of inputMethodHints as affecting what the virtual keyboard displays and how it behaves, and these properties as affecting what the TextInput itself allows as input.
it worked for me in any device:
TextInput {
validator: RegExpValidator{regExp: /[0-9]+/}
}
I want to create my custom combo box. So I went and desinged a simple item that is a rectancle with a mouse area and a text. My idea is to draw multiple of these according to the values in an string array. Here is my code so far for the item:
Item {
signal itemSelected(int id);
property int vmItemIndex: 0;
property alias itemBody: ibody;
Rectangle{
id: ibody;
property alias itemMouseArea: imouseArea;
property alias itemText: itext;
Text{
id: itext
anchors.centerIn: parent
}
MouseArea {
id: imouseArea
anchors.fill: parent
onClicked: itemSelected(vmItemIndex);
}
}
}
the acutual combo box
Item {
property int vmWidth: 300;
property int vmHeight: 50;
property int vmCurrentIndex: 0;
property var vmItemTexts: ["Item 1", "Item 2", "Item 3"];
property string vmBackColor: "#ff0000";
VMComboBoxItem {
id: main;
itemBody.itemText.text: vmItemTexts[vmCurrentIndex];
itemBody.width: vmWidth
itemBody.height: vmHeight
itemBody.color: vmBackColor
}
}
Hoewever the engine load fails telling me it cannot access itemText. So what am I doing wrong?
I think the problem is that in your VMComboBoxItem component, you expose the inner Rectangle as a property. There's some hint about this in the Qt documentation.
What you can do instead is to expose selected properties directly in your "root" item in VMComboBoxItem:
import QtQuick 2.0
Item {
signal itemSelected(int id);
property int vmItemIndex: 0;
property alias itemBody: ibody;
// Expose the "text" property directly:
property alias text: itext.text
Rectangle{
id: ibody;
property alias itemMouseArea: imouseArea;
property alias itemText: itext;
Text{
id: itext
anchors.centerIn: parent
}
MouseArea {
id: imouseArea
anchors.fill: parent
onClicked: itemSelected(vmItemIndex);
}
}
}
And then set this property in QML code using your component:
import QtQuick 2.0
Item {
property int vmWidth: 300;
property int vmHeight: 50;
property int vmCurrentIndex: 0;
property var vmItemTexts: ["Item 1", "Item 2", "Item 3"];
property string vmBackColor: "#ff0000";
VMComboBoxItem {
id: main;
itemBody.width: vmWidth
itemBody.height: vmHeight
itemBody.color: vmBackColor
// Set Text property directly:
text: vmItemTexts[vmCurrentIndex];
}
}
It might generally be a good idea to expose only selected properties in your components, as this narrows the interface your component exposes (which in turn makes it much easier should you ever need to refactor your code).
I want to create a QML binding in a repeated component. I want to bind the value of one of the elements to a property from a known object. My problem is that the name of said property that I want to bind to will be provided as a string in the component. How can I resolve the property name to an actual property that can be used as the value in a binding?
PS. If possible I guess I could pass the property directly to the repeater but then I would like to be able to convert the property to a string because I need both and don't want to pass both.
EDIT:
Here's what I want:
ListModel {
id: settingsModel
ListElement { title: "Bed Width"; setting: "bedWidth"; }
ListElement { title: "Bed Length"; setting: "bedLength"; }
}
Component {
id: settingsDelegate
Item {
width: parent.width
height: childrenRect.height
Label {
id: setLabel
text: title + ":"
width: parent.width
}
TextBox {
id: setTBox
anchors.top: setLabel.bottom
anchors.topMargin: 5
width: parent.width
Binding on text {
when: !setTBox.isActive
value: settings.setting
}
Binding {
target: settings
property: setting
value: setTBox.text
}
}
}
}
Column {
id: settingsColumn
spacing: 10
anchors.left: parent.left
anchors.right: parent.right
anchors.top: txtSave.bottom
anchors.topMargin: 15
Repeater {
model: settingsModel
delegate: settingsDelegate
}
}
My problem is that the name of said property that I want to bind to
will be provided as a string in the component. How can I resolve the
property name to an actual property that can be used as the value in a
binding?
If you look at the documentation for Binding you will discover that the property property expects a string - property : string
So you don't have anything to resolve, that happens internally.
my problem is the "value: settings.setting" line
You could try something like settings[setting]
I have rather strange scenario whereby if I launch a subwindow that contains a ListView with a moderately complex delegate and enough items to comfortably exceed the visible area, the entire subwindow will immediately close on launch.
Reducing the complexity of the delegate will allow the window to open, but then rapidly scrolling the ListView will forcibly close it.
This SSCCE triggers the effect on my laptop, but on a more powerful machine it may only do it whilst scrolling (or perhaps the delegate may need to be more complex):
import QtQuick 2.3
import QtQuick.Window 2.0
Window {
width: 300
height: 200
Component.onCompleted: {
win.createObject( null );
}
Component {
id: win
Window {
width: 600
height: 400
visible: true
ListView {
id: view
anchors.fill: parent
model: 100
boundsBehavior: Flickable.StopAtBounds
clip: true
delegate: Rectangle {
width: view.width
height: 24
property int debugLevel: index % 3
property int timestamp: index * 1000
property int message: index
color: "darkgray"
Row {
anchors.fill: parent
Repeater {
id: delegateRepeater
property list< QtObject > roleModel: [
QtObject {
property string label: timestamp
property int itemWidth: 100
},
QtObject {
property string label: debugLevel
property int itemWidth: 100
},
QtObject {
property string label: message
property int itemWidth: view.width - 100 - 100
}
]
model: roleModel
Item {
width: itemWidth
anchors {
top: parent.top
bottom: parent.bottom
}
Text {
anchors {
fill: parent
leftMargin: 4
}
verticalAlignment: Text.AlignVCenter
text: label
elide: Text.ElideRight
}
Rectangle {
anchors {
top: parent.top
bottom: parent.bottom
right: parent.right
}
width: 1
visible: index != ( delegateRepeater.count - 1 )
color: "white";
}
}
}
}
}
}
}
}
}
There doesn't seem to be any particular part of the code that is causing the problem, removing any of the objects in the delegate reduces the probability of the subwindow closing.
I've added the debugging tag because my main problem is that this effect produces no debug output. If I add a breakpoint into the subwindow's destruction handler (Component.onDestruction) then there is a single stack entry pointing at the model: roleModel statement - but removing the entire Repeater and replacing with a copy-and-pasted equivalent yields the same results minus the stack entry.
So I would be grateful is anyone knows of a way of getting more information from this pure QML example.
As noted by #BaCaRoZzo the changing of behaviour by modifying the delegate code seems to be an unrelated side-issue.
The real cause is because it turns out you cannot create new root contexts (i.e. top-level windows) from QML. This was hinted at being resolved when Qt Quick Components were released, but the blog post boasting of Window doesn't explicitly state this. Creating a new Window and passing null for the parent technically works but the result seems to be very unstable.
Thankfully in my circumstance I'm creating a QML/C++ application so I've solved the issue by creating new root contexts from Q_INVOKABLE methods on the C++ side. But if you're developing a pure QML application, it seems that you are out of luck.