1 unstable release
0.1.1 | Jul 6, 2021 |
---|
#1618 in Development tools
16KB
208 lines
ruic
The Rust analog to Qt's uic.
Installation
cargo install ruic
Usage
USAGE:
ruic [FLAGS] [OPTIONS] [path]
FLAGS:
--all Load objects that would ordinarily be ignored
-f, --format Run rustfmt on output
-h, --help Prints help information
--no-recursive Do not recursively scan directories
-V, --version Prints version information
ARGS:
<path> Directory or file to scan [default: .]
OPTIONS:
-o, --out <out> Output file [default: path + "/uic.rs"]
-s, --suffix <suffix> Suffix to append to widget names, e.g. "Ui" to turn "App" into "AppUi" [default: ]
How It Works
ruic generates a single .rs file out of one or more Qt Designer .ui files. It does this by loading all the files into source code and generating a load
method for each one. Generated files require a few dependencies:
Example
Suppose you use Qt Designer to create the following file:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HelloWorld</class>
<widget class="QDialog" name="HelloWorld">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_HelloWorld">
<property name="text">
<string>Hello world!</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="SayHi"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Running ruic on it will output the following .rs file:
// This file is automatically generated.
use cpp_core::{CastInto, Ptr};
use qt_core::{QBox, QPtr};
use qt_ui_tools::QUiLoader;
use qt_widgets::*;
#[derive(Debug)]
pub struct HelloWorld {
pub widget: QBox<QDialog>,
pub say_hi: QPtr<QLineEdit>,
}
impl HelloWorld {
pub fn load<P: CastInto<Ptr<QWidget>>>(parent: P) -> Self {
unsafe {
let loader = QUiLoader::new_0a();
loader.set_language_change_enabled(true);
let bytes = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ui version=\"4.0\"><class>HelloWorld</class><widget class=\"QDialog\" name=\"HelloWorld\"><property name=\"geometry\"><rect><x>0</x><y>0</y><width>400</width><height>300</height></rect></property><property name=\"windowTitle\"><string>Dialog</string></property><layout class=\"QHBoxLayout\" name=\"horizontalLayout\"><item><widget class=\"QLabel\" name=\"label_HelloWorld\"><property name=\"text\"><string>Hello world!</string></property></widget></item><item><widget class=\"QLineEdit\" name=\"SayHi\"/></item></layout></widget><resources/><connections/></ui>".as_bytes();
let widget = loader.load_bytes_with_parent(bytes, parent);
assert!(!widget.is_null(), "invalid ui file");
Self {
say_hi: widget.find_child("SayHi").unwrap(),
widget: QBox::from_q_ptr(widget.into_q_ptr().dynamic_cast()),
}
}
}
}
Note: load
's safety is ensured by loading the entire content of the .ui file into source code.
To make use of this file, you could then write a Rust+Qt module along these lines:
use std::rc::Rc;
use cpp_core::{CastInto, Ptr};
use qt_widgets::QWidget;
use crate::uic;
pub struct HelloWorld {
ui: uic::HelloWorld,
}
impl HelloWorld {
fn new<P: CastInto<Ptr<QWidget>>>(parent: P) -> Rc<Self> {
let this = Rc::new(Self {
ui: uic::HelloWorld::load(parent),
});
unsafe { this.init() };
this
}
unsafe fn init(self: &Rc<Self>) {
/* add slot + signal connectors, etc. */
}
}
Alternatively, you could pass something like --suffix=Ui
to ruic in order to turn crate::uic::HelloWorld
into crate::uic::HelloWorldUi
, allowing you to import it directly without a name clash.
Note that in Rust+Qt, the way to create a parentless widget is to pass NullPtr as the parent.
Ignored Fields
By default, some fields are not loaded as variables into the struct:
- QLabels that are named "label", start with "label_", or end with "_label"
- QGroupBoxes
- QLayouts with children
If you want to include such fields, pass --all
to ruic on the command line. With --all
, the generated file from the example above would instead be:
// This file is automatically generated.
use cpp_core::{CastInto, Ptr};
use qt_core::{QBox, QPtr};
use qt_ui_tools::QUiLoader;
use qt_widgets::*;
#[derive(Debug)]
pub struct HelloWorld {
pub widget: QBox<QDialog>,
pub horizontal_layout: QPtr<QHBoxLayout>,
pub label_hello_world: QPtr<QLabel>,
pub say_hi: QPtr<QLineEdit>,
}
impl HelloWorld {
pub fn load<P: CastInto<Ptr<QWidget>>>(parent: P) -> Self {
unsafe {
let loader = QUiLoader::new_0a();
loader.set_language_change_enabled(true);
let bytes = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ui version=\"4.0\"><class>HelloWorld</class><widget class=\"QDialog\" name=\"HelloWorld\"><property name=\"geometry\"><rect><x>0</x><y>0</y><width>400</width><height>300</height></rect></property><property name=\"windowTitle\"><string>Dialog</string></property><layout class=\"QHBoxLayout\" name=\"horizontalLayout\"><item><widget class=\"QLabel\" name=\"label_HelloWorld\"><property name=\"text\"><string>Hello world!</string></property></widget></item><item><widget class=\"QLineEdit\" name=\"SayHi\"/></item></layout></widget><resources/><connections/></ui>".as_bytes();
let widget = loader.load_bytes_with_parent(bytes, parent);
assert!(!widget.is_null(), "invalid ui file");
Self {
horizontal_layout: widget.find_child("horizontalLayout").unwrap(),
label_hello_world: widget.find_child("label_HelloWorld").unwrap(),
say_hi: widget.find_child("SayHi").unwrap(),
widget: QBox::from_q_ptr(widget.into_q_ptr().dynamic_cast()),
}
}
}
}
Dependencies
~7MB
~127K SLoC