rust-qt / ritual

Use C++ libraries from Rust
Apache License 2.0
1.22k stars 49 forks source link

Cannot create and render a Widget called from another Widget... #121

Closed Ouam74 closed 2 years ago

Ouam74 commented 2 years ago

Hi,

First of all than you very much for the great work, having Qt5 in RUST is great !

I am currently facing a problem.

I am creating a QTableWidget containing several QPushButtons, only in the first row. When clicking one of of these buttons it should create and render a QWidget (called "Form").

I created the corresponding signals-slots:

    unsafe fn init(self: &Rc<Self>) {
        for i in 0..self.col {
            self.button_vec[i as usize].clicked().connect(&self.slot_on_button_clicked());
        }
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_button_clicked(self: &Rc<Self>) {
        **let form = Form::new();**
        println!("create widget form");
    }

But when i click one of the buttons (in the first row of the QTableWidget) then a window seems to be created very shortly and disappears, it seems that the software panics.

1- Could you please tell me what may be the problem?

2- Should i call the "Form" QWidget in the "MyWindow" structure declaration?

struct MyWindow {
    form: QBox<Form>, # Should i call the "Form" QWidget here?
    window: QBox<QMainWindow>,
    centralwidget: QBox<QWidget>,
    gridlayout: QBox<QGridLayout>,
    table: QBox<QTableWidget>,
    verticallayout: QBox<QVBoxLayout>,
    button_vec: Vec<Ptr<QPushButton>>,
    col: i32,
    row: i32,
}

Here is the complete code:

#![windows_subsystem = "windows"]
#![allow(unused)]

use cpp_core::{Ptr, Ref, StaticUpcast,};
use qt_core::{
    q_init_resource, qs, slot, ScrollBarPolicy, CheckState, QVectorOf, QBox, QObject, QPtr, QPointF,
    SlotNoArgs, QFlags, WindowType, WindowModality, AlignmentFlag, QCoreApplication, Orientation, QStringList,
};
use qt_gui::{QStandardItem, QStandardItemModel, QPen, QBrush, QColor, q_painter::RenderHint};
use qt_widgets::{q_abstract_scroll_area, QApplication, QMainWindow, QListOfQWidget, QTableWidget,
     QHeaderView, QPushButton, QWidget, QFrame, QVBoxLayout, QGridLayout, QTreeView, QHBoxLayout,
 };
use std::rc::Rc;

///////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)]
struct Form {
    widget: QBox<QWidget>,
    gridlayout: QBox<QGridLayout>,
    treeview: QBox<QTreeView>,
    verticallayout: QBox<QVBoxLayout>,
    horizontallayout: QBox<QHBoxLayout>,
    enterbutton: QBox<QPushButton>,
    cancelbutton: QBox<QPushButton>,
}

impl StaticUpcast<QObject> for Form {
    unsafe fn static_upcast(ptr: Ptr<Self>) -> Ptr<QObject> {
        ptr.widget.as_ptr().static_upcast()
    }
}

impl Form {
    fn new() -> Rc<Form> {
        unsafe {
          let widget = QWidget::new_0a();
          widget.set_window_flags(QFlags::from(WindowType::FramelessWindowHint));
          widget.set_window_modality(WindowModality::ApplicationModal);
          let gridlayout = QGridLayout::new_1a(&widget);
          let verticallayout = QVBoxLayout::new_0a();
          let treeview = QTreeView::new_1a(&widget);
          verticallayout.add_widget_1a(&treeview);
          let horizontallayout = QHBoxLayout::new_0a();
          let enterbutton = QPushButton::new();
          enterbutton.set_text(&qs("Enter"));
          horizontallayout.add_widget(&enterbutton);
          let cancelbutton = QPushButton::new();
          cancelbutton.set_text(&qs("Cancel"));
          horizontallayout.add_widget(&cancelbutton);
          verticallayout.add_layout_1a(&horizontallayout);
          gridlayout.add_layout_5a(&verticallayout, 0, 0, 1, 1);

          widget.show();

          let this = Rc::new(Self {
              widget,
              gridlayout,
              verticallayout,
              treeview,
              horizontallayout,
              enterbutton,
              cancelbutton,
          });
          this.init();
          this
        }
    }

    unsafe fn init(self: &Rc<Self>) {
        self.enterbutton.clicked().connect(&self.slot_on_enter_clicked());
        self.cancelbutton.clicked().connect(&self.slot_on_cancel_clicked());
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_enter_clicked(self: &Rc<Self>) {
        println!("enter clicked");
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_cancel_clicked(self: &Rc<Self>) {
        println!("cancel clicked");
        self.widget.close();
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)]
struct MyWindow {
    window: QBox<QMainWindow>,
    centralwidget: QBox<QWidget>,
    gridlayout: QBox<QGridLayout>,
    table: QBox<QTableWidget>,
    verticallayout: QBox<QVBoxLayout>,
    button_vec: Vec<Ptr<QPushButton>>,
    col: i32,
    row: i32,
}

impl StaticUpcast<QObject> for MyWindow {
    unsafe fn static_upcast(ptr: Ptr<Self>) -> Ptr<QObject> {
        ptr.window.as_ptr().static_upcast()
    }
}

impl MyWindow {
    fn new() -> Rc<MyWindow> {
        unsafe {
           let row: i32 = 11;
           let col: i32 = 3;
           let window = QMainWindow::new_0a();
           window.resize_2a(400, 400);
           let centralwidget = QWidget::new_1a(&window);
           let gridlayout = QGridLayout::new_1a(&centralwidget);
           // widget.set_window_modality(WindowModality::ApplicationModal);
           let verticallayout = QVBoxLayout::new_0a();
           gridlayout.add_layout_5a(&verticallayout, 0, 0, 2, 2);
           window.set_central_widget(&centralwidget);
           let table = QTableWidget::new_0a();
           verticallayout.add_widget(&table);
           table.set_row_count(10);
           table.set_column_count(col);

           let my_string_array = [
                            ["Column", "Unit", "Test Group"],
                            ["A", "1", "a"],
                            ["A", "1", "b"],
                            ["A", "1", "c"],
                            ["A", "2", "a"],
                            ["B", "1", "a"],
                            ["B", "2", "c"],
                            ["B", "3", "a"],
                            ["C", "1", "a"],
                            ["C", "2", "c"],
                            ["D", "1", "c"]
            ];

           let mut button_vec = Vec::new();
           for i in 0..col {
               let button = QPushButton::new().into_ptr();
               button.set_text(&qs(my_string_array[0][i as usize]));
               table.set_cell_widget(0,i,button);
               button_vec.push(button);
           }

          window.show();

          let this = Rc::new(Self {
             window,
             centralwidget,
             gridlayout,
             table,
             verticallayout,
             button_vec,
             col,
             row,
          });
          this.init();
          this
        }
    }

    unsafe fn init(self: &Rc<Self>) {
        for i in 0..self.col {
            self.button_vec[i as usize].clicked().connect(&self.slot_on_button_clicked());
        }
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_button_clicked(self: &Rc<Self>) {
        let form = Form::new();
        println!("create widget form");
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

fn main() {
    QApplication::init(|_| unsafe {
        let todo_widget = MyWindow::new();
        unsafe { QApplication::exec() }
    })
}

Thank you very much for your help. Best Regards.

Olivier.

Ouam74 commented 2 years ago

I solved the issue...

The "form" QWidget must be declared in the "MyWindow" QMainWindow as follow :

#[derive(Debug)]
struct MyWindow {
    window: QBox<QMainWindow>,
    centralwidget: QBox<QWidget>,
    gridlayout: QBox<QGridLayout>,
    table: QBox<QTableWidget>,
    verticallayout: QBox<QVBoxLayout>,
    button_vec: Vec<Ptr<QPushButton>>,
    model_vec: Vec<Ptr<QStandardItemModel>>,
    form_vec: Vec<Rc<Form>>,
    col: i32,
    row: i32,
}

The goal of this project id to create a kind of "Excel" like filter. Here is the complete code :

#![windows_subsystem = "windows"]
#![allow(unused)]

use cpp_core::{Ptr, Ref, StaticUpcast,};
use cpp_core::{CppBox};
use qt_core::{
    q_init_resource, qs, slot, ScrollBarPolicy, CheckState, QVectorOf, QBox, QObject, QPtr, QPointF, SlotOfInt,
    SlotNoArgs, QFlags, WindowType, WindowModality, AlignmentFlag, QCoreApplication, Orientation, QStringList,
    q_state_machine::SignalEvent, QListOfQVariant, QItemSelectionModel, QModelIndex, QListOfQModelIndex, QVariant, QString,
};
use qt_gui::{QStandardItem, QStandardItemModel, QPen, QBrush, QColor, q_painter::RenderHint, SlotOfQStandardItem,
};
use qt_widgets::{q_abstract_scroll_area, QApplication, QMainWindow, QListOfQWidget, QTableWidget, QTableWidgetItem,
     QHeaderView, QPushButton, QWidget, QFrame, QVBoxLayout, QGridLayout, QTreeView, QHBoxLayout,
 };
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use itertools::Itertools;
use std::ptr;

///////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)]
struct Form {
    widget: QBox<QWidget>,
    gridlayout: QBox<QGridLayout>,
    treeview: QBox<QTreeView>,
    verticallayout: QBox<QVBoxLayout>,
    horizontallayout: QBox<QHBoxLayout>,
    enterbutton: QBox<QPushButton>,
    cancelbutton: QBox<QPushButton>,
}

impl StaticUpcast<QObject> for Form {
    unsafe fn static_upcast(ptr: Ptr<Self>) -> Ptr<QObject> {
        ptr.widget.as_ptr().static_upcast()
    }
}

impl Form {
    fn new() -> Rc<Form> {
        unsafe {
          let widget = QWidget::new_0a();
          widget.set_window_flags(QFlags::from(WindowType::FramelessWindowHint));
          widget.set_window_modality(WindowModality::ApplicationModal);
          let gridlayout = QGridLayout::new_1a(&widget);
          let verticallayout = QVBoxLayout::new_0a();
          let treeview = QTreeView::new_1a(&widget);
          verticallayout.add_widget_1a(&treeview);
          let horizontallayout = QHBoxLayout::new_0a();
          let enterbutton = QPushButton::new();
          enterbutton.set_text(&qs("Enter"));
          horizontallayout.add_widget(&enterbutton);
          let cancelbutton = QPushButton::new();
          cancelbutton.set_text(&qs("Cancel"));
          horizontallayout.add_widget(&cancelbutton);
          verticallayout.add_layout_1a(&horizontallayout);
          gridlayout.add_layout_5a(&verticallayout, 0, 0, 1, 1);

          // widget.show();

          let this = Rc::new(Self {
              widget,
              gridlayout,
              verticallayout,
              treeview,
              horizontallayout,
              enterbutton,
              cancelbutton,
          });
          this.init();
          this
        }
    }

    unsafe fn init(self: &Rc<Self>) {
        // self.treeview.slot_data_changed().connect(&self.slot_on_item_changed());
        self.enterbutton.clicked().connect(&self.slot_on_enter_clicked());
        self.cancelbutton.clicked().connect(&self.slot_on_cancel_clicked());
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_enter_clicked(self: &Rc<Self>) {
        println!("enter clicked");
        // Filtering function must be located here
        self.widget.close();
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_cancel_clicked(self: &Rc<Self>) {
        println!("cancel clicked");
        self.widget.close();
    }

    // #[slot(SlotNoArgs)]
    // unsafe fn on_item_changed(self: &Rc<Self>) {
    //     println!("item changed");
    // }
}
///////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)]
struct MyWindow {
    window: QBox<QMainWindow>,
    centralwidget: QBox<QWidget>,
    gridlayout: QBox<QGridLayout>,
    table: QBox<QTableWidget>,
    verticallayout: QBox<QVBoxLayout>,
    button_vec: Vec<Ptr<QPushButton>>,
    model_vec: Vec<Ptr<QStandardItemModel>>,
    form_vec: Vec<Rc<Form>>,
    col: i32,
    row: i32,
    form_index: Arc<Mutex<i32>>,
    // old_vec: Arc<Mutex<Vec<Ptr<QVariant>>>>,
    // new_vec: Arc<Mutex<Vec<Ptr<QVariant>>>>,
}

impl StaticUpcast<QObject> for MyWindow {
    unsafe fn static_upcast(ptr: Ptr<Self>) -> Ptr<QObject> {
        ptr.window.as_ptr().static_upcast()
    }
}

impl MyWindow {
    fn new() -> Rc<MyWindow> {
        unsafe {
           let row: i32 = 11;
           let col: i32 = 3;
           let form_index = Arc::new(Mutex::new(0));
           let window = QMainWindow::new_0a();
           window.resize_2a(400, 400);
           let centralwidget = QWidget::new_1a(&window);
           let gridlayout = QGridLayout::new_1a(&centralwidget);
           let verticallayout = QVBoxLayout::new_0a();
           gridlayout.add_layout_5a(&verticallayout, 0, 0, 2, 2);
           window.set_central_widget(&centralwidget);
           let table = QTableWidget::new_0a();
           verticallayout.add_widget(&table);
           table.set_row_count(row);
           table.set_column_count(col);
           table.horizontal_header().set_stretch_last_section(true);
           table.vertical_header().set_stretch_last_section(true);
           table.set_alternating_row_colors(true);
           // let new_vec = Arc::new(Mutex::new(Vec::new()));
           // let old_vec = Arc::new(Mutex::new(Vec::new()));

           let my_string_array = [
                            ["Column", "Unit", "Test Group"],
                            ["A", "1", "a"],
                            ["A", "1", "b"],
                            ["A", "1", "c"],
                            ["A", "2", "a"],
                            ["B", "1", "a"],
                            ["B", "2", "c"],
                            ["B", "3", "a"],
                            ["C", "1", "a"],
                            ["C", "2", "c"],
                            ["D", "1", "c"]
            ];

            // Fill in table
            for i in 1..row {
               for j in 0..col {
                   let item = QTableWidgetItem::new().into_ptr();
                   item.set_text(&qs(my_string_array[i as usize][j as usize].to_string()));
                   table.set_item(i, j, item);
               }
            }
            // Create choices array
            let mut choices_2d: Vec<Vec<String>> = Vec::new();
            for i in 0..col {
                let mut my_col: Vec<String> = Vec::new();
                for j in 1..row {
                    my_col.push(my_string_array[j as usize][i as usize].to_string());
                }
                let choices = my_col.iter().cloned().unique().collect_vec();
                choices_2d.push(choices);
            }
            // Create QPushButton array and place QPushButton in the cell
            let mut button_vec = Vec::new();
            for i in 0..col {
                let button = QPushButton::new().into_ptr();
                button.set_object_name((&qs(i.to_string())));
                button.set_text(&qs(my_string_array[0][i as usize]));
                table.set_cell_widget(0,i,button);
                button_vec.push(button);
           }
           // Create QStandardItemModel array
           let mut model_vec = Vec::new();
           for i in 0..col {
                let model = QStandardItemModel::new_0a().into_ptr();
                model_vec.push(model);
           }
           // Fill in the QStandardItemModel(s) with choices items
           for i in 0..col {
                let nrow = choices_2d[i as usize].len();
                let item = QStandardItem::new().into_ptr();
                item.set_text(&qs("Select All".to_string()));
                item.set_checkable(true);
                item.set_check_state(CheckState::Checked);
                model_vec[i as usize].append_row_q_standard_item(item);
                for j in 0..nrow {
                    let item = QStandardItem::new().into_ptr();
                    item.set_text(&qs(choices_2d[i as usize][j as usize].to_string()));
                    item.set_checkable(true);
                    item.set_check_state(CheckState::Checked);
                    model_vec[i as usize].append_row_q_standard_item(item);
                }
            }
            // Create Form array
            let mut form_vec = Vec::new();
            for i in 0..col {
                let form = Form::new();
                form.treeview.set_model(model_vec[i as usize]);
                form_vec.push(form);
            }

           window.show();

           let this = Rc::new(Self {
             window,
             centralwidget,
             gridlayout,
             table,
             verticallayout,
             button_vec,
             model_vec,
             form_vec,
             // old_vec,
             // new_vec,
             col,
             row,
             form_index,
           });
           this.init();
           this
         }
    }

    unsafe fn init(self: &Rc<Self>) {
        for i in 0..self.col {
            self.button_vec[i as usize].clicked().connect(&self.slot_on_button_clicked());
            self.model_vec[i as usize].item_changed().connect(&self.slot_on_item_changed());
        }
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_button_clicked(self: &Rc<Self>) {
        *self.form_index.lock().unwrap() = self.table.current_column() as i32;
        let form_index = *self.form_index.lock().unwrap();
        self.form_vec[form_index as usize].widget.show();
    }

    #[slot(SlotOfQStandardItem)]
    unsafe fn on_item_changed(self: &Rc<Self>, item_clicked: Ptr<QStandardItem>) {
         let form_index: i32 = *self.form_index.lock().unwrap();
         let mut item = QStandardItem::new().into_ptr();
         if item_clicked.text().to_std_string() == "Select All".to_string() && item_clicked.check_state() == CheckState::Unchecked {
            for i in 0..self.model_vec[form_index as usize].row_count_0a()
            {
                 item = self.model_vec[form_index as usize].item_1a(i);
                 item.set_check_state(CheckState::Unchecked);
            }
            println!("Unselecting All");
        }
         if item_clicked.text().to_std_string() == "Select All".to_string() && item_clicked.check_state() == CheckState::Checked {
            for i in 0..self.model_vec[form_index as usize].row_count_0a()
            {
                item = self.model_vec[form_index as usize].item_1a(i);
                item.set_check_state(CheckState::Checked);
            }
            println!("Selecting All");
         }
         else {
            println!("{:?} was clicked", item_clicked.text().to_std_string());
         }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

fn main() {
    QApplication::init(|_| unsafe {
        let todo_widget = MyWindow::new();
        unsafe { QApplication::exec() }
    })
}