logger: implement a logger with treeview

Any log should be now visible in the logger
tree view according to the level.
This commit is contained in:
Stéphane Cerveau 2022-01-04 17:48:32 +01:00
parent d291c93352
commit 3731a92d51
7 changed files with 172 additions and 21 deletions

View file

@ -9,7 +9,7 @@ TODO:
- [x] Create connection between element - [x] Create connection between element
- [] Control the connection between element - [] Control the connection between element
- [x] unable to connect in and in out and out - [x] unable to connect in and in out and out
- [] unable to connnec element with incompatible caps. - [] unable to connect element with incompatible caps.
- [x] unable to connect a port which is already connected - [x] unable to connect a port which is already connected
- [x] create contextual menu on pad or element - [x] create contextual menu on pad or element
- [] upclass the element - [] upclass the element
@ -22,7 +22,7 @@ TODO:
- [] check that a node accept to create a port on request (input/output) - [] check that a node accept to create a port on request (input/output)
- [x] select nodes/links with a Trait Selectable - [x] select nodes/links with a Trait Selectable
- [x] be able to remove a link by selecting it - [x] be able to remove a link by selecting it
- [] Connect the logs to the window - [x] Connect the logs to the window
- [] Create a window for the video output - [] Create a window for the video output
- [] Add multiple graphviews with tabs. - [] Add multiple graphviews with tabs.
@ -30,7 +30,7 @@ TODO:
- [x] crash with x11 on contextual menu - [x] crash with x11 on contextual menu
- [] check that element exists before creating it on file load. - [] check that element exists before creating it on file load.
- [] open multiple times dialog (About) prevent to close it. - [x] open multiple times dialog (About) prevent to close it.
## Code cleanup ## Code cleanup

View file

@ -31,9 +31,11 @@ use std::collections::HashMap;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::{error, ops}; use std::{error, ops};
use crate::logger;
use crate::pipeline::{Pipeline, PipelineState}; use crate::pipeline::{Pipeline, PipelineState};
use crate::plugindialogs; use crate::plugindialogs;
use crate::settings::Settings; use crate::settings::Settings;
use crate::{GPS_DEBUG, GPS_ERROR};
use crate::graphmanager::{GraphView, Node, PortDirection}; use crate::graphmanager::{GraphView, Node, PortDirection};
@ -189,6 +191,39 @@ impl GPSApp {
dialog.set_resizable(false); dialog.set_resizable(false);
dialog.show(); dialog.show();
} }
fn reset_logger_list(&self, logger_list: &TreeView) {
let model = ListStore::new(&[String::static_type()]);
logger_list.set_model(Some(&model));
}
fn setup_logger_list(&self) {
let logger_list: TreeView = self
.builder
.object("logger_list")
.expect("Couldn't get window");
let column = TreeViewColumn::new();
let cell = CellRendererText::new();
column.pack_start(&cell, true);
// Association of the view's column with the model's `id` column.
column.add_attribute(&cell, "text", 0);
column.set_title("");
logger_list.append_column(&column);
self.reset_logger_list(&logger_list);
}
fn add_to_logger_list(&self, log_entry: String) {
let logger_list: TreeView = self
.builder
.object("logger_list")
.expect("Couldn't get window");
if let Some(model) = logger_list.model() {
let list_store = model
.dynamic_cast::<ListStore>()
.expect("Could not cast to ListStore");
list_store.insert_with_values(None, &[(0, &log_entry)]);
}
}
fn reset_favorite_list(&self, favorite_list: &TreeView) { fn reset_favorite_list(&self, favorite_list: &TreeView) {
let model = ListStore::new(&[String::static_type()]); let model = ListStore::new(&[String::static_type()]);
@ -222,7 +257,7 @@ impl GPSApp {
.get(&iter, 0) .get(&iter, 0)
.get::<String>() .get::<String>()
.expect("Treeview selection, column 1"); .expect("Treeview selection, column 1");
println!("{}", element_name); GPS_DEBUG!("{}", element_name);
app.add_new_element(&element_name); app.add_new_element(&element_name);
} }
}); });
@ -239,7 +274,7 @@ impl GPSApp {
.get(&iter, 0) .get(&iter, 0)
.get::<String>() .get::<String>()
.expect("Treeview selection, column 1"); .expect("Treeview selection, column 1");
println!("{}", element_name); GPS_DEBUG!("{}", element_name);
let point = graphene::Point::new(x as f32,y as f32); let point = graphene::Point::new(x as f32,y as f32);
@ -292,6 +327,7 @@ impl GPSApp {
} }
} }
} }
pub fn display_plugin_list(app: &GPSApp) { pub fn display_plugin_list(app: &GPSApp) {
let elements = Pipeline::elements_list().expect("Unable to obtain element's list"); let elements = Pipeline::elements_list().expect("Unable to obtain element's list");
plugindialogs::display_plugin_list(app, &elements); plugindialogs::display_plugin_list(app, &elements);
@ -321,7 +357,7 @@ impl GPSApp {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
GPSApp::get_file_from_dialog(&app, false, move |app, filename| GPSApp::get_file_from_dialog(&app, false, move |app, filename|
{ {
println!("Open file {}", filename); //logger::print_log(format!("Open file {}", filename));
app.load_graph(&filename).expect("Unable to open file"); app.load_graph(&filename).expect("Unable to open file");
}); });
})); }));
@ -336,7 +372,7 @@ impl GPSApp {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
GPSApp::get_file_from_dialog(&app, true, move |app, filename| GPSApp::get_file_from_dialog(&app, true, move |app, filename|
{ {
println!("Save file {}", filename); GPS_DEBUG!("Save file {}", filename);
app.save_graph(&filename).expect("Unable to save file"); app.save_graph(&filename).expect("Unable to save file");
}); });
})); }));
@ -373,6 +409,7 @@ impl GPSApp {
move |_, _| { move |_, _| {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
app.clear_graph(); app.clear_graph();
GPS_ERROR!("clear graph");
} }
}); });
application.add_action(&action); application.add_action(&action);
@ -538,7 +575,7 @@ impl GPSApp {
// add an action to delete link // add an action to delete link
let action = gio::SimpleAction::new("port.delete-link", None); let action = gio::SimpleAction::new("port.delete-link", None);
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| { action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
println!("port.delete-link port {} node {}", port_id, node_id); GPS_DEBUG!("port.delete-link port {} node {}", port_id, node_id);
pop_menu.unparent(); pop_menu.unparent();
})); }));
application.add_action(&action); application.add_action(&action);
@ -580,7 +617,7 @@ impl GPSApp {
let app_weak = app.downgrade(); 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); let app = upgrade_weak!(app_weak);
println!("node.delete {}", node_id); GPS_DEBUG!("node.delete {}", node_id);
let node = app.graphview.borrow().node(&node_id).unwrap(); let node = app.graphview.borrow().node(&node_id).unwrap();
app.add_to_favorite_list(node.name()); app.add_to_favorite_list(node.name());
pop_menu.unparent(); pop_menu.unparent();
@ -590,7 +627,7 @@ impl GPSApp {
let app_weak = app.downgrade(); 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); let app = upgrade_weak!(app_weak);
println!("node.delete {}", node_id); GPS_DEBUG!("node.delete {}", node_id);
app.graphview.borrow_mut().remove_node(node_id); app.graphview.borrow_mut().remove_node(node_id);
pop_menu.unparent(); pop_menu.unparent();
})); }));
@ -600,7 +637,7 @@ impl GPSApp {
let app_weak = app.downgrade(); 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); let app = upgrade_weak!(app_weak);
println!("node.request-pad-input {}", node_id); GPS_DEBUG!("node.request-pad-input {}", node_id);
let mut node = app.graphview.borrow_mut().node(&node_id).unwrap(); let mut node = app.graphview.borrow_mut().node(&node_id).unwrap();
let port_id = app.graphview.borrow().next_port_id(); let port_id = app.graphview.borrow().next_port_id();
node.add_port(port_id, "in", PortDirection::Input); node.add_port(port_id, "in", PortDirection::Input);
@ -612,7 +649,7 @@ impl GPSApp {
let app_weak = app.downgrade(); 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); let app = upgrade_weak!(app_weak);
println!("node.request-pad-output {}", node_id); GPS_DEBUG!("node.request-pad-output {}", node_id);
let mut node = app.graphview.borrow_mut().node(&node_id).unwrap(); let mut node = app.graphview.borrow_mut().node(&node_id).unwrap();
let port_id = app.graphview.borrow_mut().next_port_id(); let port_id = app.graphview.borrow_mut().next_port_id();
node.add_port(port_id, "out", PortDirection::Output); node.add_port(port_id, "out", PortDirection::Output);
@ -622,7 +659,7 @@ impl GPSApp {
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); GPS_DEBUG!("node.properties {}", node_id);
let node = app.graphview.borrow().node(&node_id).unwrap(); let node = app.graphview.borrow().node(&node_id).unwrap();
plugindialogs::display_plugin_properties(&app, &node.name(), node_id); plugindialogs::display_plugin_properties(&app, &node.name(), node_id);
pop_menu.unparent(); pop_menu.unparent();
@ -638,6 +675,17 @@ impl GPSApp {
// Setup the favorite list // Setup the favorite list
self.setup_favorite_list(); self.setup_favorite_list();
// Setup the logger to get messages into the TreeView
let (ready_tx, ready_rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let app_weak = self.downgrade();
logger::init_logger(ready_tx, logger::LogLevel::Debug);
self.setup_logger_list();
let _ = ready_rx.attach(None, move |msg: String| {
let app = upgrade_weak!(app_weak, glib::Continue(false));
app.add_to_logger_list(msg);
glib::Continue(true)
});
} }
// Downgrade to a weak reference // Downgrade to a weak reference
@ -654,7 +702,7 @@ impl GPSApp {
let pads = Pipeline::pads(element_name, false); let pads = Pipeline::pads(element_name, false);
if Pipeline::element_is_uri_src_handler(element_name) { if Pipeline::element_is_uri_src_handler(element_name) {
GPSApp::get_file_from_dialog(self, false, move |app, filename| { GPSApp::get_file_from_dialog(self, false, move |app, filename| {
println!("Open file {}", filename); GPS_DEBUG!("Open file {}", filename);
let node = app.graphview.borrow().node(&node_id).unwrap(); let node = app.graphview.borrow().node(&node_id).unwrap();
let mut properties: HashMap<String, String> = HashMap::new(); let mut properties: HashMap<String, String> = HashMap::new();
properties.insert(String::from("location"), filename); properties.insert(String::from("location"), filename);

View file

@ -270,7 +270,7 @@
<object class="GtkScrolledWindow"> <object class="GtkScrolledWindow">
<property name="valign">end</property> <property name="valign">end</property>
<property name="child"> <property name="child">
<object class="GtkTreeView"/> <object class="GtkTreeView" id="logger_list"/>
</property> </property>
</object> </object>
</child> </child>

97
src/logger.rs Normal file
View file

@ -0,0 +1,97 @@
use glib::Sender;
use gtk::glib;
use once_cell::sync::Lazy;
use once_cell::sync::OnceCell;
use std::cell::RefCell;
use std::fmt;
use std::sync::{Arc, Mutex};
#[derive(Default)]
struct Logger {
pub log_sender: OnceCell<Arc<Mutex<RefCell<Sender<String>>>>>,
pub log_level: OnceCell<LogLevel>,
}
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum LogLevel {
Error,
_Warning,
Info,
_Log,
Debug,
}
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[macro_export]
macro_rules! GPS_ERROR (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Error, format_args!($($arg)*).to_string());
})
);
#[macro_export]
macro_rules! GPS_WARN (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Warning, format_args!($($arg)*).to_string());
})
);
#[macro_export]
macro_rules! GPS_INFO (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Info, format_args!($($arg)*).to_string());
})
);
#[macro_export]
macro_rules! GPS_LOG (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Log, format_args!($($arg)*).to_string());
})
);
#[macro_export]
macro_rules! GPS_DEBUG (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Debug, format_args!($($arg)*).to_string());
})
);
static LOGGER: Lazy<Logger> = Lazy::new(Logger::default);
pub fn init_logger(sender: Sender<String>, log_level: LogLevel) {
LOGGER
.log_sender
.set(Arc::new(Mutex::new(RefCell::new(sender))))
.expect("init logger should be called once");
let _ = LOGGER.log_level.set(log_level);
}
pub fn print_log(log_level: LogLevel, msg: String) {
if log_level
<= *LOGGER
.log_level
.get()
.expect("Logger should be initialized before calling print_log")
{
let mut sender = LOGGER
.log_sender
.get()
.expect("Logger should be initialized before calling print_log")
.lock()
.expect("guarded");
if let Err(e) = sender.get_mut().send(format!("{}:{}", log_level, msg)) {
println!("Error: {}", e)
};
}
}

View file

@ -22,6 +22,8 @@ mod macros;
mod app; mod app;
mod common; mod common;
mod graphmanager; mod graphmanager;
#[macro_use]
mod logger;
mod pipeline; mod pipeline;
mod plugindialogs; mod plugindialogs;
mod settings; mod settings;

View file

@ -18,6 +18,9 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::app::GPSApp; use crate::app::GPSApp;
use crate::graphmanager::{GraphView, Node, NodeType, PortDirection}; use crate::graphmanager::{GraphView, Node, NodeType, PortDirection};
use crate::logger;
use crate::GPS_INFO;
use gst::prelude::*; use gst::prelude::*;
use gstreamer as gst; use gstreamer as gst;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
@ -95,7 +98,7 @@ impl Pipeline {
} }
pub fn create_pipeline(&self, description: &str) -> Result<(), Box<dyn error::Error>> { pub fn create_pipeline(&self, description: &str) -> Result<(), Box<dyn error::Error>> {
println!("Creating pipeline {}", description); GPS_INFO!("Creating pipeline {}", description);
/* create playbin */ /* create playbin */
@ -335,7 +338,7 @@ impl Pipeline {
let params = element.class().list_properties(); let params = element.class().list_properties();
for param in params { for param in params {
println!("Property_name {}", param.name()); GPS_INFO!("Property_name {}", param.name());
if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE || (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
{ {
@ -345,7 +348,7 @@ impl Pipeline {
} else if let Some(value) = Pipeline::value_as_str(param.default_value()) { } else if let Some(value) = Pipeline::value_as_str(param.default_value()) {
properties_list.insert(String::from(param.name()), value); properties_list.insert(String::from(param.name()), value);
} else { } else {
println!("Unable to add property_name {}", param.name()); GPS_INFO!("Unable to add property_name {}", param.name());
} }
} }
Ok(properties_list) Ok(properties_list)

View file

@ -4,6 +4,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::common; use crate::common;
use crate::logger;
#[derive(Debug, Serialize, Deserialize, Default)] #[derive(Debug, Serialize, Deserialize, Default)]
pub struct Settings { pub struct Settings {
@ -18,7 +19,7 @@ impl Settings {
if let Some(parent_dir) = s.parent() { if let Some(parent_dir) = s.parent() {
if !parent_dir.exists() { if !parent_dir.exists() {
if let Err(e) = create_dir_all(parent_dir) { if let Err(e) = create_dir_all(parent_dir) {
println!( GPS_ERROR!(
"Error while trying to build settings snapshot_directory '{}': {}", "Error while trying to build settings snapshot_directory '{}': {}",
parent_dir.display(), parent_dir.display(),
e e
@ -64,7 +65,7 @@ impl Settings {
Settings::settings_file_exist(); Settings::settings_file_exist();
let s = Settings::get_settings_file_path(); let s = Settings::get_settings_file_path();
if let Err(e) = serde_any::to_file(&s, settings) { if let Err(e) = serde_any::to_file(&s, settings) {
println!("Error while trying to save file: {} {}", s.display(), e); GPS_ERROR!("Error while trying to save file: {} {}", s.display(), e);
} }
} }
@ -75,7 +76,7 @@ impl Settings {
match serde_any::from_file::<Settings, _>(&s) { match serde_any::from_file::<Settings, _>(&s) {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(e) => {
println!("Error while opening '{}': {}", s.display(), e); GPS_ERROR!("Error while opening '{}': {}", s.display(), e);
Settings::default() Settings::default()
} }
} }