A table with system processes and a qml chart

master
Jos van den Oever 2017-08-24 01:55:37 +02:00
parent 8043efe34e
commit 2cc3d2ccf9
18 changed files with 1058 additions and 39 deletions

View File

@ -22,6 +22,17 @@ add_custom_command(
DEPENDS rust_qt_binding_generator tree.json
)
# generate c++ and rust code from processes.json
add_custom_command(
OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/rust/src/processes_interface.rs"
"${CMAKE_CURRENT_SOURCE_DIR}/rust/src/processes_types.rs"
"${CMAKE_CURRENT_SOURCE_DIR}/src/Processes.h"
# if the cpp file is marked GENERATED, CMake will not check it for moc
# "${CMAKE_CURRENT_SOURCE_DIR}/src/Processes.cpp"
COMMAND ${CMAKE_BINARY_DIR}/src/rust_qt_binding_generator "${CMAKE_CURRENT_SOURCE_DIR}/processes.json"
DEPENDS rust_qt_binding_generator processes.json
)
# generate c++ and rust code from time_series.json
add_custom_command(
OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/rust/src/time_series_interface.rs"
@ -47,6 +58,9 @@ add_custom_command(
rust/src/time_series_interface.rs
rust/src/time_series_implementation.rs
rust/src/time_series_types.rs
rust/src/processes_interface.rs
rust/src/processes_implementation.rs
rust/src/processes_types.rs
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/rust"
)
add_custom_target(rust_target DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/rust/${RUST_TARGET_DIR}/librust.a")
@ -65,7 +79,7 @@ if (Qt5Charts_FOUND)
endif()
set(Demo_SRCS src/main.cpp src/Fibonacci.cpp src/Tree.cpp src/TimeSeries.cpp
resource_file.qrc)
src/Processes.cpp resource_file.qrc)
add_executable(Demo ${Demo_SRCS})
add_dependencies(Demo rust_target)

38
demo/chart.qml Normal file
View File

@ -0,0 +1,38 @@
import QtQuick 2.6
import QtCharts 2.2
Item {
ChartView {
id: chart
anchors.fill: parent
legend.alignment: Qt.AlignBottom
antialiasing: true
DateTimeAxis {
id: axisX
titleText: "Date"
format: "MMM yyyy"
tickCount: 10
//alignment: Qt.AlignBottom
}
ValueAxis {
id: axisY
titleText: "Sunspots count"
}
LineSeries {
id: cpu
name: "CPU"
axisX: axisX
axisY: axisY
}
VXYModelMapper {
model: timeSeries
xColumn: 0
yColumn: 1
series: cpu
}
}
}

View File

@ -12,22 +12,35 @@ ApplicationWindow {
visible: true
ItemSelectionModel {
id: selectionModel
model: fsModel
model: sortedFileSystem
}
TabView {
id: tabView
anchors.fill: parent
onCountChanged: {
for (var i = 0; i < tabView.count; ++i) {
if (tabView.getTab(i).title == initialTab) {
tabView.currentIndex = i;
}
}
}
Tab {
title: "style"
ComboBox {
currentIndex: qtquickIndex
model: styles
textRole: "display"
onCurrentIndexChanged: {
if (currentIndex != qtquickIndex) {
widgets.currentIndex = currentIndex;
application.close();
Column {
anchors.fill: parent
anchors.margins: 5
ComboBox {
currentIndex: qtquickIndex
model: styles
textRole: "display"
onCurrentIndexChanged: {
if (currentIndex != qtquickIndex) {
widgets.currentIndex = currentIndex;
application.close();
}
}
}
Item {}
}
}
Tab {
@ -61,7 +74,9 @@ ApplicationWindow {
title: "tree"
TreeView {
id: treeView
model: sortedFsModel
model: sortedFileSystem
sortIndicatorVisible: true
alternatingRowColors: true
selection: selectionModel
TableViewColumn {
title: "Name"
@ -78,5 +93,65 @@ ApplicationWindow {
}
}
}
Tab {
title: "processes"
TreeView {
id: processView
model: processes
sortIndicatorVisible: true
alternatingRowColors: true
TableViewColumn {
title: "pid"
role: "pid"
}
TableViewColumn {
title: "name"
role: "name"
}
TableViewColumn {
title: "cpu"
role: "cpu"
}
onSortIndicatorColumnChanged: {
switch (processView.sortIndicatorColumn) {
case 0: model.sortRole = Qt.DisplayRole; break;
}
console.log(model.sortRole);
}
onSortIndicatorOrderChanged: {
console.log(model.sort);
model.sort(Qt.DisplayRole, processView.sortIndicatorOrderChanged);
}
}
}
Tab {
id: chartTab
title: "chart"
// Row {
// anchors.fill: parent
Text {
text: "YAYAYA"
anchors.left: parent.left
anchors.right: i.left
}
Item {
id: i
anchors.right: parent.right
Text {
anchors.centerIn: parent
text: "QtChart is not available.";
visible: chartLoader.status !== Loader.Ready
}
Loader {
width: 100
id: chartLoader
// anchors.fill: parent
source: "chart.qml"
}
}
// }
}
}
}

43
demo/processes.json Normal file
View File

@ -0,0 +1,43 @@
{
"cppFile": "src/Processes.cpp",
"rust": {
"dir": "rust",
"interfaceModule": "processes_interface",
"implementationModule": "processes_implementation",
"typesModule": "processes_types"
},
"objects": {
"Processes": {
"type": "UniformTree",
"properties": {
},
"itemProperties": {
"pid": {
"type": "quint32",
"roles": [ ["display"] ]
},
"name": {
"type": "QString",
"roles": [ [], ["display"] ]
},
"cpuUsage": {
"type": "float",
"roles": [ [], [], ["display"] ]
},
"memory": {
"type": "quint64",
"roles": [ [], [], [], ["display"] ]
},
"uid": {
"type": "quint32"
},
"cpuPercentage": {
"type": "quint8"
},
"cmd": {
"type": "QString"
}
}
}
}
}

View File

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>demo.qml</file>
<file>chart.qml</file>
<file>demo-qtquick2.qml</file>
</qresource>
</RCC>

View File

@ -4,6 +4,7 @@ version = "0.1.0"
[dependencies]
libc = "*"
sysinfo = "0.3"
[lib]
name = "rust"

View File

@ -1,4 +1,5 @@
extern crate libc;
extern crate sysinfo;
mod fibonacci_types;
mod fibonacci_implementation;
@ -11,3 +12,7 @@ pub mod interface;
mod time_series_types;
mod time_series_implementation;
pub mod time_series_interface;
mod processes_types;
mod processes_implementation;
pub mod processes_interface;

View File

@ -0,0 +1,147 @@
use types::*;
use processes_interface::*;
use sysinfo::*;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use libc::pid_t;
use std::{thread, time};
struct ProcessItem {
parent: Option<usize>,
row: usize,
tasks: Vec<usize>,
process: Process
}
#[derive (Default)]
struct ProcessTree {
top: Vec<usize>,
processes: Vec<ProcessItem>,
cpusum: f32
}
pub struct Processes {
emit: ProcessesEmitter,
model: ProcessesUniformTree,
p: ProcessTree,
incoming: Arc<Mutex<Option<ProcessTree>>>
}
fn handle_tasks(tasks: &HashMap<pid_t,Process>,
mut processes: &mut Vec<ProcessItem>,
parentPos: Option<usize>) -> (Vec<usize>, f32) {
let mut t = Vec::new();
let mut i = 0;
let mut cpusum = 0.0;
for process in tasks.values() {
let pos = processes.len();
t.push(pos);
processes.push(ProcessItem {
parent: parentPos,
row: i,
tasks: Vec::new(),
process: process.clone()
});
let (t, s) = handle_tasks(&process.tasks, &mut processes, Some(pos));
processes[pos].tasks = t;
cpusum += process.cpu_usage + s;
i += 1;
}
(t, cpusum)
}
fn update() -> ProcessTree {
let mut p = ProcessTree::default();
let mut sysinfo = System::new();
sysinfo.refresh_processes();
let (top, cpusum) = handle_tasks(sysinfo.get_process_list(),
&mut p.processes, None);
p.top = top;
p.cpusum = cpusum;
p
}
fn update_thread(emit: ProcessesEmitter, incoming: Arc<Mutex<Option<ProcessTree>>>) {
thread::spawn(move || {
let second = time::Duration::new(1, 0);
while true {
*incoming.lock().unwrap() = Some(update());
emit.new_data_ready(None);
thread::sleep(second);
}
});
}
impl ProcessesTrait for Processes {
fn create(emit: ProcessesEmitter, model: ProcessesUniformTree) -> Processes {
let mut p = Processes {
emit: emit.clone(),
model: model,
p: ProcessTree::default(),
incoming: Arc::new(Mutex::new(None))
};
update_thread(emit, p.incoming.clone());
p
}
fn emit(&self) -> &ProcessesEmitter {
&self.emit
}
fn row_count(&self, item: Option<usize>) -> usize {
if let Some(item) = item {
self.p.processes[item].tasks.len()
} else {
self.p.top.len()
}
}
fn index(&self, item: Option<usize>, row: usize) -> usize {
if let Some(item) = item {
self.p.processes[item].tasks[row]
} else {
self.p.top[row]
}
}
fn parent(&self, item: usize) -> Option<usize> {
self.p.processes[item].parent
}
fn can_fetch_more(&self, item: Option<usize>) -> bool {
if let Ok(ref incoming) = self.incoming.try_lock() {
incoming.is_some()
} else {
false
}
}
fn fetch_more(&mut self, item: Option<usize>) {
if let Ok(ref mut incoming) = self.incoming.try_lock() {
if let Some(more) = incoming.take() {
self.model.begin_reset_model();
self.p = more;
self.model.end_reset_model();
}
}
}
fn row(&self, item: usize) -> usize {
self.p.processes[item].row
}
fn pid(&self, item: usize) -> u32 {
self.p.processes[item].process.pid as u32
}
fn uid(&self, item: usize) -> u32 {
self.p.processes[item].process.uid as u32
}
fn cpu_usage(&self, item: usize) -> f32 {
self.p.processes[item].process.cpu_usage
}
fn cpu_percentage(&self, item: usize) -> u8 {
let cpu = self.p.processes[item].process.cpu_usage / self.p.cpusum;
(cpu * 100.0) as u8
}
fn memory(&self, item: usize) -> u64 {
self.p.processes[item].process.memory
}
fn name(&self, item: usize) -> String {
self.p.processes[item].process.name.clone()
}
fn cmd(&self, item: usize) -> String {
self.p.processes[item].process.cmd.join(" ")
}
}

View File

@ -0,0 +1,208 @@
/* generated by rust_qt_binding_generator */
#![allow(unknown_lints)]
#![allow(mutex_atomic, needless_pass_by_value)]
#![allow(unused_imports)]
use libc::{c_int, c_void};
use types::*;
use std::sync::{Arc, Mutex};
use std::ptr::null;
use processes_implementation::*;
pub struct ProcessesQObject {}
#[derive (Clone)]
pub struct ProcessesEmitter {
qobject: Arc<Mutex<*const ProcessesQObject>>,
new_data_ready: fn(*const ProcessesQObject, item: usize, valid: bool),
}
unsafe impl Send for ProcessesEmitter {}
impl ProcessesEmitter {
fn clear(&self) {
*self.qobject.lock().unwrap() = null();
}
pub fn new_data_ready(&self, item: Option<usize>) {
let ptr = *self.qobject.lock().unwrap();
if !ptr.is_null() {
(self.new_data_ready)(ptr, item.unwrap_or(13), item.is_some());
}
}
}
pub struct ProcessesUniformTree {
qobject: *const ProcessesQObject,
begin_reset_model: fn(*const ProcessesQObject),
end_reset_model: fn(*const ProcessesQObject),
begin_insert_rows: fn(*const ProcessesQObject, item: usize, valid: bool, usize, usize),
end_insert_rows: fn(*const ProcessesQObject),
begin_remove_rows: fn(*const ProcessesQObject, item: usize, valid: bool, usize, usize),
end_remove_rows: fn(*const ProcessesQObject),
}
impl ProcessesUniformTree {
pub fn begin_reset_model(&self) {
(self.begin_reset_model)(self.qobject);
}
pub fn end_reset_model(&self) {
(self.end_reset_model)(self.qobject);
}
pub fn begin_insert_rows(&self, item: Option<usize>, first: usize, last: usize) {
(self.begin_insert_rows)(self.qobject, item.unwrap_or(13), item.is_some(), first, last);
}
pub fn end_insert_rows(&self) {
(self.end_insert_rows)(self.qobject);
}
pub fn begin_remove_rows(&self, item: Option<usize>, first: usize, last: usize) {
(self.begin_remove_rows)(self.qobject, item.unwrap_or(13), item.is_some(), first, last);
}
pub fn end_remove_rows(&self) {
(self.end_remove_rows)(self.qobject);
}
}
pub trait ProcessesTrait {
fn create(emit: ProcessesEmitter, model: ProcessesUniformTree) -> Self;
fn emit(&self) -> &ProcessesEmitter;
fn row_count(&self, Option<usize>) -> usize;
fn can_fetch_more(&self, Option<usize>) -> bool { false }
fn fetch_more(&mut self, Option<usize>) {}
fn sort(&mut self, u8, SortOrder) {}
fn index(&self, item: Option<usize>, row: usize) -> usize;
fn parent(&self, item: usize) -> Option<usize>;
fn row(&self, item: usize) -> usize;
fn cmd(&self, item: usize) -> String;
fn cpu_percentage(&self, item: usize) -> u8;
fn cpu_usage(&self, item: usize) -> f32;
fn memory(&self, item: usize) -> u64;
fn name(&self, item: usize) -> String;
fn pid(&self, item: usize) -> u32;
fn uid(&self, item: usize) -> u32;
}
#[no_mangle]
pub extern "C" fn processes_new(qobject: *const ProcessesQObject,
new_data_ready: fn(*const ProcessesQObject, item: usize, valid: bool),
begin_reset_model: fn(*const ProcessesQObject),
end_reset_model: fn(*const ProcessesQObject),
begin_insert_rows: fn(*const ProcessesQObject, item: usize, valid: bool,
usize,
usize),
end_insert_rows: fn(*const ProcessesQObject),
begin_remove_rows: fn(*const ProcessesQObject, item: usize, valid: bool,
usize,
usize),
end_remove_rows: fn(*const ProcessesQObject))
-> *mut Processes {
let emit = ProcessesEmitter {
qobject: Arc::new(Mutex::new(qobject)),
new_data_ready: new_data_ready,
};
let model = ProcessesUniformTree {
qobject: qobject,
begin_reset_model: begin_reset_model,
end_reset_model: end_reset_model,
begin_insert_rows: begin_insert_rows,
end_insert_rows: end_insert_rows,
begin_remove_rows: begin_remove_rows,
end_remove_rows: end_remove_rows,
};
let d = Processes::create(emit, model);
Box::into_raw(Box::new(d))
}
#[no_mangle]
pub unsafe extern "C" fn processes_free(ptr: *mut Processes) {
Box::from_raw(ptr).emit().clear();
}
#[no_mangle]
pub unsafe extern "C" fn processes_row_count(ptr: *const Processes, item: usize, valid: bool) -> c_int {
if valid {
(&*ptr).row_count(Some(item)) as c_int
} else {
(&*ptr).row_count(None) as c_int
}
}
#[no_mangle]
pub unsafe extern "C" fn processes_can_fetch_more(ptr: *const Processes, item: usize, valid: bool) -> bool {
if valid {
(&*ptr).can_fetch_more(Some(item))
} else {
(&*ptr).can_fetch_more(None)
}
}
#[no_mangle]
pub unsafe extern "C" fn processes_fetch_more(ptr: *mut Processes, item: usize, valid: bool) {
if valid {
(&mut *ptr).fetch_more(Some(item))
} else {
(&mut *ptr).fetch_more(None)
}
}
#[no_mangle]
pub unsafe extern "C" fn processes_sort(ptr: *mut Processes, column: u8, order: SortOrder) {
(&mut *ptr).sort(column, order)
}
#[no_mangle]
pub unsafe extern "C" fn processes_index(ptr: *const Processes, item: usize, valid: bool, row: c_int) -> usize {
if !valid {
(&*ptr).index(None, row as usize)
} else {
(&*ptr).index(Some(item), row as usize)
}
}
#[no_mangle]
pub unsafe extern "C" fn processes_parent(ptr: *const Processes, index: usize) -> QModelIndex {
if let Some(parent) = (&*ptr).parent(index) {
QModelIndex::create((&*ptr).row(parent) as c_int, parent)
} else {
QModelIndex::invalid()
}
}
#[no_mangle]
pub unsafe extern "C" fn processes_row(ptr: *const Processes, item: usize) -> c_int {
(&*ptr).row(item) as c_int
}
#[no_mangle]
pub unsafe extern "C" fn processes_data_cmd(ptr: *const Processes, item: usize,
d: *mut c_void,
set: fn(*mut c_void, QString)) {
let data = (&*ptr).cmd(item);
set(d, QString::from(&data));
}
#[no_mangle]
pub unsafe extern "C" fn processes_data_cpu_percentage(ptr: *const Processes, item: usize) -> u8 {
(&*ptr).cpu_percentage(item).into()
}
#[no_mangle]
pub unsafe extern "C" fn processes_data_cpu_usage(ptr: *const Processes, item: usize) -> f32 {
(&*ptr).cpu_usage(item).into()
}
#[no_mangle]
pub unsafe extern "C" fn processes_data_memory(ptr: *const Processes, item: usize) -> u64 {
(&*ptr).memory(item).into()
}
#[no_mangle]
pub unsafe extern "C" fn processes_data_name(ptr: *const Processes, item: usize,
d: *mut c_void,
set: fn(*mut c_void, QString)) {
let data = (&*ptr).name(item);
set(d, QString::from(&data));
}
#[no_mangle]
pub unsafe extern "C" fn processes_data_pid(ptr: *const Processes, item: usize) -> u32 {
(&*ptr).pid(item).into()
}
#[no_mangle]
pub unsafe extern "C" fn processes_data_uid(ptr: *const Processes, item: usize) -> u32 {
(&*ptr).uid(item).into()
}

View File

@ -0,0 +1,104 @@
/* generated by rust_qt_binding_generator */
#![allow(dead_code)]
use std::slice;
use libc::{c_int, uint8_t, uint16_t};
#[repr(C)]
pub struct COption<T> {
data: T,
some: bool,
}
impl<T> From<Option<T>> for COption<T> where T: Default {
fn from(t: Option<T>) -> COption <T> {
if let Some(v) = t {
COption {
data: v,
some: true
}
} else {
COption {
data: T::default(),
some: false
}
}
}
}
#[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 {
pub 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 String> for QString {
fn from(string: &'a String) -> QString {
QString {
len: string.len() as c_int,
data: string.as_ptr(),
}
}
}
#[repr(C)]
pub struct QByteArray {
data: *const uint8_t,
len: c_int,
}
impl QByteArray {
pub fn convert(&self) -> Vec<u8> {
let data = unsafe { slice::from_raw_parts(self.data, self.len as usize) };
Vec::from(data)
}
}
impl<'a> From<&'a Vec<u8>> for QByteArray {
fn from(value: &'a Vec<u8>) -> QByteArray {
QByteArray {
len: value.len() as c_int,
data: value.as_ptr(),
}
}
}
#[repr(C)]
pub struct QModelIndex {
row: c_int,
internal_id: usize,
}
impl QModelIndex {
pub fn invalid() -> QModelIndex {
QModelIndex {
row: -1,
internal_id: 0,
}
}
pub fn create(row: c_int, id: usize) -> QModelIndex {
QModelIndex {
row: row,
internal_id: id,
}
}
}
#[repr(C)]
pub enum SortOrder {
Ascending = 0,
Descending = 1
}

278
demo/src/Processes.cpp Normal file
View File

@ -0,0 +1,278 @@
/* generated by rust_qt_binding_generator */
#include "Processes.h"
namespace {
template <typename T>
struct option {
public:
T value;
bool some;
operator QVariant() const {
if (some) {
return QVariant(value);
}
return QVariant();
}
};
struct qbytearray_t {
private:
const char* data;
int len;
public:
qbytearray_t(const QByteArray& v):
data(v.data()),
len(v.size()) {
}
operator QByteArray() const {
return QByteArray(data, len);
}
};
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);
}
};
struct qmodelindex_t {
int row;
quintptr id;
};
}
typedef void (*qstring_set)(QString*, qstring_t*);
void set_qstring(QString* v, qstring_t* val) {
*v = *val;
}
typedef void (*qbytearray_set)(QByteArray*, qbytearray_t*);
void set_qbytearray(QByteArray* v, qbytearray_t* val) {
*v = *val;
}
extern "C" {
void processes_data_cmd(const Processes::Private*, quintptr, QString*, qstring_set);
quint8 processes_data_cpu_percentage(const Processes::Private*, quintptr);
float processes_data_cpu_usage(const Processes::Private*, quintptr);
quint64 processes_data_memory(const Processes::Private*, quintptr);
void processes_data_name(const Processes::Private*, quintptr, QString*, qstring_set);
uint processes_data_pid(const Processes::Private*, quintptr);
uint processes_data_uid(const Processes::Private*, quintptr);
void processes_sort(Processes::Private*, unsigned char column, Qt::SortOrder order = Qt::AscendingOrder);
int processes_row_count(const Processes::Private*, quintptr, bool);
bool processes_can_fetch_more(const Processes::Private*, quintptr, bool);
void processes_fetch_more(Processes::Private*, quintptr, bool);
quintptr processes_index(const Processes::Private*, quintptr, bool, int);
qmodelindex_t processes_parent(const Processes::Private*, quintptr);
int processes_row(const Processes::Private*, quintptr);
}
int Processes::columnCount(const QModelIndex &) const
{
return 4;
}
bool Processes::hasChildren(const QModelIndex &parent) const
{
return rowCount(parent) > 0;
}
int Processes::rowCount(const QModelIndex &parent) const
{
if (parent.isValid() && parent.column() != 0) {
return 0;
}
return processes_row_count(d, parent.internalId(), parent.isValid());
}
QModelIndex Processes::index(int row, int column, const QModelIndex &parent) const
{
if (row < 0 || column < 0 || column >= 4) {
return QModelIndex();
}
if (parent.isValid() && parent.column() != 0) {
return QModelIndex();
}
if (row >= rowCount(parent)) {
return QModelIndex();
}
const quintptr id = processes_index(d, parent.internalId(), parent.isValid(), row);
return createIndex(row, column, id);
}
QModelIndex Processes::parent(const QModelIndex &index) const
{
if (!index.isValid()) {
return QModelIndex();
}
const qmodelindex_t parent = processes_parent(d, index.internalId());
return parent.row >= 0 ?createIndex(parent.row, 0, parent.id) :QModelIndex();
}
bool Processes::canFetchMore(const QModelIndex &parent) const
{
if (parent.isValid() && parent.column() != 0) {
return false;
}
return processes_can_fetch_more(d, parent.internalId(), parent.isValid());
}
void Processes::fetchMore(const QModelIndex &parent)
{
processes_fetch_more(d, parent.internalId(), parent.isValid());
}
void Processes::sort(int column, Qt::SortOrder order)
{
processes_sort(d, column, order);
}
Qt::ItemFlags Processes::flags(const QModelIndex &i) const
{
auto flags = QAbstractItemModel::flags(i);
return flags;
}
QVariant Processes::data(const QModelIndex &index, int role) const
{
QVariant v;
QString s;
QByteArray b;
switch (index.column()) {
case 0:
switch (role) {
case Qt::UserRole + 0:
processes_data_cmd(d, index.internalId(), &s, set_qstring);
if (!s.isNull()) v.setValue<QString>(s);
break;
case Qt::UserRole + 1:
v = processes_data_cpu_percentage(d, index.internalId());
break;
case Qt::UserRole + 2:
v = processes_data_cpu_usage(d, index.internalId());
break;
case Qt::UserRole + 3:
v = processes_data_memory(d, index.internalId());
break;
case Qt::UserRole + 4:
processes_data_name(d, index.internalId(), &s, set_qstring);
if (!s.isNull()) v.setValue<QString>(s);
break;
case Qt::DisplayRole:
case Qt::UserRole + 5:
v = processes_data_pid(d, index.internalId());
break;
case Qt::UserRole + 6:
v = processes_data_uid(d, index.internalId());
break;
}
break;
case 1:
switch (role) {
case Qt::DisplayRole:
case Qt::UserRole + 4:
processes_data_name(d, index.internalId(), &s, set_qstring);
if (!s.isNull()) v.setValue<QString>(s);
break;
}
break;
case 2:
switch (role) {
case Qt::DisplayRole:
case Qt::UserRole + 2:
v = processes_data_cpu_usage(d, index.internalId());
break;
}
break;
case 3:
switch (role) {
case Qt::DisplayRole:
case Qt::UserRole + 3:
v = processes_data_memory(d, index.internalId());
break;
}
break;
}
return v;
}
QHash<int, QByteArray> Processes::roleNames() const {
QHash<int, QByteArray> names = QAbstractItemModel::roleNames();
names.insert(Qt::UserRole + 0, "cmd");
names.insert(Qt::UserRole + 1, "cpuPercentage");
names.insert(Qt::UserRole + 2, "cpuUsage");
names.insert(Qt::UserRole + 3, "memory");
names.insert(Qt::UserRole + 4, "name");
names.insert(Qt::UserRole + 5, "pid");
names.insert(Qt::UserRole + 6, "uid");
return names;
}
bool Processes::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool set = false;
if (set) {
emit dataChanged(index, index, QVector<int>() << role);
}
return set;
}
extern "C" {
Processes::Private* processes_new(Processes*,
void (*)(const Processes*, quintptr, bool),
void (*)(Processes*),
void (*)(Processes*),
void (*)(Processes*, option<quintptr>, int, int),
void (*)(Processes*),
void (*)(Processes*, option<quintptr>, int, int),
void (*)(Processes*));
void processes_free(Processes::Private*);
};
Processes::Processes(QObject *parent):
QAbstractItemModel(parent),
d(processes_new(this,
[](const Processes* o, quintptr id, bool valid) {
if (valid) {
int row = processes_row(o->d, id);
emit o->newDataReady(o->createIndex(row, 0, id));
} else {
emit o->newDataReady(QModelIndex());
}
},
[](Processes* o) {
o->beginResetModel();
},
[](Processes* o) {
o->endResetModel();
},
[](Processes* o, option<quintptr> id, int first, int last) {
if (id.some) {
int row = processes_row(o->d, id.value);
o->beginInsertRows(o->createIndex(row, 0, id.value), first, last);
} else {
o->beginInsertRows(QModelIndex(), first, last);
}
},
[](Processes* o) {
o->endInsertRows();
},
[](Processes* o, option<quintptr> id, int first, int last) {
if (id.some) {
int row = processes_row(o->d, id.value);
o->beginRemoveRows(o->createIndex(row, 0, id.value), first, last);
} else {
o->beginRemoveRows(QModelIndex(), first, last);
}
},
[](Processes* o) {
o->endRemoveRows();
}
)) {
connect(this, &Processes::newDataReady, this, [this](const QModelIndex& i) {
fetchMore(i);
}, Qt::QueuedConnection);
}
Processes::~Processes() {
processes_free(d);
}

37
demo/src/Processes.h Normal file
View File

@ -0,0 +1,37 @@
/* generated by rust_qt_binding_generator */
#ifndef PROCESSES_H
#define PROCESSES_H
#include <QObject>
#include <QAbstractItemModel>
class Processes : public QAbstractItemModel
{
Q_OBJECT
public:
class Private;
private:
Private * const d;
public:
explicit Processes(QObject *parent = nullptr);
~Processes();
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
QHash<int, QByteArray> roleNames() const override;
signals:
// new data is ready to be made available to the model with fetchMore()
void newDataReady(const QModelIndex &parent) const;
signals:
private:
};
#endif // PROCESSES_H

View File

@ -1,6 +1,7 @@
#include "Tree.h"
#include "Fibonacci.h"
#include "TimeSeries.h"
#include "Processes.h"
#ifdef QT_CHARTS_LIB
#include <QtCharts>
@ -39,8 +40,10 @@ struct Models {
QStringListModel styles;
Fibonacci fibonacci;
FibonacciList fibonacciList;
Tree tree;
QSortFilterProxyModel sortedTree;
Tree fileSystem;
QSortFilterProxyModel sortedFileSystem;
Processes processes;
QSortFilterProxyModel sortedProcesses;
TimeSeries timeSeries;
};
@ -74,17 +77,21 @@ void copyWindowGeometry(QWidget* w, QQmlContext* c) {
}
}
void createQtQuick(const QString& name, const QString& qml, Models* models, QWidget* widgets) {
void createQtQuick(const QString& name, const QString& qml, Models* models,
QWidget* widgets, const QString& initialTab) {
QQmlApplicationEngine* engine = new QQmlApplicationEngine();
QQmlContext* c = engine->rootContext();
c->setContextProperty("fsModel", &models->tree);
c->setContextProperty("sortedFsModel", &models->sortedTree);
c->setContextProperty("styles", &models->styles);
c->setContextProperty("fibonacci", &models->fibonacci);
c->setContextProperty("fibonacciList", &models->fibonacciList);
c->setContextProperty("styles", &models->styles);
c->setContextProperty("sortedFileSystem", &models->sortedFileSystem);
c->setContextProperty("processes", &models->sortedProcesses);
c->setContextProperty("timeSeries", &models->timeSeries);
c->setContextProperty("widgets", widgets);
c->setContextProperty("qtquickIndex",
QVariant(models->styles.stringList().indexOf(name)));
c->setContextProperty("initialTab", initialTab);
copyWindowGeometry(widgets, engine->rootContext());
engine->load(QUrl(qml));
}
@ -111,10 +118,10 @@ QComboBox* createStyleComboBox(Models* models) {
return box;
}
QWidget* createStyleTab(Models* models, QWidget* tabs) {
QComboBox* box = createStyleComboBox(models);
QWidget* createStyleTab(Models* models, QWidget* tabs, QComboBox* box,
const QString& initialTab) {
QRect windowRect;
box->connect(box, &QComboBox::currentTextChanged, box, [windowRect, box, tabs, models](const QString &text) mutable {
auto f = [windowRect, box, tabs, models, initialTab](const QString &text) mutable {
QWindow* window = getWindow(tabs);
bool visible = tabs->isVisible();
if (text.startsWith("QWidgets ")) {
@ -138,13 +145,16 @@ QWidget* createStyleTab(Models* models, QWidget* tabs) {
#ifdef QTQUICKCONTROLS2
if (text == "QtQuick Controls 2") {
createQtQuick("QtQuick Controls 2", "qrc:///demo-qtquick2.qml",
models, box);
models, box, initialTab);
} else
#endif
createQtQuick("QtQuick", "qrc:///demo.qml", models, box);
createQtQuick("QtQuick", "qrc:///demo.qml", models, box, initialTab);
#endif
}
});
};
box->connect(box, &QComboBox::currentTextChanged, box, f);
// box->setCurrentText(style);
// f(style);
return box;
}
@ -188,14 +198,25 @@ QWidget* createTreeTab(Models* models) {
QTreeView* view = new QTreeView();
view->setUniformRowHeights(true);
view->setSortingEnabled(true);
view->setModel(&models->sortedTree);
auto root = models->sortedTree.index(0, 0);
view->setModel(&models->sortedFileSystem);
auto root = models->sortedFileSystem.index(0, 0);
view->expand(root);
view->sortByColumn(0, Qt::AscendingOrder);
view->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
return view;
}
QWidget* createProcessesTab(Models* models) {
QTreeView* view = new QTreeView();
view->setUniformRowHeights(true);
view->setSortingEnabled(true);
view->setModel(&models->sortedProcesses);
auto root = models->sortedFileSystem.index(0, 0);
view->expand(root);
view->sortByColumn(0, Qt::AscendingOrder);
return view;
}
#ifdef QT_CHARTS_LIB
using namespace QtCharts;
@ -237,18 +258,27 @@ QWidget* createChartTab(Models* models) {
}
#endif
void createWidgets(Models* models) {
void createWidgets(Models* models, const QString& initialStyle,
const QString& initialTab) {
QTabWidget* tabs = new QTabWidget();
tabs->addTab(createStyleTab(models, tabs), "style");
QComboBox* box = createStyleComboBox(models);
tabs->addTab(createStyleTab(models, tabs, box, initialTab), "style");
tabs->addTab(createObjectTab(models), "object");
tabs->addTab(createListTab(models), "list");
tabs->addTab(createTreeTab(models), "tree");
tabs->addTab(createProcessesTab(models), "processes");
#ifdef QT_CHARTS_LIB
tabs->addTab(createChartTab(models), "chart");
#endif
tabs->setMinimumSize(QSize(500, 500));
tabs->show();
box->setCurrentText(initialStyle);
for (int i = 0; i < tabs->count(); ++i) {
if (tabs->tabText(i) == initialTab) {
tabs->setCurrentIndex(i);
}
}
}
int main (int argc, char *argv[])
@ -256,20 +286,38 @@ int main (int argc, char *argv[])
QApplication app(argc, argv);
#ifdef QT_QUICK_LIB
qmlRegisterType<QSortFilterProxyModel>("org.qtproject.example", 1, 0, "SortFilterProxyModel");
qmlRegisterType<Fibonacci>("rust", 1, 0, "Fibonacci");
qmlRegisterType<FibonacciList>("rust", 1, 0, "FibonacciList");
#endif
Models models;
Tree& model = models.tree;
QSortFilterProxyModel& sortedModel = models.sortedTree;
model.setPath("/");
sortedModel.setSourceModel(&model);
sortedModel.setDynamicSortFilter(true);
QCommandLineParser parser;
parser.setApplicationDescription("Demo application for Qt and RUst");
parser.addHelpOption();
parser.addVersionOption();
// --initial-style
QCommandLineOption initialStyleOption(QStringList()
<< "initial-style",
QCoreApplication::translate("main", "Initial widget style."),
QCoreApplication::translate("main", "style"));
parser.addOption(initialStyleOption);
// --initial-tab
QCommandLineOption initialTabOption(QStringList()
<< "initial-tab",
QCoreApplication::translate("main", "Initial tab."),
QCoreApplication::translate("main", "tab"));
parser.addOption(initialTabOption);
parser.process(app);
Models models;
models.fileSystem.setPath("/");
models.sortedFileSystem.setSourceModel(&models.fileSystem);
models.sortedFileSystem.setDynamicSortFilter(true);
models.sortedProcesses.setSourceModel(&models.processes);
models.sortedProcesses.setDynamicSortFilter(true);
createWidgets(&models, parser.value(initialStyleOption),
parser.value(initialTabOption));
createWidgets(&models);
return app.exec();
}

2
dev
View File

@ -14,4 +14,4 @@ else
charts="qt5.qtcharts libsForQt5.kirigami_2"
fi
nix-shell -p qtcreator kmail cmake ninja gcc rustc cargo qt5.full extra-cmake-modules kdeFrameworks.kwidgetsaddons kdeFrameworks.kcoreaddons kdeFrameworks.ki18n appstream cmakeCurses kdeFrameworks.ktexteditor qt5.qtquickcontrols2 $charts
nix-shell -p kdevelop qtcreator qt5.qtsensors qt5.qtdoc cmake ninja gcc rustc cargo qt5.full extra-cmake-modules kdeFrameworks.kwidgetsaddons kdeFrameworks.kcoreaddons kdeFrameworks.ki18n appstream cmakeCurses kdeFrameworks.ktexteditor qt5.qtquickcontrols2 $charts

View File

@ -3,8 +3,6 @@
#include <QFile>
#include <QTextStream>
static QTextStream err;
inline QString snakeCase(const QString& name) {
return name.left(1).toLower() + name.mid(1)
.replace(QRegExp("([A-Z])"), "_\\1").toLower();
@ -36,6 +34,7 @@ public:
void write() const {
QFile file(path);
if (!file.open(QIODevice::WriteOnly)) {
QTextStream err(stderr);
err << QCoreApplication::translate("main",
"Cannot write %1.\n").arg(file.fileName());
err.flush();

View File

@ -27,6 +27,7 @@ int main(int argc, char *argv[]) {
const QStringList args = parser.positionalArguments();
if (args.isEmpty()) {
QTextStream err(stderr);
err << QCoreApplication::translate("main",
"Configuration file is missing.\n");
return 1;

View File

@ -20,6 +20,13 @@ const QMap<BindingType, BindingTypeProperties>& bindingTypeProperties() {
if (p.empty()) {
QMap<BindingType, BindingTypeProperties> f;
f.insert(BindingType::Bool, simpleType("bool", "true"));
f.insert(BindingType::UChar, {
.name = "quint8",
.cppSetType = "quint8",
.cSetType = "quint8",
.rustType = "u8",
.rustTypeInit = "0",
});
f.insert(BindingType::Int, {
.name = "qint32",
.cppSetType = "qint32",
@ -41,6 +48,13 @@ const QMap<BindingType, BindingTypeProperties>& bindingTypeProperties() {
.rustType = "u64",
.rustTypeInit = "0"
});
f.insert(BindingType::Float, {
.name = "float",
.cppSetType = "float",
.cSetType = "float",
.rustType = "f32",
.rustTypeInit = "0.0"
});
f.insert(BindingType::QString, {
.name = "QString",
.cppSetType = "const QString&",
@ -68,6 +82,7 @@ BindingTypeProperties parseBindingType(const QString& value) {
return i.value();
}
}
QTextStream err(stderr);
err << QCoreApplication::translate("main",
"'%1' is not a supported type. Try one of\n").arg(value);
for (auto i: bindingTypeProperties()) {
@ -94,6 +109,7 @@ Qt::ItemDataRole parseItemDataRole(const QString& s) {
if (v >= 0) {
return (Qt::ItemDataRole)v;
}
QTextStream err(stderr);
err << QCoreApplication::translate("main",
"%1 is not a valid role name.\n").arg(s);
err.flush();
@ -134,6 +150,7 @@ parseObject(const QString& name, const QJsonObject& json) {
for (const QString& key: properties.keys()) {
o.properties.append(parseProperty(key, properties[key].toObject()));
}
QTextStream err(stderr);
const QJsonObject& itemProperties = json.value("itemProperties").toObject();
if (o.type != ObjectType::Object && itemProperties.size() == 0) {
err << QCoreApplication::translate("main",
@ -159,6 +176,7 @@ Configuration
parseConfiguration(const QString& path) {
QFile configurationFile(path);
const QDir base = QFileInfo(configurationFile).dir();
QTextStream err(stderr);
if (!configurationFile.open(QIODevice::ReadOnly)) {
err << QCoreApplication::translate("main",
"Cannot read %1.\n").arg(configurationFile.fileName());

View File

@ -12,9 +12,11 @@ enum class ObjectType {
enum class BindingType {
Bool,
UChar,
Int,
UInt,
ULongLong,
Float,
QString,
QByteArray
};