mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-11-25 10:30:59 +00:00
plugindialogs: rename module and support properties in element
rename pluginlist module to plugindialogs Add display_plugin_properties to modify properties from an element. Add a way to modify the location if element is URI_TYPE_SRC Able to remove a node from the graph
This commit is contained in:
parent
6c7dccd450
commit
6756b34919
9 changed files with 310 additions and 53 deletions
2
TODO.md
2
TODO.md
|
@ -26,7 +26,9 @@ TODO:
|
||||||
## bugs
|
## bugs
|
||||||
|
|
||||||
- [] crash with x11 on contextual menu
|
- [] crash with x11 on contextual menu
|
||||||
|
- [] check that element exists before creating it on file load.
|
||||||
|
|
||||||
## Code cleanup
|
## Code cleanup
|
||||||
|
|
||||||
[] remove useless code from graphview
|
[] remove useless code from graphview
|
||||||
|
[] Move render to a specific module
|
||||||
|
|
117
src/app.rs
117
src/app.rs
|
@ -25,11 +25,12 @@ use gtk::{
|
||||||
FileChooserDialog, PopoverMenu, ResponseType, Statusbar, Viewport,
|
FileChooserDialog, PopoverMenu, ResponseType, Statusbar, Viewport,
|
||||||
};
|
};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::{error, ops};
|
use std::{error, ops};
|
||||||
|
|
||||||
use crate::pipeline::{Pipeline, PipelineState};
|
use crate::pipeline::{Pipeline, PipelineState};
|
||||||
use crate::pluginlist;
|
use crate::plugindialogs;
|
||||||
|
|
||||||
use crate::graphmanager::{GraphView, Node};
|
use crate::graphmanager::{GraphView, Node};
|
||||||
|
|
||||||
|
@ -115,6 +116,47 @@ impl GPSApp {
|
||||||
app.drop();
|
app.drop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_file_from_dialog<F: Fn(GPSApp, String) + 'static>(app: &GPSApp, save: bool, f: F) {
|
||||||
|
let mut message = "Open file";
|
||||||
|
let mut ok_button = "Open";
|
||||||
|
let cancel_button = "Cancel";
|
||||||
|
let mut action = FileChooserAction::Open;
|
||||||
|
if save {
|
||||||
|
message = "Save file";
|
||||||
|
ok_button = "Save";
|
||||||
|
action = FileChooserAction::Save;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_chooser: FileChooserDialog = FileChooserDialog::new(
|
||||||
|
Some(message),
|
||||||
|
Some(&app.window),
|
||||||
|
action,
|
||||||
|
&[
|
||||||
|
(ok_button, ResponseType::Ok),
|
||||||
|
(cancel_button, ResponseType::Cancel),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let app_weak = app.downgrade();
|
||||||
|
file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| {
|
||||||
|
let app = upgrade_weak!(app_weak);
|
||||||
|
if response == ResponseType::Ok {
|
||||||
|
let file = d.file().expect("Couldn't get file");
|
||||||
|
let filename = String::from(
|
||||||
|
file.path()
|
||||||
|
.expect("Couldn't get file path")
|
||||||
|
.to_str()
|
||||||
|
.expect("unable to convert to string"),
|
||||||
|
);
|
||||||
|
f(app, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
d.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
file_chooser.show();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn show_error_dialog(fatal: bool, message: &str) {
|
pub fn show_error_dialog(fatal: bool, message: &str) {
|
||||||
let app = gio::Application::default()
|
let app = gio::Application::default()
|
||||||
.expect("No default application")
|
.expect("No default application")
|
||||||
|
@ -165,25 +207,11 @@ impl GPSApp {
|
||||||
// Add a dialog to open the graph
|
// Add a dialog to open the graph
|
||||||
action.connect_activate(glib::clone!(@weak window => move |_, _| {
|
action.connect_activate(glib::clone!(@weak window => move |_, _| {
|
||||||
let app = upgrade_weak!(app_weak);
|
let app = upgrade_weak!(app_weak);
|
||||||
let file_chooser = FileChooserDialog::new(
|
GPSApp::get_file_from_dialog(&app, false, move |app, filename|
|
||||||
Some("Open File"),
|
{
|
||||||
Some(&window),
|
|
||||||
FileChooserAction::Open,
|
|
||||||
&[("Open", ResponseType::Ok), ("Cancel", ResponseType::Cancel)],
|
|
||||||
);
|
|
||||||
file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| {
|
|
||||||
if response == ResponseType::Ok {
|
|
||||||
let file = d.file().expect("Couldn't get file");
|
|
||||||
let filename = String::from(file.path().expect("Couldn't get file path").to_str().expect("unable to convert to string"));
|
|
||||||
println!("Open file {}", filename);
|
println!("Open file {}", filename);
|
||||||
app.load_graph(&filename).expect("Unable to open file");
|
app.load_graph(&filename).expect("Unable to open file");
|
||||||
}
|
});
|
||||||
|
|
||||||
d.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
file_chooser.show();
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
application.add_action(&action);
|
application.add_action(&action);
|
||||||
application.set_accels_for_action("app.open", &["<primary>o"]);
|
application.set_accels_for_action("app.open", &["<primary>o"]);
|
||||||
|
@ -192,27 +220,14 @@ impl GPSApp {
|
||||||
let action = gio::SimpleAction::new("save_as", None);
|
let action = gio::SimpleAction::new("save_as", None);
|
||||||
let app_weak = self.downgrade();
|
let app_weak = self.downgrade();
|
||||||
action.connect_activate(glib::clone!(@weak window => move |_, _| {
|
action.connect_activate(glib::clone!(@weak window => move |_, _| {
|
||||||
let app = upgrade_weak!(app_weak);
|
|
||||||
let file_chooser = FileChooserDialog::new(
|
|
||||||
Some("Save File"),
|
|
||||||
Some(&window),
|
|
||||||
FileChooserAction::Open,
|
|
||||||
&[("Save", ResponseType::Ok), ("Cancel", ResponseType::Cancel)],
|
|
||||||
);
|
|
||||||
file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| {
|
|
||||||
if response == ResponseType::Ok {
|
|
||||||
let file = d.file().expect("Couldn't get file");
|
|
||||||
let filename = String::from(file.path().expect("Couldn't get file path").to_str().expect("unable to convert to string"));
|
|
||||||
println!("Save file {}", filename);
|
|
||||||
app.save_graph(&filename).expect("Unable to save file");
|
|
||||||
}
|
|
||||||
|
|
||||||
d.close();
|
let app = upgrade_weak!(app_weak);
|
||||||
});
|
GPSApp::get_file_from_dialog(&app, true, move |app, filename|
|
||||||
|
{
|
||||||
file_chooser.show();
|
println!("Save file {}", filename);
|
||||||
|
app.save_graph(&filename).expect("Unable to save file");
|
||||||
}));
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
application.add_action(&action);
|
application.add_action(&action);
|
||||||
application.set_accels_for_action("app.save", &["<primary>s"]);
|
application.set_accels_for_action("app.save", &["<primary>s"]);
|
||||||
|
@ -255,7 +270,7 @@ impl GPSApp {
|
||||||
add_button.connect_clicked(glib::clone!(@weak window => move |_| {
|
add_button.connect_clicked(glib::clone!(@weak window => move |_| {
|
||||||
let app = upgrade_weak!(app_weak);
|
let app = upgrade_weak!(app_weak);
|
||||||
let elements = Pipeline::elements_list().expect("Unable to obtain element's list");
|
let elements = Pipeline::elements_list().expect("Unable to obtain element's list");
|
||||||
pluginlist::display_plugin_list(&app, &elements);
|
plugindialogs::display_plugin_list(&app, &elements);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let add_button: Button = self
|
let add_button: Button = self
|
||||||
|
@ -319,7 +334,7 @@ impl GPSApp {
|
||||||
node_id,
|
node_id,
|
||||||
Node::new(
|
Node::new(
|
||||||
node_id,
|
node_id,
|
||||||
"videotestsrc",
|
"filesrc",
|
||||||
Pipeline::element_type("videotestsrc"),
|
Pipeline::element_type("videotestsrc"),
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
|
@ -395,8 +410,11 @@ impl GPSApp {
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
let action = gio::SimpleAction::new("node.delete", None);
|
let action = gio::SimpleAction::new("node.delete", None);
|
||||||
|
let app_weak = app.downgrade();
|
||||||
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
|
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
|
||||||
|
let app = upgrade_weak!(app_weak);
|
||||||
println!("node.delete {}", node_id);
|
println!("node.delete {}", node_id);
|
||||||
|
app.graphview.borrow_mut().remove_node(node_id);
|
||||||
pop_menu.unparent();
|
pop_menu.unparent();
|
||||||
}));
|
}));
|
||||||
application.add_action(&action);
|
application.add_action(&action);
|
||||||
|
@ -406,15 +424,18 @@ impl GPSApp {
|
||||||
println!("node.request-pad {}", node_id);
|
println!("node.request-pad {}", node_id);
|
||||||
pop_menu.unparent();
|
pop_menu.unparent();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
application.add_action(&action);
|
application.add_action(&action);
|
||||||
let action = gio::SimpleAction::new("node.properties", None);
|
let action = gio::SimpleAction::new("node.properties", None);
|
||||||
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
|
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
|
||||||
println!("node.properties {}", node_id);
|
println!("node.properties {}", node_id);
|
||||||
|
let node = app.graphview.borrow().node(&node_id).unwrap();
|
||||||
|
plugindialogs::display_plugin_properties(&app, &node.name(), node_id);
|
||||||
pop_menu.unparent();
|
pop_menu.unparent();
|
||||||
}));
|
}));
|
||||||
application.add_action(&action);
|
application.add_action(&action);
|
||||||
|
|
||||||
|
|
||||||
pop_menu.show();
|
pop_menu.show();
|
||||||
None
|
None
|
||||||
}),
|
}),
|
||||||
|
@ -434,6 +455,15 @@ impl GPSApp {
|
||||||
let graph_view = self.graphview.borrow_mut();
|
let graph_view = self.graphview.borrow_mut();
|
||||||
let node_id = graph_view.next_node_id();
|
let node_id = graph_view.next_node_id();
|
||||||
let pads = Pipeline::pads(&element_name, false);
|
let pads = Pipeline::pads(&element_name, false);
|
||||||
|
if Pipeline::element_is_uri_src_handler(&element_name) {
|
||||||
|
GPSApp::get_file_from_dialog(self, false, move |app, filename| {
|
||||||
|
println!("Open file {}", filename);
|
||||||
|
let node = app.graphview.borrow().node(&node_id).unwrap();
|
||||||
|
let mut properties: HashMap<String, String> = HashMap::new();
|
||||||
|
properties.insert(String::from("location"), filename);
|
||||||
|
node.update_node_properties(&properties);
|
||||||
|
});
|
||||||
|
}
|
||||||
graph_view.add_node_with_port(
|
graph_view.add_node_with_port(
|
||||||
node_id,
|
node_id,
|
||||||
Node::new(
|
Node::new(
|
||||||
|
@ -446,6 +476,11 @@ impl GPSApp {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_element_properties(&self, node_id: u32, properties: &HashMap<String, String>) {
|
||||||
|
let node = self.graphview.borrow().node(&node_id).unwrap();
|
||||||
|
node.update_node_properties(properties);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save_graph(&self, filename: &str) -> anyhow::Result<(), Box<dyn error::Error>> {
|
pub fn save_graph(&self, filename: &str) -> anyhow::Result<(), Box<dyn error::Error>> {
|
||||||
let graph_view = self.graphview.borrow_mut();
|
let graph_view = self.graphview.borrow_mut();
|
||||||
graph_view.render_xml(filename)?;
|
graph_view.render_xml(filename)?;
|
||||||
|
|
38
src/gps.ui
38
src/gps.ui
|
@ -68,8 +68,8 @@
|
||||||
<property name="default-height">260</property>
|
<property name="default-height">260</property>
|
||||||
<property name="hide-on-close">True</property>
|
<property name="hide-on-close">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox" id="v_box">
|
<object class="GtkPaned">
|
||||||
<property name="orientation">horizontal</property>
|
<property name="position">200</property>s
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||||
<property name="hexpand">True</property>
|
<property name="hexpand">True</property>
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScrolledWindow" id="scrolled2_window">
|
<object class="GtkScrolledWindow">
|
||||||
<property name="hexpand">True</property>
|
<property name="hexpand">True</property>
|
||||||
<property name="vexpand">True</property>
|
<property name="vexpand">True</property>
|
||||||
<child>
|
<child>
|
||||||
|
@ -91,6 +91,36 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkDialog" id="dialog-plugin-properties">
|
||||||
|
<property name="transient-for">mainwindow</property>
|
||||||
|
<property name="default-width">320</property>
|
||||||
|
<property name="default-height">260</property>
|
||||||
|
<property name="hide-on-close">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box-plugin-properties">
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="apply-plugin-properties">
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="hexpand">1</property>
|
||||||
|
<property name="receives-default">1</property>
|
||||||
|
<property name="label" translatable="yes">apply</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
<object class="GtkApplicationWindow" id="mainwindow">
|
<object class="GtkApplicationWindow" id="mainwindow">
|
||||||
<property name="title" translatable="yes">GstPipelineStudio</property>
|
<property name="title" translatable="yes">GstPipelineStudio</property>
|
||||||
<property name="default-width">800</property>
|
<property name="default-width">800</property>
|
||||||
|
@ -153,7 +183,7 @@
|
||||||
<object class="GtkButton" id="button-clear">
|
<object class="GtkButton" id="button-clear">
|
||||||
<property name="hexpand">1</property>
|
<property name="hexpand">1</property>
|
||||||
<property name="receives-default">1</property>
|
<property name="receives-default">1</property>
|
||||||
<property name="label">clear</property>
|
<property name="label" translatable="yes">_clear</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -412,6 +412,10 @@ impl GraphView {
|
||||||
let private = imp::GraphView::from_instance(self);
|
let private = imp::GraphView::from_instance(self);
|
||||||
let mut nodes = private.nodes.borrow_mut();
|
let mut nodes = private.nodes.borrow_mut();
|
||||||
if let Some(node) = nodes.remove(&id) {
|
if let Some(node) = nodes.remove(&id) {
|
||||||
|
if let Some(link_id) = self.node_is_linked(node.id()) {
|
||||||
|
let mut links = private.links.borrow_mut();
|
||||||
|
links.remove(&link_id);
|
||||||
|
}
|
||||||
node.unparent();
|
node.unparent();
|
||||||
} else {
|
} else {
|
||||||
warn!("Tried to remove non-existant node (id={}) from graph", id);
|
warn!("Tried to remove non-existant node (id={}) from graph", id);
|
||||||
|
@ -431,14 +435,15 @@ impl GraphView {
|
||||||
nodes_list
|
nodes_list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn node(&self, id: &u32) -> Option<super::node::Node> {
|
||||||
|
let private = imp::GraphView::from_instance(self);
|
||||||
|
private.nodes.borrow().get(id).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_all_nodes(&self) {
|
pub fn remove_all_nodes(&self) {
|
||||||
let private = imp::GraphView::from_instance(self);
|
let private = imp::GraphView::from_instance(self);
|
||||||
let nodes_list = self.all_nodes(NodeType::All);
|
let nodes_list = self.all_nodes(NodeType::All);
|
||||||
for node in nodes_list {
|
for node in nodes_list {
|
||||||
if let Some(link_id) = self.node_is_linked(node.id()) {
|
|
||||||
let mut links = private.links.borrow_mut();
|
|
||||||
links.remove(&link_id);
|
|
||||||
}
|
|
||||||
self.remove_node(node.id());
|
self.remove_node(node.id());
|
||||||
}
|
}
|
||||||
private.current_node_id.set(0);
|
private.current_node_id.set(0);
|
||||||
|
@ -493,7 +498,6 @@ impl GraphView {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link related methods
|
// Link related methods
|
||||||
|
|
||||||
pub fn all_links(&self) -> Vec<NodeLink> {
|
pub fn all_links(&self) -> Vec<NodeLink> {
|
||||||
let private = imp::GraphView::from_instance(self);
|
let private = imp::GraphView::from_instance(self);
|
||||||
let links = private.links.borrow();
|
let links = private.links.borrow();
|
||||||
|
@ -668,6 +672,10 @@ impl GraphView {
|
||||||
description.push_str(" ! ");
|
description.push_str(" ! ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (name, value) in node.properties().iter() {
|
||||||
|
description.push_str(&format!(" {}={}", name, value));
|
||||||
|
}
|
||||||
|
|
||||||
println!("{}", description);
|
println!("{}", description);
|
||||||
for port in ports {
|
for port in ports {
|
||||||
if let Some((_port_to, node_to)) = self.port_connected_to(port.id()) {
|
if let Some((_port_to, node_to)) = self.port_connected_to(port.id()) {
|
||||||
|
@ -714,6 +722,15 @@ impl GraphView {
|
||||||
)?;
|
)?;
|
||||||
writer.write(XMLWEvent::end_element())?;
|
writer.write(XMLWEvent::end_element())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (name, value) in node.properties().iter() {
|
||||||
|
writer.write(
|
||||||
|
XMLWEvent::start_element("Property")
|
||||||
|
.attr("name", name)
|
||||||
|
.attr("value", value),
|
||||||
|
)?;
|
||||||
|
writer.write(XMLWEvent::end_element())?;
|
||||||
|
}
|
||||||
writer.write(XMLWEvent::end_element())?;
|
writer.write(XMLWEvent::end_element())?;
|
||||||
}
|
}
|
||||||
//Get the link and write it.
|
//Get the link and write it.
|
||||||
|
@ -775,6 +792,17 @@ impl GraphView {
|
||||||
NodeType::from_str(node_type.as_str()),
|
NodeType::from_str(node_type.as_str()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
"Property" => {
|
||||||
|
let name = attrs
|
||||||
|
.get::<String>(&String::from("name"))
|
||||||
|
.expect("Unable to find property name");
|
||||||
|
let value: &String = attrs
|
||||||
|
.get::<String>(&String::from("value"))
|
||||||
|
.expect("Unable to find property value");
|
||||||
|
let node = current_node.clone();
|
||||||
|
node.expect("current node does not exist")
|
||||||
|
.add_property(name.clone(), value.clone());
|
||||||
|
}
|
||||||
"Port" => {
|
"Port" => {
|
||||||
let id = attrs
|
let id = attrs
|
||||||
.get::<String>(&String::from("id"))
|
.get::<String>(&String::from("id"))
|
||||||
|
@ -835,6 +863,7 @@ impl GraphView {
|
||||||
}
|
}
|
||||||
current_node = None;
|
current_node = None;
|
||||||
}
|
}
|
||||||
|
"Property" => {}
|
||||||
"Port" => {
|
"Port" => {
|
||||||
if let Some(port) = current_port {
|
if let Some(port) = current_port {
|
||||||
let node = current_node.clone();
|
let node = current_node.clone();
|
||||||
|
|
|
@ -67,6 +67,7 @@ mod imp {
|
||||||
pub(super) ports: RefCell<HashMap<u32, Port>>,
|
pub(super) ports: RefCell<HashMap<u32, Port>>,
|
||||||
pub(super) num_ports_in: Cell<i32>,
|
pub(super) num_ports_in: Cell<i32>,
|
||||||
pub(super) num_ports_out: Cell<i32>,
|
pub(super) num_ports_out: Cell<i32>,
|
||||||
|
pub(super) properties: RefCell<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -97,6 +98,7 @@ mod imp {
|
||||||
ports: RefCell::new(HashMap::new()),
|
ports: RefCell::new(HashMap::new()),
|
||||||
num_ports_in: Cell::new(0),
|
num_ports_in: Cell::new(0),
|
||||||
num_ports_out: Cell::new(0),
|
num_ports_out: Cell::new(0),
|
||||||
|
properties: RefCell::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,4 +216,21 @@ impl Node {
|
||||||
let private = imp::Node::from_instance(self);
|
let private = imp::Node::from_instance(self);
|
||||||
private.node_type.get()
|
private.node_type.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_property(&self, name: String, value: String) {
|
||||||
|
let private = imp::Node::from_instance(self);
|
||||||
|
println!("{} {} updated", name, value);
|
||||||
|
private.properties.borrow_mut().insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_node_properties(&self, new_properties: &HashMap<String, String>) {
|
||||||
|
for (key, value) in new_properties {
|
||||||
|
self.add_property(key.clone(), value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn properties(&self) -> Ref<HashMap<String, String>> {
|
||||||
|
let private = imp::Node::from_instance(self);
|
||||||
|
private.properties.borrow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,3 +12,22 @@ macro_rules! upgrade_weak {
|
||||||
upgrade_weak!($x, ())
|
upgrade_weak!($x, ())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! str_some_value (
|
||||||
|
($value:expr, $t:ty) => (
|
||||||
|
{
|
||||||
|
let value = $value.get::<$t>().expect("ser_some_value macro");
|
||||||
|
value
|
||||||
|
}
|
||||||
|
);
|
||||||
|
);
|
||||||
|
macro_rules! str_opt_value (
|
||||||
|
($value:expr, $t:ty) => (
|
||||||
|
{
|
||||||
|
match $value.get::<$t>() {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
Err(_e) => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
|
@ -23,7 +23,7 @@ mod app;
|
||||||
mod common;
|
mod common;
|
||||||
mod graphmanager;
|
mod graphmanager;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod pluginlist;
|
mod plugindialogs;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
||||||
use crate::app::GPSApp;
|
use crate::app::GPSApp;
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::graphmanager::NodeType;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use gstreamer as gst;
|
use gstreamer as gst;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
@ -304,6 +305,64 @@ impl Pipeline {
|
||||||
|
|
||||||
element_type
|
element_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 element_properties(
|
||||||
|
element_name: &str,
|
||||||
|
) -> anyhow::Result<HashMap<String, String>, Box<dyn error::Error>> {
|
||||||
|
let mut properties_list = HashMap::new();
|
||||||
|
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
|
||||||
|
|
||||||
|
let factory = feature
|
||||||
|
.downcast::<gst::ElementFactory>()
|
||||||
|
.expect("Factory not found");
|
||||||
|
let element = factory.create(None)?;
|
||||||
|
let params = element.class().list_properties();
|
||||||
|
|
||||||
|
for param in params {
|
||||||
|
println!("Property_name {}", param.name());
|
||||||
|
if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||||
|
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||||
|
{
|
||||||
|
let value = Pipeline::value_as_str(&element.property(param.name()).unwrap())
|
||||||
|
.unwrap_or_else(|| String::from(""));
|
||||||
|
properties_list.insert(String::from(param.name()), value);
|
||||||
|
} else if let Some(value) = Pipeline::value_as_str(param.default_value()) {
|
||||||
|
properties_list.insert(String::from(param.name()), value);
|
||||||
|
} else {
|
||||||
|
println!("Unable to add property_name {}", param.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(properties_list)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element_is_uri_src_handler(element_name: &str) -> bool {
|
||||||
|
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
|
||||||
|
|
||||||
|
let factory = feature
|
||||||
|
.downcast::<gst::ElementFactory>()
|
||||||
|
.expect("Factory not found");
|
||||||
|
let element = factory.create(None).expect("Unable to get factory");
|
||||||
|
match element.dynamic_cast::<gst::URIHandler>() {
|
||||||
|
Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src,
|
||||||
|
Err(_e) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for PipelineInner {
|
impl Drop for PipelineInner {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// pluginlist.rs
|
// plugindialogs.rs
|
||||||
//
|
//
|
||||||
// Copyright 2021 Stéphane Cerveau <scerveau@collabora.com>
|
// Copyright 2021 Stéphane Cerveau <scerveau@collabora.com>
|
||||||
//
|
//
|
||||||
|
@ -22,8 +22,14 @@ use crate::pipeline::Pipeline;
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::TextBuffer;
|
use gtk::TextBuffer;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use gtk::{CellRendererText, Dialog, ListStore, TextView, TreeView, TreeViewColumn};
|
use gtk::{
|
||||||
|
Box, Button, CellRendererText, Dialog, Entry, Label, ListStore, TextView, TreeView,
|
||||||
|
TreeViewColumn,
|
||||||
|
};
|
||||||
|
|
||||||
fn create_and_fill_model(elements: &[ElementInfo]) -> ListStore {
|
fn create_and_fill_model(elements: &[ElementInfo]) -> ListStore {
|
||||||
// Creation of a model with two rows.
|
// Creation of a model with two rows.
|
||||||
|
@ -115,3 +121,61 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32) {
|
||||||
|
let dialog: Dialog = app
|
||||||
|
.builder
|
||||||
|
.object("dialog-plugin-properties")
|
||||||
|
.expect("Couldn't get window");
|
||||||
|
|
||||||
|
dialog.set_title(Some(&format!("{} properties", element_name)));
|
||||||
|
dialog.set_default_size(640, 480);
|
||||||
|
dialog.set_modal(true);
|
||||||
|
|
||||||
|
let properties_box: Box = app
|
||||||
|
.builder
|
||||||
|
.object("box-plugin-properties")
|
||||||
|
.expect("Couldn't get window");
|
||||||
|
let update_properties: Rc<RefCell<HashMap<String, String>>> =
|
||||||
|
Rc::new(RefCell::new(HashMap::new()));
|
||||||
|
let properties = Pipeline::element_properties(element_name).unwrap();
|
||||||
|
for (name, value) in properties {
|
||||||
|
let entry_box = Box::new(gtk::Orientation::Horizontal, 6);
|
||||||
|
let label = Label::new(Some(&name));
|
||||||
|
label.set_hexpand(true);
|
||||||
|
label.set_halign(gtk::Align::Start);
|
||||||
|
label.set_margin_start(4);
|
||||||
|
entry_box.append(&label);
|
||||||
|
let entry: Entry = Entry::new();
|
||||||
|
entry.set_text(&value);
|
||||||
|
entry.set_hexpand(true);
|
||||||
|
entry.set_halign(gtk::Align::Start);
|
||||||
|
entry.set_widget_name(&name);
|
||||||
|
entry.connect_changed(
|
||||||
|
glib::clone!(@weak entry, @strong update_properties => move |_| {
|
||||||
|
println!("{}:{}", entry.widget_name(), entry.text());
|
||||||
|
update_properties.borrow_mut().insert(entry.widget_name().to_string(), entry.text().to_string());
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
entry_box.append(&entry);
|
||||||
|
properties_box.append(&entry_box);
|
||||||
|
}
|
||||||
|
let properties_apply_btn: Button = app
|
||||||
|
.builder
|
||||||
|
.object("apply-plugin-properties")
|
||||||
|
.expect("Couldn't get window");
|
||||||
|
|
||||||
|
let app_weak = app.downgrade();
|
||||||
|
properties_apply_btn.connect_clicked(
|
||||||
|
glib::clone!(@strong update_properties, @weak dialog => move |_| {
|
||||||
|
let app = upgrade_weak!(app_weak);
|
||||||
|
app.update_element_properties(node_id, &update_properties.borrow());
|
||||||
|
dialog.close();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
for p in update_properties.borrow().values() {
|
||||||
|
println!("updated properties {}", p);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue