From fc2d0492fa687fb4c668d28c13acacc16a6ee436 Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Thu, 6 Apr 2023 05:49:28 -0500 Subject: [PATCH] the image_model.rs is working The basic functions are all working properly. Now the model works by using diesel to connect the sql database and retrieve all the items and organize them. Then it'll ensure any additions and deletions are correct and happen first on the database before adding them to the model. There is still a C++ proxyModel inbetween QML and Rust, but this proxyModel interfaces with the Rust model instead of the C++ SqlTableModel. --- Cargo.lock | 2 + Cargo.toml | 1 + src/cpp/imagesqlmodel.cpp | 19 +-- src/cpp/imagesqlmodel.h | 7 +- src/main.cpp | 2 - src/qml/presenter/Library.qml | 10 +- src/qml/presenter/MainWindow.qml | 4 + src/rust/image_model.rs | 203 ++++++++++++++++++++++--------- src/rust/models.rs | 1 - 9 files changed, 170 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e36dee..bdef7aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,6 +272,7 @@ dependencies = [ "cxx-qt-lib", "diesel", "dirs", + "libsqlite3-sys", "serde", "serde_derive", ] @@ -282,6 +283,7 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" dependencies = [ + "cc", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index 5930934..da3dcf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ cxx-qt-lib = "0.5.1" # home = "0.5.4" dirs = "5.0.0" diesel = { version = "2.0.3", features = ["sqlite"] } +libsqlite3-sys = { version = ">=0.17.2, <0.26.0", features = ["bundled"] } # ffmpeg-next = "6.0.0" # cxx-qt-build generates C++ code from the `#[cxx_qt::bridge]` module diff --git a/src/cpp/imagesqlmodel.cpp b/src/cpp/imagesqlmodel.cpp index b10de33..ccfab76 100644 --- a/src/cpp/imagesqlmodel.cpp +++ b/src/cpp/imagesqlmodel.cpp @@ -208,14 +208,15 @@ QVariantMap ImageSqlModel::getImage(const int &row) { ImageProxyModel::ImageProxyModel(QObject *parent) :QSortFilterProxyModel(parent) { - m_imageModel = new ImageSqlModel; + m_imageModel = new ImageModel; + m_imageModel->testDatabase(); setSourceModel(m_imageModel); setDynamicSortFilter(true); - setFilterRole(Qt::UserRole + 1); + setFilterRole(1); setFilterCaseSensitivity(Qt::CaseInsensitive); } -ImageSqlModel *ImageProxyModel::imageModel() { +ImageModel *ImageProxyModel::imageModel() { return m_imageModel; } @@ -226,21 +227,21 @@ QModelIndex ImageProxyModel::idx(int row) { } QVariantMap ImageProxyModel::getImage(const int &row) { - auto model = qobject_cast(sourceModel()); - QVariantMap image = model->getImage(mapToSource(index(row, 0)).row()); + auto model = qobject_cast(sourceModel()); + QVariantMap image = model->getItem(mapToSource(index(row, 0)).row()); return image; } void ImageProxyModel::deleteImage(const int &row) { - auto model = qobject_cast(sourceModel()); - model->deleteImage(row); + auto model = qobject_cast(sourceModel()); + model->removeItem(row); } void ImageProxyModel::deleteImages(const QVector &rows) { - auto model = qobject_cast(sourceModel()); + auto model = qobject_cast(sourceModel()); qDebug() << "DELETING!!!!!!!!!!!!!!!!!!!!!!!" << rows; for (int i = rows.size() - 1; i >= 0; i--) { qDebug() << "deleting" << rows.at(i); - model->deleteImage(rows.at(i)); + model->removeItem(rows.at(i)); } } diff --git a/src/cpp/imagesqlmodel.h b/src/cpp/imagesqlmodel.h index 57d467b..ff31953 100644 --- a/src/cpp/imagesqlmodel.h +++ b/src/cpp/imagesqlmodel.h @@ -8,6 +8,7 @@ #include #include #include +#include "cxx-qt-gen/image_model.cxxqt.h" class ImageSqlModel : public QSqlTableModel { @@ -50,13 +51,13 @@ private: class ImageProxyModel : public QSortFilterProxyModel { Q_OBJECT - Q_PROPERTY(ImageSqlModel *imageModel READ imageModel) + Q_PROPERTY(ImageModel *imageModel READ imageModel) public: explicit ImageProxyModel(QObject *parent = nullptr); ~ImageProxyModel() = default; - ImageSqlModel *imageModel(); + ImageModel *imageModel(); Q_INVOKABLE QModelIndex idx(int row); public slots: @@ -65,7 +66,7 @@ public slots: Q_INVOKABLE void deleteImages(const QVector &rows); private: - ImageSqlModel *m_imageModel; + ImageModel *m_imageModel; }; diff --git a/src/main.cpp b/src/main.cpp index 9bd50ff..fc88ebb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -142,8 +142,6 @@ int main(int argc, char *argv[]) // QScopedPointer preswin(new QQuickView); QScopedPointer serviceItemModel(new ServiceItemModel); QScopedPointer slideobject(new SlideObj); - QScopedPointer imageModel(new ImageModel); - imageModel.get()->testDatabase(); Settings *settings = new Settings; settings->setup(); diff --git a/src/qml/presenter/Library.qml b/src/qml/presenter/Library.qml index 652ee67..b7f3aaf 100644 --- a/src/qml/presenter/Library.qml +++ b/src/qml/presenter/Library.qml @@ -90,7 +90,7 @@ Item { itemIcon: "folder-pictures-symbolic" itemSubtitle: { if (fileValidation) - model.filePath; + model.path; else "file is missing" } @@ -299,11 +299,11 @@ Item { } function addImg(url) { - imageProxyModel.imageModel.newImage(url); + imageProxyModel.imageModel.newItem(url); selectedLibrary = "image"; - imageLibraryList.currentIndex = imageProxyModel.imageModel.rowCount(); - console.log(imageProxyModel.imageModel.getImage(imageLibraryList.currentIndex)); - const image = imageProxyModel.imageModel.getImage(imageLibraryList.currentIndex); + imageLibrary.libraryList.currentIndex = imageProxyModel.imageModel.count(); + console.log(imageProxyModel.getImage(imageLibrary.libraryList.currentIndex)); + const image = imageProxyModel.getImage(imageLibrary.libraryList.currentIndex); showPassiveNotification("newest image: " + image.title); if (!editMode) editMode = true; diff --git a/src/qml/presenter/MainWindow.qml b/src/qml/presenter/MainWindow.qml index aa91011..6136e88 100644 --- a/src/qml/presenter/MainWindow.qml +++ b/src/qml/presenter/MainWindow.qml @@ -52,6 +52,10 @@ Controls.Page { Component.onCompleted: { changeServiceItem(0); presentation.forceActiveFocus(); + imageProxyModel.setSourceModel(ImageModel); + console.log("^^^^^"); + console.log(imageProxyModel.model); + console.log("^^^^^"); /* const loaded = ServiceItemModel.loadLastSaved(); */ /* if (!loaded) */ /* showPassiveNotification("Failed loading last file"); */ diff --git a/src/rust/image_model.rs b/src/rust/image_model.rs index 35c59d2..88519a7 100644 --- a/src/rust/image_model.rs +++ b/src/rust/image_model.rs @@ -1,5 +1,12 @@ #[cxx_qt::bridge] mod image_model { + use crate::image_model::image_model::Image; + use crate::models::*; + use crate::schema::images::dsl::*; + use diesel::sqlite::SqliteConnection; + use diesel::{delete, insert_into, prelude::*}; + use std::path::{Path, PathBuf}; + unsafe extern "C++" { include!(< QAbstractListModel >); include!("cxx-qt-lib/qhash.h"); @@ -10,6 +17,8 @@ mod image_model { type QVariant = cxx_qt_lib::QVariant; include!("cxx-qt-lib/qstring.h"); type QString = cxx_qt_lib::QString; + include!("cxx-qt-lib/qurl.h"); + type QUrl = cxx_qt_lib::QUrl; include!("cxx-qt-lib/qmodelindex.h"); type QModelIndex = cxx_qt_lib::QModelIndex; include!("cxx-qt-lib/qvector.h"); @@ -30,6 +39,7 @@ mod image_model { #[cxx_qt::qobject(base = "QAbstractListModel")] #[derive(Default, Debug)] pub struct ImageModel { + highest_id: i32, images: Vec, } @@ -51,12 +61,6 @@ mod image_model { // use crate::entities::{images, prelude::Images}; // use sea_orm::{ConnectionTrait, Database, DbBackend, DbErr, Statement, ActiveValue}; - use crate::models::*; - use diesel::prelude::*; - use diesel::sqlite::SqliteConnection; - use std::path::PathBuf; - - use crate::image_model::image_model::Image; impl qobject::ImageModel { #[qinvokable] pub fn clear(mut self: Pin<&mut Self>) { @@ -68,61 +72,128 @@ mod image_model { } #[qinvokable] - pub fn test_database(&self) { - use crate::schema::images::dsl::*; - const DATABASE_URL: &str = "sqlite:///home/chris/.local/share/librepresenter/Libre Presenter/library-db.sqlite3"; - const DB_NAME: &str = "library_db"; - - let db = &mut SqliteConnection::establish(DATABASE_URL) - .unwrap_or_else(|_| panic!("error connecting to {}", DATABASE_URL)); - + pub fn test_database(mut self: Pin<&mut Self>) { + let db = &mut self.as_mut().get_db(); let results = images .load::(db) .expect("Error loading images"); + self.as_mut().set_highest_id(0); println!("SHOWING IMAGES"); + println!("--------------"); for image in results { println!("{}", image.title); println!("{}", image.id); - println!("--------------\n"); println!("{}", image.path); + println!("--------------"); + if self.as_mut().highest_id() < &image.id { + self.as_mut().set_highest_id(image.id); + } + + let img = self::Image { + id: image.id, + title: QString::from(&image.title), + path: QString::from(&image.path), + }; + + self.as_mut().add_image(img); } + println!("--------------------------------------"); + println!("{:?}", self.as_mut().images()); + println!("--------------------------------------"); } #[qinvokable] - pub fn remove_item(mut self: Pin<&mut Self>, index: i32) { + pub fn remove_item(mut self: Pin<&mut Self>, index: i32) -> bool { if index < 0 || (index as usize) >= self.images().len() { - return; + return false; } + let db = &mut self.as_mut().get_db(); - unsafe { - self.as_mut() - .begin_remove_rows(&QModelIndex::default(), index, index); - self.as_mut().images_mut().remove(index as usize); - self.as_mut().end_remove_rows(); + let image_id = self.images().get(index as usize).unwrap().id; + + let result = delete(images.filter(id.eq(image_id))).execute(db); + + match result { + Ok(_i) => { + unsafe { + self.as_mut() + .begin_remove_rows(&QModelIndex::default(), index, index); + self.as_mut().images_mut().remove(index as usize); + self.as_mut().end_remove_rows(); + } + println!("removed-item-at-index: {:?}", image_id); + println!("new-Vec: {:?}", self.as_mut().images()); + true + } + Err(_e) => { + println!("Cannot connect to database"); + false + } + } + } + + fn get_db(self: Pin<&mut Self>) -> SqliteConnection { + const DATABASE_URL: &str = "sqlite:///home/chris/.local/share/librepresenter/Libre Presenter/library-db.sqlite3"; + + SqliteConnection::establish(DATABASE_URL) + .unwrap_or_else(|_| panic!("error connecting to {}", DATABASE_URL)) + // self.rust().db = db; + } + + #[qinvokable] + pub fn new_item(mut self: Pin<&mut Self>, url: QUrl) { + println!("LETS INSERT THIS SUCKER!"); + let file_path = PathBuf::from(url.path().to_string()); + let name = file_path.file_stem().unwrap().to_str().unwrap(); + let image_id = self.rust().highest_id + 1; + let image_title = QString::from(name); + let image_path = url.to_qstring(); + + if self.as_mut().add_item(image_id, image_title, image_path) { + println!("filename: {:?}", name); + self.as_mut().set_highest_id(image_id); + } else { + println!("Error in inserting item"); } } #[qinvokable] - pub fn add_item(mut self: Pin<&mut Self>, id: i32, title: QString, path: QString) { - const DATABASE_URL: &str = "sqlite://library-db.sqlite3"; - const DB_NAME: &str = "library_db"; + pub fn add_item( + mut self: Pin<&mut Self>, + image_id: i32, + image_title: QString, + image_path: QString, + ) -> bool { + let db = &mut self.as_mut().get_db(); + // println!("{:?}", db); + let image = self::Image { + id: image_id, + title: image_title.clone(), + path: image_path.clone(), + }; + println!("{:?}", image); - let db = SqliteConnection::establish(DATABASE_URL) - .unwrap_or_else(|_| panic!("error connecting to {}", DATABASE_URL)); + let result = insert_into(images) + .values(( + id.eq(&image_id), + title.eq(&image_title.to_string()), + path.eq(&image_path.to_string()), + )) + .execute(db); + println!("{:?}", result); - let image = self::Image { id, title, path }; - // let model = images::ActiveModel { - // id: ActiveValue::set(id), - // title: ActiveValue::set(title.to_string()), - // path: ActiveValue::set(path.to_string()), - // ..Default::default() - // }; - // let res = Images::insert(model).exec(db).await?; - - self.as_mut().add_image(image); - - // Ok(()) + match result { + Ok(_i) => { + self.as_mut().add_image(image); + println!("{:?}", self.as_mut().images()); + true + } + Err(_e) => { + println!("Cannot connect to database"); + false + } + } } fn add_image(mut self: Pin<&mut Self>, image: self::Image) { @@ -136,27 +207,41 @@ mod image_model { } } - #[qinvokable] - pub fn insert_item( - mut self: Pin<&mut Self>, - id: i32, - title: QString, - path: QString, - index: i32, - ) { - let image = Image { id, title, path }; + // #[qinvokable] + // pub fn insert_item( + // mut self: Pin<&mut Self>, + // image_id: i32, + // image_title: QString, + // image_path: QString, + // index: i32, + // ) { + // let image = Image { + // id: image_id, + // title: image_title, + // path: image_path, + // }; + // let db = self.db(); - self.as_mut().insert_image(image, index); - } + // let i = image_id; + // let t = image_title.to_string(); + // let p = image_path.to_string(); - fn insert_image(mut self: Pin<&mut Self>, image: self::Image, id: i32) { - unsafe { - self.as_mut() - .begin_insert_rows(&QModelIndex::default(), id, id); - self.as_mut().images_mut().insert(id as usize, image); - self.as_mut().end_insert_rows(); - } - } + // use crate::schema::images::dsl::*; + // let result = insert_into(images) + // .values((id.eq(&i), title.eq(&t), path.eq(&p))) + // .execute(db); + + // self.as_mut().insert_image(image, index); + // } + + // fn insert_image(mut self: Pin<&mut Self>, image: self::Image, id: i32) { + // unsafe { + // self.as_mut() + // .begin_insert_rows(&QModelIndex::default(), id, id); + // self.as_mut().images_mut().insert(id as usize, image); + // self.as_mut().end_insert_rows(); + // } + // } #[qinvokable] pub fn get_item(self: Pin<&mut Self>, index: i32) -> QMap_QString_QVariant { @@ -252,7 +337,7 @@ mod image_model { let mut roles = QHash_i32_QByteArray::default(); roles.insert(0, cxx_qt_lib::QByteArray::from("id")); roles.insert(1, cxx_qt_lib::QByteArray::from("title")); - roles.insert(2, cxx_qt_lib::QByteArray::from("path")); + roles.insert(2, cxx_qt_lib::QByteArray::from("filePath")); roles } diff --git a/src/rust/models.rs b/src/rust/models.rs index f3ce7c1..45e0ce7 100644 --- a/src/rust/models.rs +++ b/src/rust/models.rs @@ -4,6 +4,5 @@ use diesel::prelude::*; pub struct Image { pub id: i32, pub title: String, - #[diesel(column_name = "filePath")] pub path: String, }