mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-12-18 22:16:33 +00:00
properties: support more properties
Support enum and flags properties in addition to numerous and string ones.
This commit is contained in:
parent
24e5e947d5
commit
39815eb8d9
5 changed files with 218 additions and 56 deletions
1
TODO.md
1
TODO.md
|
@ -30,6 +30,7 @@
|
||||||
- [x] Remove a port from a node if possible
|
- [x] Remove a port from a node if possible
|
||||||
- [ ] Implement graphview unit test
|
- [ ] Implement graphview unit test
|
||||||
- [x] add a css class for pad (presence always or sometimes)
|
- [x] add a css class for pad (presence always or sometimes)
|
||||||
|
- [ ] Add property to port to store some specific value(Caps)
|
||||||
|
|
||||||
### app
|
### app
|
||||||
|
|
||||||
|
|
|
@ -694,6 +694,11 @@ impl GPSApp {
|
||||||
node.update_properties(properties);
|
node.update_properties(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn element_property(&self, node_id: u32, property_name: &str) -> Option<String> {
|
||||||
|
let node = self.graphview.borrow().node(node_id).unwrap();
|
||||||
|
node.property(property_name)
|
||||||
|
}
|
||||||
|
|
||||||
fn clear_graph(&self) {
|
fn clear_graph(&self) {
|
||||||
let graph_view = self.graphview.borrow_mut();
|
let graph_view = self.graphview.borrow_mut();
|
||||||
graph_view.remove_all_nodes();
|
graph_view.remove_all_nodes();
|
||||||
|
|
|
@ -131,24 +131,22 @@ impl ElementInfo {
|
||||||
|
|
||||||
element_type
|
element_type
|
||||||
}
|
}
|
||||||
|
pub fn element_property(element_name: &str, property_name: &str) -> anyhow::Result<String> {
|
||||||
|
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
|
||||||
|
|
||||||
fn value_as_str(v: &glib::Value) -> Option<String> {
|
let factory = feature
|
||||||
match v.type_() {
|
.downcast::<gst::ElementFactory>()
|
||||||
glib::Type::I8 => Some(str_some_value!(v, i8).to_string()),
|
.expect("Unable to get the factory from the feature");
|
||||||
glib::Type::U8 => Some(str_some_value!(v, u8).to_string()),
|
let element = factory.create(None)?;
|
||||||
glib::Type::BOOL => Some(str_some_value!(v, bool).to_string()),
|
let value = element
|
||||||
glib::Type::I32 => Some(str_some_value!(v, i32).to_string()),
|
.try_property::<String>(property_name)
|
||||||
glib::Type::U32 => Some(str_some_value!(v, u32).to_string()),
|
.unwrap_or_default();
|
||||||
glib::Type::I64 => Some(str_some_value!(v, i64).to_string()),
|
Ok(value)
|
||||||
glib::Type::U64 => Some(str_some_value!(v, u64).to_string()),
|
|
||||||
glib::Type::F32 => Some(str_some_value!(v, f32).to_string()),
|
|
||||||
glib::Type::F64 => Some(str_some_value!(v, f64).to_string()),
|
|
||||||
glib::Type::STRING => str_opt_value!(v, String),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_properties(element_name: &str) -> anyhow::Result<HashMap<String, String>> {
|
pub fn element_properties(
|
||||||
|
element_name: &str,
|
||||||
|
) -> anyhow::Result<HashMap<String, glib::ParamSpec>> {
|
||||||
let mut properties_list = HashMap::new();
|
let mut properties_list = HashMap::new();
|
||||||
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
|
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
|
||||||
|
|
||||||
|
@ -159,19 +157,16 @@ impl ElementInfo {
|
||||||
let params = element.class().list_properties();
|
let params = element.class().list_properties();
|
||||||
|
|
||||||
for param in params.iter() {
|
for param in params.iter() {
|
||||||
if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
let value = element
|
||||||
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
.try_property::<String>(param.name())
|
||||||
{
|
.unwrap_or_default();
|
||||||
let value = element
|
GPS_INFO!(
|
||||||
.try_property::<String>(param.name())
|
"Property_name {}={} type={:?}",
|
||||||
.unwrap_or_else(|_| String::from(""));
|
param.name(),
|
||||||
GPS_INFO!("Property_name {}={}", param.name(), value);
|
value,
|
||||||
properties_list.insert(String::from(param.name()), value);
|
param.type_()
|
||||||
} else if let Some(value) = ElementInfo::value_as_str(param.default_value()) {
|
);
|
||||||
properties_list.insert(String::from(param.name()), value);
|
properties_list.insert(String::from(param.name()), param.clone());
|
||||||
} else {
|
|
||||||
GPS_INFO!("Unable to add property_name {}", param.name());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(properties_list)
|
Ok(properties_list)
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,8 +193,10 @@ impl Pipeline {
|
||||||
description.push_str(&format!("{} name={} ", node.name(), unique_name));
|
description.push_str(&format!("{} name={} ", node.name(), unique_name));
|
||||||
elements.insert(unique_name.clone(), unique_name.clone());
|
elements.insert(unique_name.clone(), unique_name.clone());
|
||||||
for (name, value) in node.properties().iter() {
|
for (name, value) in node.properties().iter() {
|
||||||
|
//This allow to have an index in front of a property such as an enum.
|
||||||
|
let value = value.split_once(':').unwrap_or((value, value));
|
||||||
if !node.hidden_property(name) {
|
if !node.hidden_property(name) {
|
||||||
description.push_str(&format!("{}={} ", name, value));
|
description.push_str(&format!("{}={} ", name, value.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
use crate::app::GPSApp;
|
use crate::app::GPSApp;
|
||||||
use crate::gps as GPS;
|
use crate::gps as GPS;
|
||||||
use crate::logger;
|
use crate::logger;
|
||||||
use crate::GPS_TRACE;
|
use crate::{GPS_INFO, GPS_TRACE};
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
||||||
|
@ -27,7 +27,154 @@ use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use gtk::{Box, Button, Dialog, Entry, Label};
|
use gtk::{Box, Button, CheckButton, ComboBoxText, Dialog, Entry, Label, Widget};
|
||||||
|
|
||||||
|
fn value_as_str(v: &glib::Value) -> Option<String> {
|
||||||
|
match v.type_() {
|
||||||
|
glib::Type::I8 => Some(str_some_value!(v, i8).to_string()),
|
||||||
|
glib::Type::U8 => Some(str_some_value!(v, u8).to_string()),
|
||||||
|
glib::Type::BOOL => Some(str_some_value!(v, bool).to_string()),
|
||||||
|
glib::Type::I32 => Some(str_some_value!(v, i32).to_string()),
|
||||||
|
glib::Type::U32 => Some(str_some_value!(v, u32).to_string()),
|
||||||
|
glib::Type::I64 => Some(str_some_value!(v, i64).to_string()),
|
||||||
|
glib::Type::U64 => Some(str_some_value!(v, u64).to_string()),
|
||||||
|
glib::Type::F32 => Some(str_some_value!(v, f32).to_string()),
|
||||||
|
glib::Type::F64 => Some(str_some_value!(v, f64).to_string()),
|
||||||
|
glib::Type::STRING => str_opt_value!(v, String),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_to_widget<F: Fn(String, String) + 'static>(
|
||||||
|
app: &GPSApp,
|
||||||
|
node_id: u32,
|
||||||
|
element_name: &str,
|
||||||
|
property_name: &str,
|
||||||
|
param: &glib::ParamSpec,
|
||||||
|
f: F,
|
||||||
|
) -> Option<Widget> {
|
||||||
|
match param.type_() {
|
||||||
|
_t if param.type_() == glib::ParamSpecBoolean::static_type() => {
|
||||||
|
let check_button = CheckButton::new();
|
||||||
|
check_button.set_widget_name(property_name);
|
||||||
|
GPS_TRACE!("add CheckBox property : {}", check_button.widget_name());
|
||||||
|
if let Some(value) = app.element_property(node_id, property_name) {
|
||||||
|
check_button.set_active(value.parse::<bool>().unwrap_or(false));
|
||||||
|
} else if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||||
|
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||||
|
{
|
||||||
|
if let Ok(value) = GPS::ElementInfo::element_property(element_name, param.name()) {
|
||||||
|
check_button.set_active(value.parse::<bool>().unwrap_or(false));
|
||||||
|
}
|
||||||
|
} else if let Some(value) = value_as_str(param.default_value()) {
|
||||||
|
check_button.set_active(value.parse::<bool>().unwrap_or(false));
|
||||||
|
}
|
||||||
|
check_button.connect_toggled(glib::clone!(@weak check_button => move |c| {
|
||||||
|
f(c.widget_name().to_string(), c.is_active().to_string() );
|
||||||
|
}));
|
||||||
|
Some(check_button.upcast::<Widget>())
|
||||||
|
}
|
||||||
|
t if [
|
||||||
|
glib::ParamSpecInt::static_type(),
|
||||||
|
glib::ParamSpecUInt::static_type(),
|
||||||
|
glib::ParamSpecInt64::static_type(),
|
||||||
|
glib::ParamSpecUInt64::static_type(),
|
||||||
|
glib::ParamSpecString::static_type(),
|
||||||
|
]
|
||||||
|
.contains(&t) =>
|
||||||
|
{
|
||||||
|
let entry = Entry::new();
|
||||||
|
entry.set_widget_name(property_name);
|
||||||
|
GPS_TRACE!("Add Edit property : {}", entry.widget_name());
|
||||||
|
if let Some(value) = app.element_property(node_id, property_name) {
|
||||||
|
entry.set_text(&value);
|
||||||
|
} else if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||||
|
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||||
|
{
|
||||||
|
if let Ok(value) = GPS::ElementInfo::element_property(element_name, param.name()) {
|
||||||
|
entry.set_text(&value);
|
||||||
|
}
|
||||||
|
} else if let Some(value) = value_as_str(param.default_value()) {
|
||||||
|
entry.set_text(&value);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.connect_changed(glib::clone!(@weak entry=> move |e| {
|
||||||
|
f(e.widget_name().to_string(), e.text().to_string())
|
||||||
|
}));
|
||||||
|
Some(entry.upcast::<Widget>())
|
||||||
|
}
|
||||||
|
t if [
|
||||||
|
glib::ParamSpecEnum::static_type(),
|
||||||
|
glib::ParamSpecFlags::static_type(),
|
||||||
|
]
|
||||||
|
.contains(&t) =>
|
||||||
|
{
|
||||||
|
let combo = ComboBoxText::new();
|
||||||
|
combo.set_widget_name(property_name);
|
||||||
|
GPS_TRACE!("add ComboBox property : {}", combo.widget_name());
|
||||||
|
if t.is_a(glib::ParamSpecEnum::static_type()) {
|
||||||
|
let param = param
|
||||||
|
.clone()
|
||||||
|
.downcast::<glib::ParamSpecEnum>()
|
||||||
|
.expect("Should be a ParamSpecEnum");
|
||||||
|
let enums = param.enum_class();
|
||||||
|
for value in enums.values() {
|
||||||
|
combo.append_text(&format!(
|
||||||
|
"{}:{}:{}",
|
||||||
|
value.value(),
|
||||||
|
value.nick(),
|
||||||
|
value.name()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else if t.is_a(glib::ParamSpecFlags::static_type()) {
|
||||||
|
let param = param
|
||||||
|
.clone()
|
||||||
|
.downcast::<glib::ParamSpecFlags>()
|
||||||
|
.expect("Should be a ParamSpecEnum");
|
||||||
|
let flags = param.flags_class();
|
||||||
|
for value in flags.values() {
|
||||||
|
combo.append_text(&format!(
|
||||||
|
"{}:{}: {}",
|
||||||
|
value.value(),
|
||||||
|
value.nick(),
|
||||||
|
value.name()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(value) = app.element_property(node_id, property_name) {
|
||||||
|
let value = value.split_once(':').unwrap_or(("0", ""));
|
||||||
|
//Retrieve the first value (index) from the property
|
||||||
|
combo.set_active(Some(value.0.parse::<u32>().unwrap_or(0)));
|
||||||
|
} else if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||||
|
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||||
|
{
|
||||||
|
if let Ok(value) = GPS::ElementInfo::element_property(element_name, param.name()) {
|
||||||
|
combo.set_active(Some(value.parse::<u32>().unwrap_or(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
combo.connect_changed(move |c| {
|
||||||
|
if let Some(text) = c.active_text() {
|
||||||
|
let text = text.to_string();
|
||||||
|
let fields: Vec<&str> = text.split(':').collect();
|
||||||
|
f(
|
||||||
|
c.widget_name().to_string(),
|
||||||
|
format!("{}:{}", fields[0], fields[1]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Some(combo.upcast::<Widget>())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
GPS_INFO!(
|
||||||
|
"Property not supported : name={} type={}",
|
||||||
|
property_name,
|
||||||
|
param.type_()
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32) {
|
pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32) {
|
||||||
let dialog: Dialog = app
|
let dialog: Dialog = app
|
||||||
|
@ -50,34 +197,46 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32)
|
||||||
properties_box.remove(&child);
|
properties_box.remove(&child);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, value) in properties {
|
let grid = gtk::Grid::builder()
|
||||||
let entry_box = Box::new(gtk::Orientation::Horizontal, 1);
|
.margin_start(6)
|
||||||
let name_box = Box::new(gtk::Orientation::Vertical, 6);
|
.margin_end(6)
|
||||||
let value_box = Box::new(gtk::Orientation::Vertical, 6);
|
.margin_top(6)
|
||||||
//Label
|
.margin_bottom(6)
|
||||||
let label = Label::new(Some(&name));
|
.halign(gtk::Align::Start)
|
||||||
label.set_hexpand(true);
|
.valign(gtk::Align::Center)
|
||||||
label.set_halign(gtk::Align::Start);
|
.row_spacing(6)
|
||||||
label.set_margin_start(4);
|
.column_spacing(100)
|
||||||
name_box.append(&label);
|
.width_request(100)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let mut properties: Vec<(&String, &glib::ParamSpec)> = properties.iter().collect();
|
||||||
|
properties.sort_by(|a, b| a.0.cmp(b.0));
|
||||||
|
let mut i = 0;
|
||||||
|
for (name, param) in properties {
|
||||||
//Entry
|
//Entry
|
||||||
let entry: Entry = Entry::new();
|
let widget = property_to_widget(
|
||||||
entry.set_text(&value);
|
app,
|
||||||
entry.set_hexpand(true);
|
node_id,
|
||||||
entry.set_halign(gtk::Align::End);
|
element_name,
|
||||||
entry.set_widget_name(&name);
|
name,
|
||||||
entry.connect_changed(
|
param,
|
||||||
glib::clone!(@weak entry, @strong update_properties => move |_| {
|
glib::clone!(@strong update_properties => move |name, value| {
|
||||||
GPS_TRACE!("property changed: {}:{}", entry.widget_name(), entry.text());
|
GPS_TRACE!("property changed: {}:{}", name, value);
|
||||||
update_properties.borrow_mut().insert(entry.widget_name().to_string(), entry.text().to_string());
|
update_properties.borrow_mut().insert(name, value);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
value_box.append(&entry);
|
if let Some(widget) = widget {
|
||||||
|
let label = Label::new(Some(name));
|
||||||
entry_box.append(&name_box);
|
label.set_hexpand(true);
|
||||||
entry_box.append(&value_box);
|
label.set_halign(gtk::Align::Start);
|
||||||
properties_box.append(&entry_box);
|
label.set_margin_start(4);
|
||||||
|
grid.attach(&label, 0, i, 1, 1);
|
||||||
|
grid.attach(&widget, 1, i, 1, 1);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
properties_box.append(&grid);
|
||||||
|
|
||||||
let properties_apply_btn: Button = app
|
let properties_apply_btn: Button = app
|
||||||
.builder
|
.builder
|
||||||
.object("button-apply-plugin-properties")
|
.object("button-apply-plugin-properties")
|
||||||
|
@ -94,6 +253,6 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32)
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
for p in update_properties.borrow().values() {
|
for p in update_properties.borrow().values() {
|
||||||
GPS_TRACE!("updated properties {}", p);
|
GPS_INFO!("updated properties {}", p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue