Add a template for a Qt Widgets project
parent
38fe6be82b
commit
af7139027b
|
@ -0,0 +1,73 @@
|
||||||
|
project (my_rust_qt_widgets_project)
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
|
||||||
|
cmake_policy(SET CMP0046 NEW)
|
||||||
|
cmake_policy(SET CMP0063 NEW)
|
||||||
|
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
|
string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER)
|
||||||
|
if(CMAKE_BUILD_TYPE_UPPER STREQUAL DEBUG)
|
||||||
|
set(RUST_TARGET_DIR target/debug/)
|
||||||
|
set(RUST_BUILD_FLAG)
|
||||||
|
else()
|
||||||
|
set(RUST_TARGET_DIR target/release/)
|
||||||
|
set(RUST_BUILD_FLAG --release)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
### find dependencies ###
|
||||||
|
|
||||||
|
include(FeatureSummary)
|
||||||
|
find_package(Cargo REQUIRED)
|
||||||
|
find_package(Rust REQUIRED)
|
||||||
|
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
set(QT_MIN_VERSION "5.6.0")
|
||||||
|
find_package(Qt5 ${QT_MIN_VERSION} CONFIG
|
||||||
|
REQUIRED COMPONENTS
|
||||||
|
Widgets
|
||||||
|
)
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
find_package(RustQtBindingGenerator REQUIRED)
|
||||||
|
|
||||||
|
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
||||||
|
### build commands ###
|
||||||
|
|
||||||
|
SET(RUST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/rust")
|
||||||
|
SET(RUST_LIB "${RUST_DIR}/${RUST_TARGET_DIR}/librust.a")
|
||||||
|
|
||||||
|
# generate c++ and rust code from bindings.json
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${RUST_DIR}/src/interface.rs"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.h"
|
||||||
|
# if the cpp file is marked GENERATED, CMake will not check it for moc
|
||||||
|
# "${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.cpp"
|
||||||
|
COMMAND "${RustQtBindingGenerator_EXECUTABLE}"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/bindings.json"
|
||||||
|
DEPENDS bindings.json
|
||||||
|
)
|
||||||
|
|
||||||
|
# compile the rust code into a static library
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${RUST_LIB}"
|
||||||
|
COMMAND ${Cargo_EXECUTABLE} build ${RUST_BUILD_FLAG}
|
||||||
|
DEPENDS rust/src/lib.rs
|
||||||
|
rust/src/implementation.rs
|
||||||
|
rust/src/interface.rs
|
||||||
|
WORKING_DIRECTORY "${RUST_DIR}"
|
||||||
|
)
|
||||||
|
add_custom_target(rust_target DEPENDS "${RUST_LIB}")
|
||||||
|
|
||||||
|
list(APPEND Libs "${RUST_LIB}")
|
||||||
|
list(APPEND Libs Qt5::Widgets Threads::Threads ${CMAKE_DL_LIBS})
|
||||||
|
set(SRCS src/main.cpp src/Bindings.cpp)
|
||||||
|
add_executable(MyExe ${SRCS})
|
||||||
|
add_dependencies(MyExe rust_target)
|
||||||
|
target_link_libraries(MyExe ${Libs})
|
||||||
|
set_target_properties(MyExe PROPERTIES
|
||||||
|
CXX_STANDARD 11
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
Qt Widgets template project with Rust bindings
|
||||||
|
|
||||||
|
This is a template project for writing a Qt Widgets GUI on top of Rust code.
|
||||||
|
|
||||||
|
bindings.json defines the interface between the Qt and Rust code.
|
||||||
|
|
||||||
|
Build this code with
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"cppFile": "src/Bindings.cpp",
|
||||||
|
"rust": {
|
||||||
|
"dir": "rust",
|
||||||
|
"interfaceModule": "interface",
|
||||||
|
"implementationModule": "implementation"
|
||||||
|
},
|
||||||
|
"objects": {
|
||||||
|
"Simple": {
|
||||||
|
"type": "Object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "QString",
|
||||||
|
"write": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_program(Cargo_EXECUTABLE cargo)
|
||||||
|
execute_process(COMMAND "${Cargo_EXECUTABLE}" --version
|
||||||
|
OUTPUT_VARIABLE Cargo_VERSION_OUTPUT)
|
||||||
|
STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
|
||||||
|
Cargo_VERSION "${Cargo_VERSION_OUTPUT}")
|
||||||
|
find_package_handle_standard_args(Cargo
|
||||||
|
REQUIRED_VARS Cargo_EXECUTABLE
|
||||||
|
VERSION_VAR Cargo_VERSION)
|
||||||
|
mark_as_advanced(Cargo_EXECUTABLE)
|
|
@ -0,0 +1,10 @@
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_program(Rust_EXECUTABLE rustc)
|
||||||
|
execute_process(COMMAND "${Rust_EXECUTABLE}" --version
|
||||||
|
OUTPUT_VARIABLE Rust_VERSION_OUTPUT)
|
||||||
|
STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
|
||||||
|
Rust_VERSION "${Rust_VERSION_OUTPUT}")
|
||||||
|
find_package_handle_standard_args(Rust
|
||||||
|
REQUIRED_VARS Rust_EXECUTABLE
|
||||||
|
VERSION_VAR Rust_VERSION)
|
||||||
|
mark_as_advanced(Rust_EXECUTABLE)
|
|
@ -0,0 +1,5 @@
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_program(RustQtBindingGenerator_EXECUTABLE rust_qt_binding_generator)
|
||||||
|
find_package_handle_standard_args(RustQtBindingGenerator
|
||||||
|
REQUIRED_VARS RustQtBindingGenerator_EXECUTABLE)
|
||||||
|
mark_as_advanced(RustQtBindingGenerator_EXECUTABLE)
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "rust"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "*"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "rust"
|
||||||
|
crate-type = ["staticlib"]
|
|
@ -0,0 +1,26 @@
|
||||||
|
use interface::*;
|
||||||
|
|
||||||
|
pub struct Simple {
|
||||||
|
emit: SimpleEmitter,
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleTrait for Simple {
|
||||||
|
fn new(emit: SimpleEmitter) -> Simple {
|
||||||
|
Simple {
|
||||||
|
emit: emit,
|
||||||
|
message: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn emit(&self) -> &SimpleEmitter {
|
||||||
|
&self.emit
|
||||||
|
}
|
||||||
|
fn message(&self) -> &str {
|
||||||
|
"Hello World!"
|
||||||
|
}
|
||||||
|
fn set_message(&mut self, value: String) {
|
||||||
|
self.message = value;
|
||||||
|
self.emit.message_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/* generated by rust_qt_binding_generator */
|
||||||
|
#![allow(unknown_lints)]
|
||||||
|
#![allow(mutex_atomic, needless_pass_by_value)]
|
||||||
|
use libc::{c_int, c_void, uint8_t, uint16_t};
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::ptr::null;
|
||||||
|
|
||||||
|
use implementation::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct QString {
|
||||||
|
data: *const uint8_t,
|
||||||
|
len: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct QStringIn {
|
||||||
|
data: *const uint16_t,
|
||||||
|
len: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QStringIn {
|
||||||
|
fn convert(&self) -> String {
|
||||||
|
let data = unsafe { slice::from_raw_parts(self.data, self.len as usize) };
|
||||||
|
String::from_utf16_lossy(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for QString {
|
||||||
|
fn from(string: &'a str) -> QString {
|
||||||
|
QString {
|
||||||
|
len: string.len() as c_int,
|
||||||
|
data: string.as_ptr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a String> for QString {
|
||||||
|
fn from(string: &'a String) -> QString {
|
||||||
|
QString {
|
||||||
|
len: string.len() as c_int,
|
||||||
|
data: string.as_ptr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SimpleQObject {}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SimpleEmitter {
|
||||||
|
qobject: Arc<Mutex<*const SimpleQObject>>,
|
||||||
|
message_changed: fn(*const SimpleQObject),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for SimpleEmitter {}
|
||||||
|
|
||||||
|
impl SimpleEmitter {
|
||||||
|
fn clear(&self) {
|
||||||
|
*self.qobject.lock().unwrap() = null();
|
||||||
|
}
|
||||||
|
pub fn message_changed(&self) {
|
||||||
|
let ptr = *self.qobject.lock().unwrap();
|
||||||
|
if !ptr.is_null() {
|
||||||
|
(self.message_changed)(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SimpleTrait {
|
||||||
|
fn new(emit: SimpleEmitter) -> Self;
|
||||||
|
fn emit(&self) -> &SimpleEmitter;
|
||||||
|
fn message(&self) -> &str;
|
||||||
|
fn set_message(&mut self, value: String);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn simple_new(
|
||||||
|
simple: *mut SimpleQObject,
|
||||||
|
message_changed: fn(*const SimpleQObject),
|
||||||
|
) -> *mut Simple {
|
||||||
|
let simple_emit = SimpleEmitter {
|
||||||
|
qobject: Arc::new(Mutex::new(simple)),
|
||||||
|
message_changed: message_changed,
|
||||||
|
};
|
||||||
|
let d_simple = Simple::new(simple_emit);
|
||||||
|
Box::into_raw(Box::new(d_simple))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn simple_free(ptr: *mut Simple) {
|
||||||
|
Box::from_raw(ptr).emit().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn simple_message_get(
|
||||||
|
ptr: *const Simple,
|
||||||
|
p: *mut c_void,
|
||||||
|
set: fn(*mut c_void, QString),
|
||||||
|
) {
|
||||||
|
let data = (&*ptr).message();
|
||||||
|
set(p, data.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn simple_message_set(ptr: *mut Simple, v: QStringIn) {
|
||||||
|
(&mut *ptr).set_message(v.convert());
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
pub mod interface;
|
||||||
|
mod implementation;
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* generated by rust_qt_binding_generator */
|
||||||
|
#include "Bindings.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct qstring_t {
|
||||||
|
private:
|
||||||
|
const void* data;
|
||||||
|
int len;
|
||||||
|
public:
|
||||||
|
qstring_t(const QString& v):
|
||||||
|
data(static_cast<const void*>(v.utf16())),
|
||||||
|
len(v.size()) {
|
||||||
|
}
|
||||||
|
operator QString() const {
|
||||||
|
return QString::fromUtf8(static_cast<const char*>(data), len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
typedef void (*qstring_set)(QString*, qstring_t*);
|
||||||
|
void set_qstring(QString* v, qstring_t* val) {
|
||||||
|
*v = *val;
|
||||||
|
}
|
||||||
|
inline void simpleMessageChanged(Simple* o)
|
||||||
|
{
|
||||||
|
emit o->messageChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern "C" {
|
||||||
|
Simple::Private* simple_new(Simple*, void (*)(Simple*));
|
||||||
|
void simple_free(Simple::Private*);
|
||||||
|
void simple_message_get(const Simple::Private*, QString*, qstring_set);
|
||||||
|
void simple_message_set(Simple::Private*, qstring_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
Simple::Simple(bool /*owned*/, QObject *parent):
|
||||||
|
QObject(parent),
|
||||||
|
m_d(0),
|
||||||
|
m_ownsPrivate(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple::Simple(QObject *parent):
|
||||||
|
QObject(parent),
|
||||||
|
m_d(simple_new(this,
|
||||||
|
simpleMessageChanged)),
|
||||||
|
m_ownsPrivate(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple::~Simple() {
|
||||||
|
if (m_ownsPrivate) {
|
||||||
|
simple_free(m_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString Simple::message() const
|
||||||
|
{
|
||||||
|
QString v;
|
||||||
|
simple_message_get(m_d, &v, set_qstring);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
void Simple::setMessage(const QString& v) {
|
||||||
|
simple_message_set(m_d, v);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* generated by rust_qt_binding_generator */
|
||||||
|
#ifndef BINDINGS_H
|
||||||
|
#define BINDINGS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
class Simple;
|
||||||
|
|
||||||
|
class Simple : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
class Private;
|
||||||
|
private:
|
||||||
|
Private * m_d;
|
||||||
|
bool m_ownsPrivate;
|
||||||
|
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged FINAL)
|
||||||
|
explicit Simple(bool owned, QObject *parent);
|
||||||
|
public:
|
||||||
|
explicit Simple(QObject *parent = nullptr);
|
||||||
|
~Simple();
|
||||||
|
QString message() const;
|
||||||
|
void setMessage(const QString& v);
|
||||||
|
signals:
|
||||||
|
void messageChanged();
|
||||||
|
};
|
||||||
|
#endif // BINDINGS_H
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "Bindings.h"
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
Simple simple; // This is the Rust object
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setText(simple.message());
|
||||||
|
msgBox.connect(&msgBox, &QMessageBox::finished, &msgBox, []() {
|
||||||
|
QCoreApplication::quit();
|
||||||
|
});
|
||||||
|
msgBox.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
Loading…
Reference in New Issue