Property alias cannot be accessed through another QML - sqlite

i was trying to make an app with Local Storage for the database and i want to display the data from the database to a listview in different QML file. i know i can make a property alias to the listview so i can access it from another file then append the query result. but when i hit the button to show the data it says that listViewKos was not define but it was already property alias
i did my best to keep the code short, some of the component might have been deleted due to it but appart from the problem i describe above everything works just fine.
#main.qml the JS is just a JS object where the data came from
import QtQuick 2.5
import QtQuick.Controls 2.5
import QtQuick.Dialogs 1.1
import QtQuick.LocalStorage 2.0
import "./Storage.js" as Storage
ApplicationWindow {
id: applicationWindow
width: 640
height: 480
property int kontrakanJumlahKamar
property int kontrakanPrice
property int kosPrice
property string kosGenderType
property alias kosloader: kosloader
property var db
Component.onCompleted: {
db = LocalStorage.openDatabaseSync("ngomahyuk", "1.0", "StorageDatabase", 1000000)
db.transaction(function(tx){
tx.executeSql('CREATE TABLE IF NOT EXISTS kos(namakos TEXT, alamat TEXT, thumbnail TEXT)');
});
// insert data for kos
db.transaction(function(tx){
for (var i = 0; i < Storage.kos.length; i++){
try {
tx.executeSql("INSERT INTO kos (namakos, alamat, thumbnail) VALUES('"+Storage.kos[i].namakos+"','"+Storage.kos[i].alamat+"','"+Storage.kos[i].thumbnail+"'");
} catch (err) {
console.log(err);
}
}
});
}
Button {
id: button
text: qsTr("Search")
MouseArea{
anchors.fill: parent
onClicked:{
if (textFieldHarga.text === ""){
kosPrice = 0
} else {
kosPrice = parseInt(textFieldHarga.text)
}
kosGenderType = comboBoxGender.currentText
kosloader.visible = true
db.transaction(function(tx){
var rs = tx.executeSql("SELECT * FROM kos WHERE gender = '"+kosGenderType+"' AND harga <= "+kosPrice);
if (rs.rows.length === 0){
alertDialogKos.open()
}else {
for (var i = 0; i < rs.rows.length; i++){
listViewKos.model.append({ //this is the one that suppose to be working
imagePath : rs.rows[i].thumbnail,
kosName : rs.rows[i].namakos,
kosAlamat : rs.rows[i].alamat,
})
}
kosloader.source = "Kos.qml"
}
});
}
}
MessageDialog{
// messagedialog code deleted to keep it short
}
background: Rectangle {
// deleted to keep it short
}
contentItem: Text {
id: textItem
text: "Search"
}
}
Loader{
id: kosloader
width: 640
height: 480
opacity: 1
clip: false
visible: false
active: false
anchors.fill: parent
source: ""
}
}
#Kos.qml i use PageBackground.qml as a background
import QtQuick 2.4
import QtQuick.Controls 2.3
PageBackground {
id: kos
width: 640
height: 480
property alias listViewKos: listViewKos
ListView {
id: listViewKos
x: 15
y: 87
width: 640
height: 410
clip: true
model: ListModel{
// need to be in for loop and data from database
}
// delegate listview template
delegate: Item {
height : 195
width : 640
Image {
id: idthumbnail
width: 235
height: 165
source: imagePath
}
Text {
id: idnamakos
x: 252
y: 8
text: kosName
}
Text {
id: idalamat
text: qsTr("Alamat : " + kosAlamat)
}
}
}
}
}

Related

What's the source in image in ListView QML

I need to realise list of images, which are chosen by user (folder) and implemet it in a list.
I have some trouble with source in object Image. In this way I see in console "You chose path..", but my images doesn't appear. Why? What's the right path for source?
The path isn't static because the user choose a folder with many pictures in it, so what should I do?
ApplicationWindow {
id:window;
visible: true
FileDialog {
id: fileDialog;
nameFilters: ["Image files (*.jpg *.png *.jpeg)"]
selectFolder: true;
onAccepted{
console.log("You chose " + fileDialog.fileUrl
}
listView.model = fileDialog.fileUrls;
}
......
ListView {
id: listView
visible: true;
orientation: Qt.Vertical
delegate: {
Image{
id: img
width: 40
height:40
source: model(????)
}
}
....
}
I think you misunderstood what fileDialog.fileUrls gives you. It doesn't give you a list of files in the folder that you chose. It gives you a list of the files or folders that you selected. You still need to search the directory for the files that you want. Thankfully, Qt provides a FolderListModel to do that. So here's a cleaned up example that does this:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.15
import QtQuick.Dialogs 1.2
import Qt.labs.folderlistmodel 2.15
Window {
id: window
width: 640
height: 480
visible: true
Button {
id: btn
text: "Open"
onClicked: fileDialog.open();
}
FileDialog {
id: fileDialog;
selectFolder: true;
onAccepted: folderModel.folder = fileDialog.fileUrl;
}
ListView {
id: listView
anchors.top: btn.bottom
width: 200
height: 400
model: FolderListModel {
id: folderModel
nameFilters: ["*.jpg", "*.png", "*.jpeg"]
showDirs: false
}
delegate: Image {
id: img
width: 40
height: 40
source: fileUrl
}
}
}
First you need add the url paths to the model and then use it. Take care about the file prefix. Something like this :
FileDialog {
id: fileDialog;
selectFolder: true;
onAccepted: updateFiles(fileDialog.files)
}
ListView {
id: listView
anchors.top: btn.bottom
width: 200
height: 400
model: FolderListModel {
id: folderModel
}
delegate: Image {
id: img
width: 40
height: 40
source: listView.model.get(index).fileUrl
}
}
function updateFiles(fileList){
console.log(fileList)
console.log("You chose file(s): " + fileList.fileUrls)
console.log("Num files: " + fileList.length)
for(var i = 0; i < fileList.length; i++){
var path = fileList[i];
path = stripFilePrefix(path);
var newFileUrl = {
"fileUrl": path
};
folderModel.append(newFileUrl);
}
}
function stripFilePrefix(filePath) {
if (Qt.platform.os === "windows") {
return filePath.replace(/^(file:\/{3})|(file:)|(qrc:\/{3})|(http:\/{3})/, "")
}
else {
return filePath.replace(/^(file:\/{2})|(qrc:\/{2})|(http:\/{2})/, "");
}
}

Qt5/QML - Pagination in Text/TextArea

Let's say I have a QML Text or TextArea that contains a very long HTML page. I want to make it easier to read by splitting it into pages.
More specifically, every time I press the down key, I want it to scroll down until none of the current text on the screen is still there.
I already know how to make the entire TextArea scrollable; that's not what I'm looking for. What I'm looking for is more like the kind of behavior you'd expect in an ebook reader.
Is there any way to do something like that, preferably in pure QML (though C++ is also fine).
You can measure the TextArea height after loading, divide it to the container height and so get the count of pages. Then you just move the TextArea relative to its container according to the current page.
The simple illustration of my idea:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
Window {
id: main
visible: true
width: 640
height: 800
property int currentPage: 1
property int pageCount: 1
ColumnLayout
{
anchors.fill: parent
anchors.margins: 5
RowLayout {
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignHCenter
Button {
text: "<"
onClicked: {
if(main.currentPage > 1)
main.currentPage --;
}
}
Text {
text: main.currentPage + " / " + main.pageCount
}
Button {
text: ">"
onClicked: {
if(main.currentPage < main.pageCount)
main.currentPage ++;
}
}
}
Rectangle {
id: container
clip: true
Layout.fillHeight: true
Layout.fillWidth: true
TextArea {
id: msg
text: "Loading ..."
width: container.width
height: container.height
y: -(main.currentPage - 1) * container.height
textFormat: TextEdit.RichText
wrapMode: TextEdit.Wrap
Component.onCompleted: {
msg.makeRequest();
}
onContentHeightChanged: {
msg.height = msg.contentHeight;
if(msg.contentHeight >= container.height && container.height > 0)
{
main.pageCount = msg.contentHeight / container.height;
loader.running = false;
}
}
function makeRequest()
{
var doc = new XMLHttpRequest();
msg.text = "";
doc.onreadystatechange = function() {
if (doc.readyState === XMLHttpRequest.DONE) {
msg.text = doc.responseText;
}
}
doc.open("GET", "http://www.gutenberg.org/files/730/730-h/730-h.htm");
doc.send();
}
}
}
}
BusyIndicator {
id: loader
running: true
anchors.centerIn: parent
}
}
Of course you have to process margins, the last line on a page, recalculate the values on resizing etc.

ListModel content not set properly

I have the following code. It basically builds a dialog with a TableView in it, in which I can set values manually in the cells:
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
Window {
visible: true
width: 538
height: 360
MouseArea {
anchors.fill: parent
onClicked: {
dial.open()
}
}
Dialog {
id:dial
width: 300
height: 500
title: "Set Path Parameters"
standardButtons: StandardButton.Ok | StandardButton.Cancel
signal updSig(var content)
ListModel {
id: streetsModel
property int count: 100
Component.onCompleted: {
for (var i=1 ; i<= count ; ++i)
streetsModel.append({"street_alias":"str_"+i , "start_xstart_y": "", "end_xend_y": "", "width": ""})
}
//todo: erste Spalte auch einbeziehen
function getColumnContent(role) {
var cont = ""
for (var i=0 ; i< count ; ++i) {
var cellContent;
cellContent = streetsModel.get(i).role
//streetsModel.get(styleData.row).role
if(!(cellContent === "") && !(cellContent === undefined))
cont += cellContent === "" ? "" : (cellContent + "\n")
}
cont = cont.slice(0,-1)
return cont
}
}
Item {
anchors.fill: parent
Component {
id: editableDelegate
Item {
TextInput {
id: textinput
width: parent.width
anchors.margins: 4
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: styleData.value
color: styleData.textColor
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
onEditingFinished: {
var role = styleData.role
streetsModel.set(styleData.row, {role: textinput.text})
console.log(streetsModel.get(styleData.row).role)
console.log(styleData.row, role)
}
}
}
}
}
TableView {
id: streetsTab
model: streetsModel
anchors.margins: 12
anchors.fill:parent
TableViewColumn {
id: strt_al_cl
role: "street_alias"
title: "Street Alias"
width: 120
}
TableViewColumn {
id: start_xy_cl
role: "start_xstart_y"
title: "StartX,StartY"
width: 120
}
TableViewColumn {
id: end_xy_cl
role: "end_xend_y"
title: "EndX,EndY"
width: 120
}
TableViewColumn {
id: width_cl
role: "width"
title: "Width"
width: 120
}
itemDelegate: {
return editableDelegate;
}
}
onAccepted: {
var content = [streetsModel.getColumnContent(start_xy_cl.role), streetsModel.getColumnContent(end_xy_cl.role), streetsModel.getColumnContent(width_cl.role)];
updSig(content)
this.close()
}
onRejected: this.close()
}
}
Now, if I set the values 1 in cellnumber (0,0), 2 in cellnumber (0,1) and 3 in cellnumber (0,2) (and click on an arbitrary other cell after that to insert the content), I get as content in my onAccepted-method [3,3,3]. This means the values for the first two roles start_xstart_y and end_xend_y get overwritten by the third role value for the role width. Why is this? If the code is run, on the console, the correct values in the cells are printed (see the console.log(...) parts), which stem from the model directly. So I don't understand why they are finally overwritten

Get active Tab from TabView and change item property

In the context of a dual-pane file manager, I have two TabView items side by side, each contains multiple tabs of course, and each Tab loads a TableView showing the content of a specific directory using FolderListModel.
SplitView
TabView
Tab
Tab
TabView
Tab
My current task is to implement a toolbar button to toggle the showHidden property of the FolderListModel instance shown in the active tab. Therefore, I need a way to find out what the currently active tab is.
Next, once I get the active Tab, I need to change Tab.item.some_property, in particular, the property of interest is show_hidden, which is an alias to the showHidden property of the underlying FolderListModel. For example, a hard-coded scenario would be:
ToolButton {
onClicked: {
tab1.item.show_hidden = false;
tab1.destroy(); // need "refresh" instead
}
}
First I need to get tab1 based on whether it is active, and second, after I change show_hidden, the view doesn't refresh by itself, so I need to call some kind of reload function, but which? Or maybe reload isn't the best way to do it? Is it possible to do it using a custom signal handler? (Again I can only think conceptually without knowing how to implement it.)
As suggested I'm posting a running example below:
/* main.qml */
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
visible: true
width: 1280
height: 700
toolBar: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
onClicked: { // TODO toggle folderModel.showHidden property
tab1A.item.show_hidden = false;
// tab1A.destroy(); // fixme how to refresh the view?
}
}
}
}
Item {
anchors.fill: parent
SplitView {
id: splitView
anchors.fill: parent
TabView {
id: tabView1
width: splitView.width / 2
Tab {
id: tab1A
title: qsTr("Home")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///tmp";
}
}
Tab {
title: qsTr("Folder")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///home";
}
}
}
TabView {
id: tabView2
Tab {
title: qsTr("Home")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///home";
}
}
}
}
}
}
/* dirview.qml */
import QtQuick 2.4
import QtQuick.Controls 1.4
import Qt.labs.folderlistmodel 2.1
TableView {
property alias folder_url: folderModel.folder
property alias show_hidden: folderModel.showHidden
id: tableView
anchors.fill: parent
TableViewColumn {
role: "fileName"
title: qsTr("Name")
width: tableView.width * 0.7
}
TableViewColumn {
role: "fileSize"
title: qsTr("Size")
width: tableView.width * 0.2
}
FolderListModel {
id: folderModel
nameFilters: ["*"]
showHidden: true
showDirsFirst: true
showDotAndDotDot: true
}
model: folderModel
}
Thank you.
Noticed something weird: Tab.item.folder_url has the right info, however, Tab.item.show_hidden is always false, even if I remove the line where I manually set it to false. This is hard to understand as I initially set FolderListModel.showHidden to true in dirview.qml.
ToolButton {
onClicked: { // TODO toggle folderModel.showHidden property
var cur_tab_idx = tabView1.currentIndex;
console.log(tabView1.getTab(cur_tab_idx).item.folder_url);
console.log(tabView1.getTab(cur_tab_idx).item.show_hidden);
}
}
Here is an explanation of how I have got it to work.
I solved first problem using focus flag. When current Tab in TabView changes one Tab gains focus and the other one looses. So by using onFocusChanged() signal you can know exactly when one Tab becomes active or inactive.
The focus of Tab does not change when focus of whole TabView changes. Because of this I created an Array (named tabs in code) containing references to every TabView and Tab it contains. With this when TabView becomes inactive I can set focus of its Tab objects to false using simple for.
Second problem was more tricky. I see no other option of turning showHidden flag off than destroying and creating a new FolderListModel. We cannot (or I could not :) ) provide model to TableView dynamically so I made a ListModel. Advantage of regular ListModel compared to FolderListModel is that it can be cleared and refilled with data. Every time folder_url or show_hidden changes I destroy current FolderListModel and create a new one. After it is created I rewrite its data to the ListModel.
Here is the working code.
main.qml
/* main.qml */
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
visible: true
width: 1280
height: 700
property var tabs: [
[tabView1, [tab1A, tab1B]],
[tabView2, [tab2A]]
]
toolBar: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
onClicked: { // TODO toggle folderModel.showHidden property
tab1A.item.show_hidden = false;
// tab1A.destroy(); // fixme how to refresh the view?
}
}
}
}
Item {
anchors.fill: parent
SplitView {
id: splitView
anchors.fill: parent
TabView {
id: tabView1
width: splitView.width / 2
Tab {
id: tab1A
title: qsTr("Home")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///tmp";
}
onFocusChanged: {
item.show_hidden = focus
}
}
onFocusChanged: {
if (!focus)
for (var i = 0 ; i < tabs[0][1].length ; i++)
tabs[0][1][i].focus = false
}
Tab {
id: tab1B
title: qsTr("Folder")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///home";
}
onFocusChanged: {
item.show_hidden = focus
}
}
}
TabView {
id: tabView2
Tab {
id: tab2A
title: qsTr("Home")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///tmp";
}
onFocusChanged: {
item.show_hidden = focus
}
}
onFocusChanged: {
if (!focus)
for (var i = 0 ; i < tabs[1][1].length ; i++)
tabs[1][1][i].focus = false
}
}
}
}
}
dirview.qml
/* dirview.qml */
import QtQuick 2.4
import QtQuick.Controls 1.4
import Qt.labs.folderlistmodel 2.1
TableView {
property string folder_url
property bool show_hidden
id: tableView
anchors.fill: parent
TableViewColumn {
role: "fileName"
title: qsTr("Name")
width: tableView.width * 0.7
}
TableViewColumn {
role: "fileSize"
title: qsTr("Size")
width: tableView.width * 0.2
}
ListModel {
id: secondListModel
}
property var fm
property int folderModelCount
onFolder_urlChanged: {
reloadFolderModel()
}
onShow_hiddenChanged: {
reloadFolderModel()
}
onFolderModelCountChanged: {
resetSecondListModel()
}
function reloadFolderModel() {
folderModelCount = 0
if (typeof(fm) !== "undefined")
fm.destroy()
var component = Qt.createComponent("foldermodel.qml")
if (component.status === Component.Ready)
fm = component.createObject(
tableView, {"folder_url": folder_url, "show_hidden": show_hidden})
else
console.error(component.errorString())
folderModelCount =
Qt.binding(function(){return fm.folderModel.count})
}
function resetSecondListModel() {
secondListModel.clear()
for (var i = 0 ; i < folderModelCount ; i++) {
secondListModel.append({
"fileName": fm.folderModel.get(i, "fileName"),
"filePath": fm.folderModel.get(i, "filePath"),
"fileURL": fm.folderModel.get(i, "fileURL"),
"fileBaseName": fm.folderModel.get(i, "fileBaseName"),
"fileSuffix": fm.folderModel.get(i, "fileSuffix"),
"fileSize": fm.folderModel.get(i, "fileSize"),
"fileModified": fm.folderModel.get(i, "fileModified"),
"fileAccessed": fm.folderModel.get(i, "fileAccessed"),
"fileIsDir": fm.folderModel.get(i, "fileIsDir")
})
}
}
model: secondListModel
}
foldermodel.qml (add this file)
import QtQuick 2.4
import QtQuick.Controls 1.4
import Qt.labs.folderlistmodel 2.1
Item {
property string folder_url
property bool show_hidden
property alias folderModel: folderModelObject
FolderListModel {
id: folderModelObject
nameFilters: ["*"]
folder: folder_url
showHidden: show_hidden
showDirsFirst: true
showDotAndDotDot: true
}
}
Now you understand why QML is not very flexible. :)
Solution to finding the current Tab in the active TabView (pane): declare a property of SplitView to store the TabView that has activeFocus.
A StatusBar is added to demo the functionality.
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
visible: true
width: 1280
height: 700
toolBar: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
onClicked: { // TODO toggle folderModel.showHidden property
// Demo: get the current tab of the active pane
var active_pane = splitView.activePane;
var cur_tab_idx = active_pane.currentIndex;
var cur_tab_item = active_pane.getTab(cur_tab_idx).item;
testLabel.text = cur_tab_item.folder_url;
}
}
}
}
SplitView {
id: splitView
property TabView activePane: tabView1
anchors.fill: parent
TabView {
id: tabView1
width: splitView.width / 2
onActiveFocusChanged: {
if (activeFocus) {
splitView.activePane = tabView1;
}
}
Tab {
title: qsTr("tmp")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///tmp";
}
}
Tab {
title: qsTr("home")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///home";
}
}
}
TabView {
id: tabView2
onActiveFocusChanged: {
if (activeFocus) {
splitView.activePane = tabView2;
}
}
Tab {
title: qsTr("bin")
source: "dirview.qml"
onLoaded: {
item.folder_url = "file:///bin";
}
}
}
}
statusBar: StatusBar {
RowLayout {
Label {
text: (splitView.activePane === tabView1) ? "Pane 1" : "Pane 2"
}
Label {
id: testLabel
}
}
}
}

Filtering with QML Document Gallery (Qt Mobility)

I want to filter albums for only one artist, but nothing is shown.
My code is:
SelectArtistPage.qml:
import QtQuick 1.1
import com.nokia.meego 1.0
import QtMobility.gallery 1.1
Page
{
id: selectArtistPage
tools: backtoolbar
property string selectedartist
ListView
{
id: galleryView
anchors.fill: parent
clip: true
model: artistModel
delegate: Item
{
height: 60
width: parent.width
Button
{
height: 50
width: parent.width - 20
text: (artist != "") ? artist : "(unknown artist)"
onClicked:
{
selectedartist = (text != "(unknown artist)") ? text : ""
selectAlbumPageLoader.source = "SelectAlbumPage.qml"
pageStack.push(selectAlbumPageLoader.item)
}
}
}
}
DocumentGalleryModel
{
id: artistModel
rootType: DocumentGallery.Artist
properties: ["artist"]
}
}
SelectAlbumPage.qml:
import QtQuick 1.1
import com.nokia.meego 1.0
import QtMobility.gallery 1.1
Page
{
id: selectAlbumPage
tools: backtoolbar
property string selectedalbum
ListView
{
id: galleryView
anchors.fill: parent
clip: true
model: albumModel
delegate: Item
{
height: 60
width: parent.width
Button
{
height: 50
width: parent.width - 20
text: (albumTitle != "") ? albumTitle : "(unknown album)"
onClicked:
{
selectedalbum = (text != "(unknown album)") ? text : ""
// here should be launching the last page
}
}
}
}
DocumentGalleryModel
{
id: albumModel
rootType: DocumentGallery.Album
properties: ["albumTitle", "artist"]
filter: GalleryEqualsFilter
{
property: "artist"
value: selectedartist
}
}
}
When I select an artist in SelectArtistPage another page opens, no buttons with albums names are painted.
If I remove the filter all the albums are shown.
What am I doing wrong? I'm using Qt Mobility 1.2 package for Maemo (I'm testing on N900).
It seems to be a bug in Maemo Qt Mobility, as I wrote here: http://talk.maemo.org/showpost.php?p=1254495&postcount=112

Resources