Dynamically adding Text and Image objects
-
wrote on 18 Sept 2021, 13:17 last edited by tacdin
Hi, I am trying to code an application demo for my graduation project. As a feature of the demo, the images and textedit part need to be transferred to the user interface. I have a "button" under the section that says "Value" in the first step. When you press this button, buttons that write Text, Image and Export appear in a Popup. Of these buttons, Text and Image are related with function addText and function addImage, which depend on the functions in createobject.js. When you press the button that says Text, the function creates a texteditor in the column whose id is root via ListModel whose id is objectmodel. The TextEditor.qml file it calls provides this. The Image button first opens the folder with the photos on the device via FileDialog. I think the next step should be related to the "onaccepted:" property of FileDialog. I call the addimage function on the onaccepted part, but I have no idea what to write in AddImage.qml for later. How can I relate the source of Image in AddImage.qml with FileDialog? Any ideas and help would be greatly appreciated. thanks.
Text, Image and Export buttons are in the NavigationBarButton section. The full code is at the bottom. Before, the functions and sections I mentioned are as follows;
- createobject.js is here
var _component; var _callback; var _parent; var _source; function create(source, parent, callback) { _parent = parent; _callback = callback; _source = source; _component = Qt.createComponent(source); if (_component.status === Component.Ready || _component.status === Component.Error) createDone(); else _component.statusChanged.connect(createDone); } function createDone() { if (_component.status === Component.Ready) { var obj = _component.createObject(_parent); if (obj != null) _callback(obj, _source); else console.log("Error object: " + _source); _component.destroy(); } else if (_component.status === Component.Error) console.log("Error component: " + component.errorString()); }
**
- function addText() and function addImage are here
function addText() { CreateObject.create("TextEditor.qml", root, itemAdded); } function addImage() { CreateObject.create("AddImage.qml", root, itemAdded); } function itemAdded(obj, source) { objectsModel.append({"obj": obj, "source": source}) }
- The itemadded function adds an object to the ListModel dynamically with the append() method.
function itemAdded(obj, source) { objectsModel.append({"obj": obj, "source": source}) }
- The TextEditor.qml file is implemented from QT's own examples.
import QtQuick 2.0 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtQuick.Window 2.0 import io.qt.examples.texteditor 1.0 import QtQuick 2.5 Rectangle{ visible: true width:300 height:300 border.color: "black" radius: 25 ToolBar { leftPadding: 5 RowLayout { anchors.fill: parent spacing: 0 ToolButton { id: doneEditingButton font.family: "fontello" text: "\uE80A" // icon-ok opacity: !textArea.readOnly ? 1 : 0 onClicked: textArea.readOnly = true } } } DocumentHandler { id: document document: textArea.textDocument cursorPosition: textArea.cursorPosition selectionStart: textArea.selectionStart selectionEnd: textArea.selectionEnd onLoaded: { textArea.text = text } onError: { errorDialog.text = message errorDialog.visible = true } } Flickable { id: flickable flickableDirection: Flickable.VerticalFlick anchors.fill: parent TextArea.flickable: TextArea { id: textArea textFormat: Qt.RichText wrapMode: TextArea.Wrap readOnly: true persistentSelection: true leftPadding: 6 rightPadding: 6 topPadding: 0 bottomPadding: 0 background: null onLinkActivated: Qt.openUrlExternally(link) } ScrollBar.vertical: ScrollBar {} } ToolBar { visible: !textArea.readOnly && textArea.activeFocus Flickable { anchors.fill: parent contentWidth: toolRow.implicitWidth flickableDirection: Qt.Horizontal boundsBehavior: Flickable.StopAtBounds Row { id: toolRow ToolButton { id: boldButton text: "\uE800" // icon-bold font.family: "fontello" focusPolicy: Qt.NoFocus checkable: true checked: document.bold onClicked: document.bold = !document.bold } ToolButton { id: italicButton text: "\uE801" // icon-italic font.family: "fontello" focusPolicy: Qt.NoFocus checkable: true checked: document.italic onClicked: document.italic = !document.italic } ToolButton { id: underlineButton text: "\uF0CD" // icon-underline font.family: "fontello" focusPolicy: Qt.NoFocus checkable: true checked: document.underline onClicked: document.underline = !document.underline } ToolSeparator {} ToolButton { id: alignLeftButton text: "\uE803" // icon-align-left font.family: "fontello" focusPolicy: Qt.NoFocus checkable: true checked: document.alignment == Qt.AlignLeft onClicked: document.alignment = Qt.AlignLeft } ToolButton { id: alignCenterButton text: "\uE804" // icon-align-center font.family: "fontello" focusPolicy: Qt.NoFocus checkable: true checked: document.alignment == Qt.AlignHCenter onClicked: document.alignment = Qt.AlignHCenter } ToolButton { id: alignRightButton text: "\uE805" // icon-align-right font.family: "fontello" focusPolicy: Qt.NoFocus checkable: true checked: document.alignment == Qt.AlignRight onClicked: document.alignment = Qt.AlignRight } ToolButton { id: alignJustifyButton text: "\uE806" // icon-align-justify font.family: "fontello" focusPolicy: Qt.NoFocus checkable: true checked: document.alignment == Qt.AlignJustify onClicked: document.alignment = Qt.AlignJustify } } } } RoundButton { id: editButton font.family: "fontello" text: "\uE809" // icon-pencil width: 48 height: width y: parent.height - height - 12 anchors.right: parent.right anchors.margins: 12 visible: textArea.readOnly highlighted: true onClicked: { textArea.readOnly = false textArea.forceActiveFocus() } } }
- The full code is here;
import QtQuick 2.6 import QtQuick.Dialogs 1.2 import QtQuick.Controls 2.12 import QtQuick 2.12 import QtQuick.Layouts 1.12 import QtGraphicalEffects 1.12 import "createtextobject.js" as TextCreator import "createobject.js" as CreateObject ApplicationWindow { id:appwindow visible: true width: 640 height: 480 title: qsTr("Demo App") function getControl(){ return { data: null, getTotalBataryCount:()=>{ let battery = 0; for(let i=0;i<this.data.length;i++){ battery += ((this.data[i]["battery0.mahConsumed"]!="--.--")?parseFloat(this.data[i]["battery0.mahConsumed"]):0); } return battery; }, getAltitudeCountHigh:()=>{ let amslHigh = 0; for(let i=0;i<this.data.length;i++){ if((this.data[i]["altitudeAMSL"]>amslHigh)){ amslHigh = this.data[i]["altitudeAMSL"]; } } return amslHigh; }, getTotalFlighttime : ()=> { let flighttime= 0; for(let i=0; i<this.data.length;i++){ flighttime += ((this.data[i]["flightTime"]!= "00:00:00")?parseFloat(this.data[i]["flightTime"]):0); } return flighttime; }, }; } function openFile(fileUrl){ return new Promise((resolve,reject)=>{ var parser = []; if (fileUrl.toString()) parser = csvJSON if (parser) { var request = new XMLHttpRequest() request.open('GET', fileUrl) request.onreadystatechange = function(event) { if (request.readyState === XMLHttpRequest.DONE) { let controlObject = getControl(); controlObject.data = parser(request.responseText); let items= [] items.push(controlObject.getTotalFlighttime().toString(), controlObject.getTotalBataryCount().toString(), controlObject.getAltitudeCountHigh() ) myModel.append({"flightTime": items[0], "battConsump": items[1], "altitude" : items[2] }) console.log( "deneme1", controlObject.getTotalFlighttime()) console.log("deneme2",controlObject.getTotalBataryCount()); console.log("test",controlObject.getAltitudeCountHigh()); console.log("test2",controlObject.getTotalFlighttime()); resolve(data); } } request.send() } reject(false); }); } Image { anchors.fill: parent source: "qrc:/images/heritage.jpeg" id: blurtest } ListModel { id: myModel ListElement { flightTime:"" battConsump:"" altitude:"" } } ListView { id: viewlist width: 350 height: 200 //anchors.fill: parent model: myModel anchors.centerIn: parent anchors.left: parent.left anchors.leftMargin: 50 anchors.top: parent.top anchors.topMargin: 40 delegate: GridLayout { visible:true columns: 3 columnSpacing: 50 rowSpacing: 3 Rectangle{ id: flighttimerect width: 75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id: flighttimerect1 // text: "Total Flight Time:"+ flightTime +"h" color: "grey" } } Rectangle { id: batteryconsumptrect width:75 height:75 Layout.alignment: Qt.AlignHCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id:batteryconsumptvalue //text: "Battery Consumption:"+ battConsump + "A" color: "black" font.pixelSize: 12 } } Rectangle { id: altituderect width:75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id:altitudetext //text: "Maximum Altitude:" + altitude+"m" color: "black" } } Rectangle{ id: valuestest width: 75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id: valueslabel1 // text: "Total Flight Time:"+ flightTime +"h" color: "grey" } } Rectangle{ id: valuestest2 width: 75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id: valueslabel2 //text: "Total Flight Time:"+ flightTime +"h" color: "grey" } } Rectangle{ id: valuestest3 width: 75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id: valueslabel3 // text: "Total Flight Time:"+ flightTime +"h" color: "grey" } } Rectangle{ id: valuestest4 width: 75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id: valueslabel4 // text: "Total Flight Time:"+ flightTime +"h" color: "grey" } } Rectangle{ id: valuestest5 width: 75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id: valueslabel5 //text: "Total Flight Time:"+ flightTime +"h" color: "grey" font.pixelSize: 12 } } Rectangle{ id: valuestest6 width: 75 height:75 Layout.alignment: Qt.AlignCenter color: "grey" radius: 25 Label{ anchors.centerIn: parent id: valueslabel6 //text: "Total Flight Time:"+ flightTime +"h" color: "grey" font.pixelSize: 12 } } } } function saveFile(fileUrl, text) { var request = new XMLHttpRequest(); request.open("PUT", fileUrl, false); request.send(text); return request.status; } function csvJSON(csvText) { let lines = []; const linesArray = csvText.split('\n'); linesArray.forEach((e, any) => { const row = e.replace(/[\s]+[,]+|[,]+[\s]+/g, ',').trim(); lines.push(row); }); lines.splice(lines.length - 1, 1); const result = []; const headers = lines[0].split(","); for (let i = 1; i < lines.length; i++) { const obj = {}; const currentline = lines[i].split(","); for (let j = 0; j < headers.length; j++) { obj[headers[j]] = currentline[j]; } result.push(obj); } return result; } FileDialog { id: filedialog nameFilters: ["Image files (*.png *.jpg)"] onAccepted: addImage() } RoundButton { visible: true x:0 y:0 contentItem: Image { id: roundbuttonimg source: "qrc:/images/plus.png" visible: true fillMode: Image.PreserveAspectFit anchors.centerIn: parent } onClicked: test123.visible= true } Popup { id:test123 // visible: angleSlider.pressed ? test123.close() : 1 width: parent.width height: parent.height modal: true padding: 0.0 background: Item { ShaderEffectSource { id: effectSource sourceItem: blurtest anchors.fill: parent sourceRect: Qt.rect(test123.x,test123.y,test123.width,test123.height) } GaussianBlur{ id: blur anchors.fill: effectSource source: effectSource radius: 64 samples: radius*2+1 } } contentItem: Item { height:tab.height width: tab.width visible:true anchors.centerIn: parent anchors.top: parent.top anchors.bottom: parent.bottom anchors.rightMargin: 40 anchors.leftMargin: 40 NavigationBar{ id:tab anchors.left: parent.left anchors.leftMargin: 100 anchors.right: parent.right anchors.rightMargin: 200 anchors.bottom: parent.bottom anchors.bottomMargin: 200 anchors.topMargin: 20 NavigationBarButton { id:textbutton text: "Text" icon.source: "qrc:/images/file.png" onClicked: { addText()//TextCreator.createTextObject(); test123.visible=false console.log("success") } palette { buttonText: "white" highlight: "white" highlightedText: "white" } background: Rectangle { anchors.fill: parent color: "#CF0730" radius: parent.height * 0.6 } onCheckedChanged: { if (checked) { // rootmainmenu.color = palette.highlight } } } NavigationBarButton { text: "Image" icon.source: "qrc:/images/image.png" onClicked: {filedialog.open() test123.visible= false } palette { buttonText: "white" highlight: "white" highlightedText: "white" } background: Rectangle { anchors.fill: parent color: "#335A9D" radius: parent.height * 0.6 } onCheckedChanged: { if (checked) { // rootmainmenu.color = palette.highlight } } } NavigationBarButton { text: "Export" icon.source: "qrc:/images/export.png" onClicked: stackview.push("Log.qml") palette { buttonText: "white" highlight: "white" highlightedText: "white" } background: Rectangle { anchors.fill: parent color: "#00704A" radius: parent.height * 0.6 } onCheckedChanged: { if (checked) { // rootmainmenu.color = palette.highlight } } } } } } function addText() { CreateObject.create("TextEditor.qml", root, itemAdded); } function addImage() { CreateObject.create("AddImage.qml", root, itemAdded); } function itemAdded(obj, source) { objectsModel.append({"obj": obj, "source": source}) } ListModel { id: objectsModel } Column { id: root anchors.left: parent.left anchors.top: viewlist.bottom anchors.topMargin: 10 anchors.leftMargin: 150 spacing: 10 width: 300 } }
-
wrote on 21 Sept 2021, 05:40 last edited by
This is a lot of source to understand before beginning to offer an answer. Positioning, formatting, and working features get in the way of understanding the core question. There might be more people willing to tackle it if the scope is limited to a single detail that doesn't work.
From a glance, it looks like this code is dynamically creating and managing QML objects using imperative javascript. In my experience, this leads to more complex and brittle code. If at all possible, leave that to
Loader
for single objects, andRepeater
or a model + view for a variable number. -
This is a lot of source to understand before beginning to offer an answer. Positioning, formatting, and working features get in the way of understanding the core question. There might be more people willing to tackle it if the scope is limited to a single detail that doesn't work.
From a glance, it looks like this code is dynamically creating and managing QML objects using imperative javascript. In my experience, this leads to more complex and brittle code. If at all possible, leave that to
Loader
for single objects, andRepeater
or a model + view for a variable number.
1/3