scope windows: Fit X axis / Fit all in the menu, and a window that cannot paint says so
The scope menu carried a y fit only: with one shared time span, the x fit *is* the main window's fit, and the next frame mirrors the result back -- so `Fit X axis` and `Fit all` now sit in the menu beside the y entries and reset the shared range. And a blank plot becomes a sentence: a scope window whose GL context failed, or whose area has painted no frame two seconds after opening, writes the reason into its own title and to stderr (`[scope] window 'plot 1' is not drawing: GL context failed: ...`). A blank rectangle is indistinguishable from a failed context or an area that never got a frame, and the report "the plot is not there, no x axis" is exactly what this answers: if the title stays unchanged, the area is painting and the question moves to the compositor presenting the second window.
This commit is contained in:
1 parent
2924f7627d
commit
fa2d0f12fc
3 files changed
+64
-7
No files matched your search
+55
-5
@@ -3711,15 +3711,38 @@ void on_scope_y_fixed_clicked(GtkButton* b, gpointer user) {
|
||||
scope_menu_dismiss(b);
|
||||
}
|
||||
|
||||
/// The y fit of a scope window: the window's x follows the main window's span, so the fit is the
|
||||
/// y axes alone -- `fit_all()`'s x reset would be undone by the next frame's mirror.
|
||||
void on_scope_fit_clicked(GtkButton* b, gpointer) {
|
||||
/// The y fit of a scope window: the y axes alone.
|
||||
void on_scope_fit_y_clicked(GtkButton* b, gpointer) {
|
||||
App::ScopeWindow* scope = scope_menu_scope(b);
|
||||
if (scope == nullptr || scope->rows == nullptr) return;
|
||||
scope->rows->set_y_auto(scope_menu_row(b), true);
|
||||
scope_menu_dismiss(b);
|
||||
}
|
||||
|
||||
/// The x fit of a scope window is the main window's: there is **one** time span for every view,
|
||||
/// and the scope mirrors it -- so the fit resets the shared x range and the next frame mirrors
|
||||
/// the result back, in this window and in every other.
|
||||
void on_scope_fit_x_clicked(GtkButton* b, gpointer user) {
|
||||
App& app = *static_cast<App*>(user);
|
||||
app.rows->fit_x();
|
||||
app.notice = "x back to the " + format_window(app.rows->time_window()) + " window";
|
||||
app.notice_until_us = g_get_monotonic_time() + 3 * G_TIME_SPAN_SECOND;
|
||||
scope_menu_dismiss(b);
|
||||
}
|
||||
|
||||
/// Both fits: this window's y, the shared x.
|
||||
void on_scope_fit_all_clicked(GtkButton* b, gpointer user) {
|
||||
App& app = *static_cast<App*>(user);
|
||||
App::ScopeWindow* scope = scope_menu_scope(b);
|
||||
if (scope == nullptr || scope->rows == nullptr) return;
|
||||
scope->rows->set_y_auto(scope_menu_row(b), true);
|
||||
app.rows->fit_x();
|
||||
app.notice = "y fitted to the data, x back to the " +
|
||||
format_window(app.rows->time_window()) + " window";
|
||||
app.notice_until_us = g_get_monotonic_time() + 3 * G_TIME_SPAN_SECOND;
|
||||
scope_menu_dismiss(b);
|
||||
}
|
||||
|
||||
void on_scope_remove_clicked(GtkButton* b, gpointer) {
|
||||
App::ScopeWindow* scope = scope_menu_scope(b);
|
||||
if (scope == nullptr || scope->rows == nullptr) return;
|
||||
@@ -3785,8 +3808,10 @@ void on_scope_right_click(GtkGestureClick* gesture, int, double x, double y, gpo
|
||||
g_signal_connect(y_auto, "clicked", G_CALLBACK(on_scope_y_auto_clicked), user);
|
||||
GtkWidget* y_fixed = row_btn("Y axis: fixed range...");
|
||||
g_signal_connect(y_fixed, "clicked", G_CALLBACK(on_scope_y_fixed_clicked), user);
|
||||
GtkWidget* fit = row_btn("Fit Y axis");
|
||||
g_signal_connect(fit, "clicked", G_CALLBACK(on_scope_fit_clicked), user);
|
||||
GtkWidget* fit_x = row_btn("Fit X axis");
|
||||
g_signal_connect(fit_x, "clicked", G_CALLBACK(on_scope_fit_x_clicked), user);
|
||||
GtkWidget* fit = row_btn("Fit all");
|
||||
g_signal_connect(fit, "clicked", G_CALLBACK(on_scope_fit_all_clicked), user);
|
||||
|
||||
// One removal per trace on the plot, flat rather than as a second view: a scope window holds
|
||||
// at most its own figure's traces, and the menu is small.
|
||||
@@ -4610,6 +4635,31 @@ gboolean tick(gpointer user) {
|
||||
// scope->area->set_figure(&scope->rows->figure());
|
||||
// }
|
||||
if (scope->detached) sync_scope_attach(*app, *scope);
|
||||
// **A window that cannot paint says so.** A blank plot is indistinguishable from a
|
||||
// context that failed or an area that never got a frame, and the report ("nothing is
|
||||
// drawn, no axes") is exactly what this turns into a sentence: the window's own title
|
||||
// carries the reason, and stderr carries it with the window's name.
|
||||
if (scope->area != nullptr && scope->paint_checks >= 0 && scope->paint_checks < 120) {
|
||||
++scope->paint_checks;
|
||||
if (scope->paint_checks == 120) {
|
||||
const plot::Status st = scope->area->init_status();
|
||||
const plot::u64 painted = scope->area->frames_painted();
|
||||
if (!st || painted == 0) {
|
||||
const std::string why =
|
||||
st ? std::string("the plot painted no frame in two seconds")
|
||||
: std::string("GL context failed: ") + st.message;
|
||||
std::fprintf(stderr, "[scope] window '%s' is not drawing: %s\n",
|
||||
scope->name.c_str(), why.c_str());
|
||||
std::fflush(stderr);
|
||||
if (scope->window != nullptr) {
|
||||
const char* current = gtk_window_get_title(scope->window);
|
||||
gtk_window_set_title(scope->window,
|
||||
((current != nullptr ? std::string(current) + " — " : "") + why)
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scope->widget != nullptr) gtk_gl_area_queue_render(GTK_GL_AREA(scope->widget));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +233,10 @@ struct App {
|
||||
double pointer_x = 0.0;
|
||||
double pointer_y = 0.0;
|
||||
bool pointer_valid = false;
|
||||
/// Frames this window's area was watched for its first paint: a window whose GL context failed
|
||||
/// or that never paints says so in its own title (and on stderr) instead of staying a blank
|
||||
/// rectangle nobody can diagnose.
|
||||
int paint_checks = 0;
|
||||
/// The area's initial plot, owned here: `PlotArea::create()` wants a `Plot&` that
|
||||
/// outlives the widget, and sharing the main window's placeholder would hand two areas
|
||||
/// one plot the moment the figure mode is ever left.
|
||||
|
||||
Reference in new issue
Block a user