mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-11-15 21:31:03 +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
|
||||
- [ ] Implement graphview unit test
|
||||
- [x] add a css class for pad (presence always or sometimes)
|
||||
- [ ] Add property to port to store some specific value(Caps)
|
||||
|
||||
### app
|
||||
|
||||
|
|
|
@ -694,6 +694,11 @@ impl GPSApp {
|
|||
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) {
|
||||
let graph_view = self.graphview.borrow_mut();
|
||||
graph_view.remove_all_nodes();
|
||||
|
|
|
@ -131,24 +131,22 @@ impl ElementInfo {
|
|||
|
||||
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> {
|
||||
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,
|
||||
}
|
||||
let factory = feature
|
||||
.downcast::<gst::ElementFactory>()
|
||||
.expect("Unable to get the factory from the feature");
|
||||
let element = factory.create(None)?;
|
||||
let value = element
|
||||
.try_property::<String>(property_name)
|
||||
.unwrap_or_default();
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
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 feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
|
||||
|
||||
|
@ -159,19 +157,16 @@ impl ElementInfo {
|
|||
let params = element.class().list_properties();
|
||||
|
||||
for param in params.iter() {
|
||||
if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||
{
|
||||
let value = element
|
||||
.try_property::<String>(param.name())
|
||||
.unwrap_or_else(|_| String::from(""));
|
||||
GPS_INFO!("Property_name {}={}", param.name(), value);
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else if let Some(value) = ElementInfo::value_as_str(param.default_value()) {
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else {
|
||||
GPS_INFO!("Unable to add property_name {}", param.name());
|
||||
}
|
||||
let value = element
|
||||
.try_property::<String>(param.name())
|
||||
.unwrap_or_default();
|
||||
GPS_INFO!(
|
||||
"Property_name {}={} type={:?}",
|
||||
param.name(),
|
||||
value,
|
||||
param.type_()
|
||||
);
|
||||
properties_list.insert(String::from(param.name()), param.clone());
|
||||
}
|
||||
Ok(properties_list)
|
||||
}
|
||||
|
|
|
@ -193,8 +193,10 @@ impl Pipeline {
|
|||
description.push_str(&format!("{} name={} ", node.name(), unique_name));
|
||||
elements.insert(unique_name.clone(), unique_name.clone());
|
||||
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) {
|
||||
description.push_str(&format!("{}={} ", name, value));
|
||||
description.push_str(&format!("{}={} ", name, value.1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
use crate::app::GPSApp;
|
||||
use crate::gps as GPS;
|
||||
use crate::logger;
|
||||
use crate::GPS_TRACE;
|
||||
use crate::{GPS_INFO, GPS_TRACE};
|
||||
use gtk::glib;
|
||||
use gtk::prelude::*;
|
||||
|
||||
|
@ -27,7 +27,154 @@ use std::cell::RefCell;
|
|||
use std::collections::HashMap;
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
for (name, value) in properties {
|
||||
let entry_box = Box::new(gtk::Orientation::Horizontal, 1);
|
||||
let name_box = Box::new(gtk::Orientation::Vertical, 6);
|
||||
let value_box = Box::new(gtk::Orientation::Vertical, 6);
|
||||
//Label
|
||||
let label = Label::new(Some(&name));
|
||||
label.set_hexpand(true);
|
||||
label.set_halign(gtk::Align::Start);
|
||||
label.set_margin_start(4);
|
||||
name_box.append(&label);
|
||||
let grid = gtk::Grid::builder()
|
||||
.margin_start(6)
|
||||
.margin_end(6)
|
||||
.margin_top(6)
|
||||
.margin_bottom(6)
|
||||
.halign(gtk::Align::Start)
|
||||
.valign(gtk::Align::Center)
|
||||
.row_spacing(6)
|
||||
.column_spacing(100)
|
||||
.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
|
||||
let entry: Entry = Entry::new();
|
||||
entry.set_text(&value);
|
||||
entry.set_hexpand(true);
|
||||
entry.set_halign(gtk::Align::End);
|
||||
entry.set_widget_name(&name);
|
||||
entry.connect_changed(
|
||||
glib::clone!(@weak entry, @strong update_properties => move |_| {
|
||||
GPS_TRACE!("property changed: {}:{}", entry.widget_name(), entry.text());
|
||||
update_properties.borrow_mut().insert(entry.widget_name().to_string(), entry.text().to_string());
|
||||
let widget = property_to_widget(
|
||||
app,
|
||||
node_id,
|
||||
element_name,
|
||||
name,
|
||||
param,
|
||||
glib::clone!(@strong update_properties => move |name, value| {
|
||||
GPS_TRACE!("property changed: {}:{}", name, value);
|
||||
update_properties.borrow_mut().insert(name, value);
|
||||
}),
|
||||
);
|
||||
value_box.append(&entry);
|
||||
|
||||
entry_box.append(&name_box);
|
||||
entry_box.append(&value_box);
|
||||
properties_box.append(&entry_box);
|
||||
if let Some(widget) = widget {
|
||||
let label = Label::new(Some(name));
|
||||
label.set_hexpand(true);
|
||||
label.set_halign(gtk::Align::Start);
|
||||
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
|
||||
.builder
|
||||
.object("button-apply-plugin-properties")
|
||||
|
@ -94,6 +253,6 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32)
|
|||
|
||||
dialog.show();
|
||||
for p in update_properties.borrow().values() {
|
||||
GPS_TRACE!("updated properties {}", p);
|
||||
GPS_INFO!("updated properties {}", p);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue