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