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})/, "");
}
}
Related
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)
}
}
}
}
}
I have created the following MWE (Qt 5.13.0):
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
ApplicationWindow
{
property int itemsNo: 3;
id: window
visible: true
width: 480
height: 480
SwipeView
{
anchors.fill: parent;
id: theSwipeView;
Loader
{
sourceComponent: theSingleComp;
Component
{
id: theSingleComp;
Page
{
Text
{
text: "The single one";
}
}
}
}
Repeater
{
model: itemsNo;
Loader
{
sourceComponent: theMultiComp;
Component
{
id: theMultiComp;
Page
{
Text
{
text: "The multi one " +
(theSwipeView.currentIndex - 1);
}
}
}
}
}
}
}
In my program, I have an unique component (theSingleComp) and multiple components behind him (theMultiComp). As for now, I need to implement the following functionality:
In case the model used for theMultiComp has only 1 item, display only this item and not the theSingleComp. In case the are more theMultiComp items, display it like now. It seems to me that there is no possibility for this to work if I keep the items defined statically. But on the other hand, I don't know how to do this dynamically, since there is a case in which one of the components should not be displayed at all. I tried an approach like this:
sourceComponent: (itemsNo > 1) ? theSingleComp : null;
But then the page for this null component is still created.
Your problem is that Loader is an Item and SwipeView creates a page for it even if it doesn't have a source component.
To solve this problem you can use Repeater instead with a model of 1 (or 0 to disable it). Repeater is also an Item but it has some special code under the hood to be ignored by containers.
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
ApplicationWindow
{
id: window
property int itemsNo: 0
visible: true
width: 480
height: 480
SwipeView {
id: theSwipeView
anchors.fill: parent
Repeater {
model: window.itemsNo > 1 ? 1 : 0
Page {
Text {
text: "The single one"
}
}
}
Repeater {
model: window.itemsNo
Page {
Text {
text: "The multi one " + model.index
}
}
}
}
}
(I've simplified your code to remove the explicit Components and the Loaders)
I have come up with the following solution but I am not happy with it. It's very hacky and the user can see how the page index changes.
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
ApplicationWindow
{
property int itemsNo: 2;
id: window
visible: true
width: 480
height: 480
SwipeView
{
anchors.fill: parent;
id: theSwipeView;
Component.onCompleted:
{
if (itemsNo > 1)
insertItem(0, theSingleComp);
set0IndexTimer.start();
}
Timer
{
id: set0IndexTimer;
interval: 1;
running: false;
repeat: false;
onTriggered: theSwipeView.setCurrentIndex(0);
}
onCurrentIndexChanged: console.log("page: ", currentIndex);
Repeater
{
model: itemsNo;
Loader
{
sourceComponent: theMultiComp;
Component
{
id: theMultiComp;
Page
{
Text
{
text: "The multi one " + theSwipeView.currentIndex;
}
}
}
}
}
}
Item
{
id: theSingleComp;
Page
{
Text
{
text: "The single one";
}
}
}
}
I am still seeking some other examples.
I have a program in Qt and a WebEngineView in it .I want to when my user clicked on a inputbox in webEngine a keyboard have been loaded and the inputbox get its contents from my keyboard (i wrote my own keyboard) but i can't do it .i try codes in bellow but don't work
WebEngineView {
anchors.fill:parent
id:webEng
url: "https://example.com"
visible: true
MouseArea {
id : mousearea
anchors.fill: parent
onClicked: {
mykeyboard.visible=true;
}
}
}
This is not a complete answer but this code could help:
import QtQuick 2.10
import QtWebView 1.1
import QtQuick.Controls 1.4
import QtWebEngine 1.7
Item {
width: 1280
height: 720
WebView { // or WebEngineView {
id: webview
width: 1280
height: 720
url: "http://google.com"
visible: true
onLoadingChanged: {
if (loadRequest.status === WebView.LoadSucceededStatus) {
console.log("Loaded!!")
webview.runJavaScript('
var input = document.getElementById("lst-ib");
input.addEventListener("click", function() {
alert("Clicked!");
});
'
)
}
}
}
Rectangle {
id: myDummyKeyboard
anchors.bottom: parent.bottom
width: parent.width
height: 100
color: "gray"
visible: true
border.width: 20
Button {
anchors.centerIn: parent
text: "Dummy"
onClicked: {
webview.runJavaScript('document.getElementById("lst-ib").value += "' + text + '"');
}
}
}
}
The part in the WebView (or WebEnginView) allows to display an alert when the input is clicked. But, something is missing, to link it to a QML handler. The solution is maybe to use WebChannel or maybe WebEngineScript as said by #folibis in the comments.
The part defined by myDummyKeyboard allows to add a string into the input when the user is clicking the button in the rectangle (fake keyboard).
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
}
}
}
}
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