I am using PlasmaCore.DataSource executable for execution some commands, but the var stdout is always undefined.Below is the code I wrote:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 1.4
import Qt.labs.folderlistmodel 2.15
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.kcoreaddons 1.0 as KCoreAddons // kuser
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
ApplicationWindow {
id: appWindow
width: 300
height: 800
KCoreAddons.KUser {
id: kuser
}
ListView {
id:listview
anchors.fill:parent
FolderListModel {
id: folderModel
showDirs:true
showHidden:true
sortField:FolderListModel.Time
folder:"file:///home/"+kuser.loginName+"/.local/share/RecentDocuments"
}
clip : true
interactive: true
boundsBehavior:Flickable.StopAtBounds
model: folderModel
delegate:Item{
id:modelItem
property var basename:label
width:parent.width
height:25
visible:true
Label {
id:label
width:parent.width
height:label.text!==""?parent.height:-25
elide :Text.ElideRight
text:{
var textFileArray = new Array()
var file
var filename
for(var j=0; j<folderModel.count;++j){
file=folderModel.get(j,"fileName");
filename=file.replace('.lock','').replace('.desktop', '').replace('[1]','').replace('[2]','').replace('[3]','').replace('[4]','').replace('[5]','').replace('[6]','').replace('[7]','').replace('[8]','').replace('[9]','').replace('[10]','').replace('[11]','').replace('[12]','').replace('[13]','').replace('[14]','').replace('[15]','').replace('[16]','').replace('[17]','').replace('[18]','').replace('[19]','').replace('[20]','').replace('[21]','').replace('[22]','').replace('[23]','').replace('[24]','').replace('[25]','').replace('[26]','').replace('[27]','').replace('[28]','').replace('[29]','').replace('[30]','')
textFileArray.push(filename)
}
var newtextFile=[... new Set(textFileArray)]
//console.log(newtextFile[index])
if(newtextFile[index])
{newtextFile[index]}else
{""}
}
MouseArea {
id:mouseArea
anchors.fill: parent
onClicked: {
modelItem.ListView.view.currentIndex=index
commandTimer.running=true
//console.log(label.text ,outputText,command)
}
}
}
}
Component.onCompleted:{
forceLayout();
}
}
PlasmaCore.DataSource {
id: executable
engine: "executable"
connectedSources: []
onNewData: {
var exitCode = data["exit code"]
var exitStatus = data["exit status"]
var stdout = data["stdout"]
var stderr = data["stderr"]
exited(sourceName, exitCode, exitStatus, stdout, stderr)
disconnectSource(sourceName) // cmd finished
}
function exec(cmd) {
if(cmd){
connectSource(cmd)
}
}
signal exited(string cmd, int exitCode, int exitStatus, string stdout, string stderr)
}
property string outputText:''
property string command:"[[ -f /home/" + kuser.loginName + "/.local/share/RecentDocuments/"+listview.currentItem.basename.text+".desktop ]] && echo 'true'"
Connections {
target:executable
onExited:{
var formattedText = stdout
outputText = formattedText
}
}
function runCommand() {
executable.exec(command)
}
Timer{
id:commandTimer
interval:10
running:false
onTriggered:{
runCommand()
if(outputText==="true"){ Qt.openUrlExternally( "/home/" + kuser.loginName + "/.local/share/RecentDocuments/"+listview.currentItem.basename.text+".desktop")}else{}
console.log(listview.currentItem.basename.text ,outputText,command)
}
}
}
The application is written in qml for kde neon linux.
I tried moving either DataSource or Connections part inside and outside ListView to expose the variable but i didn't manage anything.Maybe the solution is simple but I can not find it .
Any help will be appreciated.
Related
I am trying to make a change to all items of a GridView.
I have tried to iterate through either the model or the grid, I looked at similar examples on the web, but everything I try ends with Cannot read property 'buttonText' of undefined.
It seems to me that the problem is that the interpreter can't figure out that the item from the grid or model is a Button. But I don't know how to cast it.
If I change the log to only display the item, not any property, (see code snippet), it seems that it knows it is an item... see my experiments below.
The only thing I can make work is set a property (or call a signal, or a function) from the delegate. But that only affects one grid item, not all.
How can I set a property on every item of the grid ? Alternatively, how can I send a signal, or call a function, on every item?
My experiments are in function changeEverythingFunction()
file: Button.qml
Item
{
id: itemButton
signal changeEverything
property int buttonIndex
property string buttonText
...
}
file: Model.qml
Item
{
id: modelItem
ListModel
{
id: listModel
}
property int buttonCount: listModel.count
function changeEverythingFunction()
{
// for (var i = 0; i < buttonCount; i++)
// listModel.setProperty(i, buttonText, "abc")
for(var childIndex in gridItems.contentItem.children)
{
console.log(listModel.get(childIndex).buttonText) // Cannot read property 'buttonText' of undefined
console.log(gridItems.contentItem.children[childIndex].buttonText) // Cannot read property 'buttonText' of undefined
console.log(gridItems.contentItem.children[childIndex]["buttonText"]) // undefined (I saw this in a SO example)
var item = gridItems.contentItem.children[childIndex]
console.log(item) // qml: QQuickItem(0xe496370)
}
}
MouseArea
{
....
Rectangle
{
...
GridView
{
id: gridItems
anchors.fill: parent
clip: true
model: listModel
delegate: Item
{
id: buttonDelegate
Button
{
buttonIndex: gridId
buttonText: itemText
onChangeEverything:
{
changeEverythingFunction();
}
}
}
}
}
}
}
Your approach is in the opposite direction: Your approach is to obtain the item of the view and modify it, but the approach that Qt points out is that the view reflects the information of the model and modifies it when necessary.
The following is a simple example where every time you press on the button with "change me" text increasing the number it shows, but if you press the button with "change all" text it will change all the numbers. As it is observed everything is done through the model, not through the view that are only used to display information or receive user interaction.
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
Window {
visible: true
width: 640
height: 480
ListModel{
id: listmodel
}
function changeAll(){
for(var i=0; i< listmodel.count; ++i){
listmodel.setProperty(i, "number", listmodel.get(i).number + 1)
}
}
GridView{
id: grid
anchors.fill: parent
clip: true
model: listmodel
cellHeight: 120
cellWidth: 120
delegate: Item {
width: grid.cellWidth; height: grid.cellHeight
Column {
anchors.fill: parent
Text { text: model.number; anchors.horizontalCenter: parent.horizontalCenter }
Button{text: "change me"; onClicked: model.number +=1}
Button{text: "change all"; onClicked: changeAll()}
}
}
}
Component.onCompleted: {
for(var i=0; i < 10; ++i){
listmodel.append({"number": 0});
}
}
}
I am trying to load tabs dynamicly into a TabView from a different file. For this I am using Qt.createComponent and adds the component to the view. The tab is loaded but it's content does not show up and it is properly loaded. Like this:
TabView {
id:editor
Layout.minimumWidth: 50
Layout.fillWidth: true
}
Component.onCompleted: {
function newTab() {
var c = Qt.createComponent("tab.qml");
editor.addTab("tab", c);
var last = editor.count - 1;
editor.getTab(last).active = true;
}
newTab();
newTab();
}
And the "tab.qml" file:
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Tab {
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "lightgray"
TextArea {
anchors.fill: parent
}
}
}
What do I do wrong?
After reading #folibis clean-up suggestion I realized the way I got the Tab onCompleted handler wasn't working. Why I don't know. However replacing
var c = Qt.createComponent("tab.qml");
editor.addTab("tab", c);
var last = editor.count - 1;
editor.getTab(last).active = true;
with
var c = Qt.createComponent("tab.qml");
var tab = editor.addTab("tab", c);
tab.active = true
solved it.
I want to make an area, where a small rectangles appear in the places it where clicked. Later on I would try to add an ability to move those rectangles by dragging.
After studying Help, I tried to accomplish this with a MouseArea and a Component containing a Rectangle. Then, with onClicked, I was trying to create a new copy of a Component, but I failed whatever I tried (createComponent, createObject, etc.).
What is the correct way of creating a copy of an object in this case?
Am I using right tools for this goal?
MouseArea {
Component {
id: rect
Rectangle {
width: 10
height: 10
}
}
onClicked: < what? >
}
you can create a QML object from a string of QML using the Qt.createQmlObject() and set it's x and y values to mouseX and mouseY :
import QtQuick 2.3
import QtQuick.Window 2.0
Window {
id : root
visible: true
width: 1000
height: 500
MouseArea {
anchors.fill: parent
onClicked:{
var newObject = Qt.createQmlObject('import QtQuick 2.3; Rectangle {color: "red"; width: 10; height: 10}',
root);
newObject.x = mouseX;
newObject.y = mouseY;
}
}
}
Also if you put the code for your rectangle in a separate qml file say myRect.qml, you can create the object from the qml file by :
onClicked:{
var component = Qt.createComponent("myRect.qml");
var newObject = component.createObject(root, {"x": mouseX, "y": mouseY});
}
Qt Quick uses qDebug to perform logging, where the standard Javascript logging methods are maped to a Qt log type
console.log() -> qDebug()
console.debug() -> qDebug()
console.info() -> qDebug()
console.warn() -> qWarning()
console.error() -> qCritical()
At this point you loose the distinction between debug() and info().
Is there any way to register a custom logger for the Javascript methods directly in the QML engine without going over qDebug and qInstallMessageHandler?
Although the globalObject is read-only in the QQmlEngine, values stored therein are not. So you can modify the console property of the globalObject. You can do that both in C++ and in QML. Here is a trivial running example in QML:
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Console example")
Column {
anchors.centerIn: parent
Button {
text: "debug"
onClicked: {
console.log = console.debug
}
}
Button {
text: "exception"
onClicked: {
console.log = console.exception
}
}
Button {
text: "print something"
onClicked: {
console.log( "logging something" );
}
}
}
}
The first two buttons change, what the third button does by modifying the console.log method. C++ would look something like this (I cannot copy all of my code here, sorry, but it should get you going and it works nicely):
// in a header file
class HelperObject: public QObject {
Q_OBJECT
// ...
public slots:
myLog( QString aMessage );
};
// in an implementation file
QQmlEngine qmlEngine;
HelperObject helperObject;
QJSValue helperValue = qmlEngine.newQObject( &helperObject );
QJSValue consoleObject( qmlEngine.globalObject().property( "console" ) );
if (!consoleObject.isUndefined()) {
QJSValue myLogValue = helperValue.property( "myLog" );
consoleObject.setProperty( "log", myLogValue );
}
My problem is the following: I have a custom QML object based on a Rectangle, and I need to execute some javascript function with the width & height of this object when it's created.
To do that, I use Component.onCompleted to execute the javascript but in this function the width and height properties are wrong (like 0;0 or 0;-30), as if they were not yet created.
Code:
import QtQuick 2.3
import "js/kloggr.js" as Game
Rectangle {
property var kloggr: undefined
MouseArea {
anchors.fill: parent
// outputs the right values (about 400;600)
onClicked: console.log(width+";"+height);
}
Component.onCompleted: {
Game.kloggr = this;
kloggr = new Game.Kloggr(width, height);
console.log(width+";"+height); // outputs 0;-30
}
}
This object is created like this:
Kloggr {
id: kloggr
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: pauseBtn.top
}
(I've removed the irrelevant parts of the code)
So what did I do wrong, or what could I do to get the same result?
You have forget to set the width and height of your Rectangle ...
import QtQuick 2.3
import "js/kloggr.js" as Game
Rectangle {
width: 300
height: 300
property var kloggr: undefined
MouseArea {
anchors.fill: parent
// outputs the right values (about 400;600)
onClicked: console.log(width+";"+height);
}
Component.onCompleted: {
Game.kloggr = this;
kloggr = new Game.Kloggr(width, height);
console.log(width+";"+height); // outputs 0;-30
//now h + w = 300
}
}
If your use anchors, change your Rectangle by Item, for exemple
import QtQuick 2.3
import "js/kloggr.js" as Game
Item{
//without W and Height if you don't use the designer, if you use the designer you should set a default size
property var kloggr: undefined
Rectangle{
anchors.fill : parent
MouseArea {
anchors.fill: parent
// outputs the right values (about 400;600)
onClicked: console.log(width+";"+height);
}
Component.onCompleted: {
Game.kloggr = this;
kloggr = new Game.Kloggr(width, height);
console.log(width+";"+height); // outputs 0;-30
}
}//end of rectangle
} //end of item
I did not find out it if was a bug or something but I found a workaround: instead of using Component.onCompleted I used onVisibleChanged:
onVisibleChanged: {
if (visible && kloggr === undefined) {
Game.kloggr = this;
kloggr = new Game.Kloggr(width, height);
}
}
It seems that the width and height properties are only valid when the object is set to visible...
Thank you #yekmen for your help