Skip to content
This repository has been archived by the owner on Feb 17, 2021. It is now read-only.

Accessing QObjects from Rust threads #32

Open
flanfly opened this issue Feb 1, 2017 · 7 comments
Open

Accessing QObjects from Rust threads #32

flanfly opened this issue Feb 1, 2017 · 7 comments

Comments

@flanfly
Copy link
Contributor

flanfly commented Feb 1, 2017

I want to modify a Qt model from another thread. Currently I define the model using the Q_LISTMODEL! macro, and call threaded() to start another thread. When calling append_row on the model while inside the new thread, the view does not get updated and the application prints the following:

QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'
(Make sure 'QQmlChangeSet' is registered using qRegisterMetaType().)

Searching the web it seems that changing a Qt model must happen in the GUI thread [1].

Looking at the code of threaded I'm not sure whenever I even want to use that function. It simply copies a mutable pointer to the new thread. As the new and the original thread have now a mutable reference to the object, this creates a possible data race and eliminates Rusts safety guarantees:

pub struct Blah {
  value: usize,
}

Q_OBJECT!(
pub Blah as QBlah {
  signals:
  slots:
    fn boom();
  properties:
});

impl QBlah {
  fn boom(&mut self) -> Option<&QVariant> {
    self.threaded(|s| {
        let value1: &mut usize = &mut s.value;
    });
    let value2: &mut usize = &mut self.value;
    None
  }
}

Both value1 and value2 can exist concurrently.

1: http://stackoverflow.com/questions/30421176/no-update-in-listview and http://blog.hostilefork.com/qt-model-view-different-threads/

@White-Oak
Copy link
Owner

You can call slots and signals and maybe change properties of Qt objects from different threads, but yes, you cannot change GUI for example. That is a very tricky and complicated topic -- the easiest would be to forbid any concurrent usage of QObjects, but that would just cripple the possibilities to work with qml.

@flanfly
Copy link
Contributor Author

flanfly commented Feb 7, 2017

Ok, how about we add a mechanism that allows us to run Rust code in the GUI thread? For example calling a user provided function every event loop iteration.

@White-Oak
Copy link
Owner

@flanfly well now that is quite an interesting idea...

@flanfly
Copy link
Contributor Author

flanfly commented Feb 10, 2017

The easiest way would be to allow spinning the event loop manual as I suggested in #25. So you can do stuff like this.

let engine = QmlEngine::new();
// ...
loop {
  engine.loop();
  // do stuff in GUI thread
}

It's still rather inelegant and maybe wasteful. Seems like the canonical way is to use a QTimer with interval set to 0. For this we'd need to expose the QTimer interface in DOtherSide and qml-rust.

@vandenoever
Copy link

A common use-case is that the rust replaces the content in a QListModel that is shown with QML. I do not currently see an sample that shows how to do this. The existing examples populate the QListModel before the UI is shown and do not modify it afterwards.

Ideally the contents of the QListModel would be replaced in one go, so the user does not see the list being repopulated. Would it be most efficient (or even possible) to make the model a property and set that?

@White-Oak
Copy link
Owner

@vandenoever https://github.com/White-Oak/kefia does it. In response to actions, QML calls Rust slots that modify QModelList.

@vandenoever
Copy link

@White-Oak Thanks! I've now ported my rust+qml example code to qml-rust.

https://gitlab.com/vandenoever/metadatadb/commit/7d9efab964bd9859c44c8b9bda0b6bccc944e54a

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants