Hello
This is what I want to do: I want a Qt-GUI application which has multiple uses/options. For example one option to add something to a database, another option to delete something from a database,...
On the startscreen of the application there are several buttons with the text 'Add to database', 'Delete from database',...
Clicking on a button deletes all the current labels and buttons in the window, and calls a function building new labels and buttons to for example add something to a database.
For this to work I basically need to be able to do the following things:
- Having a sort of 'global variable', or being able to pass the same variable to the different build_functions by reference
- Call the functions in the UserInterface class to build different userinterface-views
I was able to code this in C++:
#include <QApplication>
#include <QPushButton>
#include <QAbstractButton>
#include <QLabel>
#include <QFont>
#include <QVBoxLayout>
#include <QMessageBox>
//Class handling all the UI in this Application
class UserInterface {
public:
//Build the initial UI the user sees
void build_start_screen(QWidget& window) {
//Calling reset function to delete all the childs in QVBoxLayout
this->reset(this->layout);
//Make new QVBoxLayout for this startscreen UI
this->layout = new QVBoxLayout(&window);
//Label 'Cat Manager'
QLabel* label_a = new QLabel();
label_a->setText("Cat Manager");
label_a->setFont(*new QFont("Sans Serif", 24, QFont::Bold));
//Label 'Your favourite...'
QLabel* label_b = new QLabel();
label_b->setText("Your favourite application to manage your best friends!");
label_b->setFont(*new QFont("Sans Serif", 16, QFont::Normal));
//Button to go to Option A-screen
QPushButton* option_a_btn = new QPushButton("Go option A");
QObject::connect(option_a_btn, &QPushButton::clicked, [this, &window](){
this->build_option_a_screen(window);
});
//Add labels and button to QVBoxLayout
layout->addWidget(label_a);
layout->addWidget(label_b);
layout->addWidget(option_a_btn);
}
//Build the screen for option A
void build_option_a_screen(QWidget& window) {
//Call the reset function to delete all the childs in QVBoxLayout
this->reset(this->layout);
//Make new QVBoxLayout for this option a UI
this->layout = new QVBoxLayout(&window);
//Label 'Cat Manager'
QLabel* label_a = new QLabel();
label_a->setText("Rofllllllll");
label_a->setFont(*new QFont("Sans Serif", 24, QFont::Bold));
//Add label to QVBoxLayout
layout->addWidget(label_a);
}
private:
//Function to reset the layout
void reset(QLayout* layout) {
if (layout == nullptr)
return;
QLayoutItem* child;
while (layout->count() > 0) {
child = layout->takeAt(0);
if (child->layout() != 0) {
reset(child->layout());
} else if (child->widget() != 0) {
delete child->widget();
}
delete child;
}
delete layout;
}
//Properties
QVBoxLayout* layout;
};
int main(int argc, char **argv) {
QApplication app (argc, argv);
//Initialize Window
QWidget Window;
Window.resize(400, 250);
//Create new UserInterface object
//This will allow us to create different user-interfaces
//depending on the function we call
UserInterface* ui = new UserInterface();
ui->build_start_screen(Window);
Window.show();
return app.exec();
}
As you can see I have one QWidget variable defined in the main-function called Window
.
After the class UserInterface is initiated I pass that Window variable by reference to the function build_start_screen()
. In that function different labels and buttons get added to a QVBoxLayout which is linked to Window. Now when option_btn_a
gets clicked the related function for Option A gets executed and the Window variable gets passed with it as reference. Because I pass it as reference the Window will get altered by the new labels and options needed for the Option A screen.
QVBoxLayout variable 'layout' is a class property so it's also shared between the different functions in the class.
I tried to replicate this in Rust:
#![windows_subsystem = "windows"]
use qt_widgets::{
cpp_core::{CppBox, MutPtr},
qt_core::QString,
qt_core::Slot,
QApplication, QLineEdit, QMessageBox, QPushButton, QVBoxLayout, QWidget,
QLabel,
qt_gui::QFont,
qt_core::QObject,
};
//Userinterface handling all the possible UserInterface-screens
//Each function is to build a specific userinterface (depending on e.g
//selected option)
struct UserInterface {
layout: Option<CppBox<QVBoxLayout>>,
}
impl UserInterface {
//Constructor
pub fn new() -> UserInterface {
UserInterface {
layout: None,
}
}
//Build the labels, buttons,... for the startscreen
pub fn build_start_screen(&mut self, window: &mut CppBox<QWidget>) {
unsafe {
//Make the new QVBoxLayout for this UI
self.layout = Some(QVBoxLayout::new_1a(window));
//Big fat font
let mut big_font : CppBox<QFont> = QFont::new();
big_font.set_family(&QString::from_std_str("Sans Serif"));
big_font.set_point_size(24);
big_font.set_weight(400);
//Label 'Cat Manager'
let mut label_a : CppBox<QLabel> = QLabel::new();
label_a.set_text(&QString::from_std_str("Cat Manager"));
label_a.set_font(&big_font);
//Label 'Your favourite...'
let mut label_b : CppBox<QLabel> = QLabel::new();
label_b.set_text(&QString::from_std_str("Your favourite application to manage your best friends!"));
//Button to go to Option A-screen
let mut option_a_btn : CppBox<QPushButton> = QPushButton::new();
option_a_btn.set_text(&QString::from_std_str("Go option A"));
//When button option_a_btn is clicked we need to toggle the screen
//to the Option A-userinterface
let option_a_btn_clicked = Slot::new(move || {
// [BORROW ERROR] self.build_option_a_screen(window);
});
option_a_btn.clicked().connect(&option_a_btn_clicked);
//Add all the widgets to the layout
// [BORROW_ERROR] self.layout.unwrap().add_widget(&mut label_a);
// [BORROW_ERROR] self.layout.unwrap().add_widget(&mut label_b);
// [BORROW_ERROR] self.layout.unwrap().add_widget(&mut option_a_btn);
}
}
//Build labels, options,... for the screen which shows when Option A gets selected
pub fn build_option_a_screen(&mut self, window: &mut CppBox<QWidget>) {
unsafe {
/*
//Make the new QVBoxLayout for this UI
self.layout = Some(QVBoxLayout::new_1a(window));
//Label 'Cat Manager'
let mut label_a : CppBox<QLabel> = QLabel::new();
label_a.set_text(&QString::from_std_str("Option A selected!"));
// [BORROW_ERROR] self.layout.unwrap().add_widget(&mut label_a);
*/
}
}
pub fn reset(&mut self, layout: &mut CppBox<QVBoxLayout>) {
/*
* Function to remove all buttons, labels,.. from user interface
* so other screen can be build on a clean window.
*/
}
}
fn main() {
QApplication::init(|_app| unsafe {
let mut window : CppBox<QWidget> = QWidget::new_0a();
//window.resize(400, 250);
let mut ui : UserInterface = UserInterface::new();
ui.build_start_screen(&mut window);
window.show();
QApplication::exec()
})
}
This code will compile but will only display an empty window. This because I am not able to add labels to my QVBoxLayout, and am not able to launch another function from my struct when a button gets clicked with QWidget as parameter.
I marked the lines of code where the errors occur by labeling them with 'BORROW_ERROR' in the comments. When I uncomment those lines with BORROW_ERROR (e.g line 55 and 61) these are the errors:
error[E0382]: use of moved value: `window`
--> src/main.rs:54:50
|
28 | pub fn build_start_screen(&mut self, window: &mut CppBox<QWidget>) {
| ------ move occurs because `window` has type `&mut cpp_core::cpp_box::CppBox<qt_widgets::QWidget>`, which does not implement the `Copy` trait
...
31 | self.layout = Some(QVBoxLayout::new_1a(window));
| ------ value moved here
...
54 | let option_a_btn_clicked = Slot::new(move || {
| ^^^^^^^ value used here after move
55 | self.build_option_a_screen(window);
| ------ use occurs due to use in closure
error[E0507]: cannot move out of `self.layout` which is behind a mutable reference
--> src/main.rs:60:13
|
60 | self.layout.unwrap().add_widget(&mut label_a);
| ^^^^^^^^^^^
| |
| move occurs because `self.layout` has type `std::option::Option<cpp_core::cpp_box::CppBox<qt_widgets::QVBoxLayout>>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `self.layout.as_ref()`
error[E0382]: use of moved value: `self`
--> src/main.rs:60:13
|
28 | pub fn build_start_screen(&mut self, window: &mut CppBox<QWidget>) {
| --------- move occurs because `self` has type `&mut UserInterface`, which does not implement the `Copy` trait
...
54 | let option_a_btn_clicked = Slot::new(move || {
| ------- value moved into closure here
55 | self.build_option_a_screen(window);
| ---- variable moved due to use in closure
...
60 | self.layout.unwrap().add_widget(&mut label_a);
| ^^^^^^^^^^^ value used here after move
How do I fix these errors? How to rewrite the working C++ code in Rust? The lifetime/borrow things in Rust are causing my trouble to write a GUI.
Thanks!
PS.
Qmake.pro file for C++:
TEMPLATE += app
QT += gui widgets
SOURCES += main.cpp
Cargo.toml file for Rust:
[package]
name = "Qt-rs"
version = "0.1.0"
authors = ["ONiel"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
qt_core = "0.4.1"
qt_gui = "0.4.1"
qt_widgets = "0.4.1"
qt_ui_tools = "0.4.1"
qt_3d_core = "0.4.1"
qt_3d_render = "0.4.1"
qt_3d_input = "0.4.1"
qt_3d_logic = "0.4.1"
qt_3d_extras = "0.4.1"
qt_charts = "0.4.1"
cpp_core = "0.5.1"
Qt Version is 5.14.