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
@@ -146,8 +146,11 @@ mode or the span. **A signal badge dragged onto a scope window's plot lands ther
|
|||||||
drop the main plot takes, into that window's own row set, on the cell the pointer is over — and
|
drop the main plot takes, into that window's own row set, on the cell the pointer is over — and
|
||||||
the signal page's *Plot on* buttons list **every window**: one group per window (named, `In
|
the signal page's *Plot on* buttons list **every window**: one group per window (named, `In
|
||||||
"plot 2"`), one button per plot of it. A right-click on a scope window's plot opens **its own
|
"plot 2"`), one button per plot of it. A right-click on a scope window's plot opens **its own
|
||||||
menu** — y axis (auto or a fixed range), fit, remove a trace — acting on that window's rows; a
|
menu** — y axis (auto or a fixed range), *Fit X axis*, *Fit all*, remove a trace — acting on that
|
||||||
detached window's menu carries **Attach back to the main window** beside them.
|
window's rows (the x fit resets the **shared** time span, so every view follows); a detached
|
||||||
|
window's menu carries **Attach back to the main window** beside them. A window that cannot paint
|
||||||
|
says so in its own title after two seconds (and on stderr): a blank plot is indistinguishable from
|
||||||
|
a context that failed, and the sentence names which.
|
||||||
mode or the span.
|
mode or the span.
|
||||||
|
|
||||||
**The window.** The plot is the window. Under it sits a fixed-height **badge bar** (36 px). Its left
|
**The window.** The plot is the window. Under it sits a fixed-height **badge bar** (36 px). Its left
|
||||||
|
|||||||
+55
-5
@@ -3711,15 +3711,38 @@ void on_scope_y_fixed_clicked(GtkButton* b, gpointer user) {
|
|||||||
scope_menu_dismiss(b);
|
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
|
/// The y fit of a scope window: the y axes alone.
|
||||||
/// y axes alone -- `fit_all()`'s x reset would be undone by the next frame's mirror.
|
void on_scope_fit_y_clicked(GtkButton* b, gpointer) {
|
||||||
void on_scope_fit_clicked(GtkButton* b, gpointer) {
|
|
||||||
App::ScopeWindow* scope = scope_menu_scope(b);
|
App::ScopeWindow* scope = scope_menu_scope(b);
|
||||||
if (scope == nullptr || scope->rows == nullptr) return;
|
if (scope == nullptr || scope->rows == nullptr) return;
|
||||||
scope->rows->set_y_auto(scope_menu_row(b), true);
|
scope->rows->set_y_auto(scope_menu_row(b), true);
|
||||||
scope_menu_dismiss(b);
|
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) {
|
void on_scope_remove_clicked(GtkButton* b, gpointer) {
|
||||||
App::ScopeWindow* scope = scope_menu_scope(b);
|
App::ScopeWindow* scope = scope_menu_scope(b);
|
||||||
if (scope == nullptr || scope->rows == nullptr) return;
|
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);
|
g_signal_connect(y_auto, "clicked", G_CALLBACK(on_scope_y_auto_clicked), user);
|
||||||
GtkWidget* y_fixed = row_btn("Y axis: fixed range...");
|
GtkWidget* y_fixed = row_btn("Y axis: fixed range...");
|
||||||
g_signal_connect(y_fixed, "clicked", G_CALLBACK(on_scope_y_fixed_clicked), user);
|
g_signal_connect(y_fixed, "clicked", G_CALLBACK(on_scope_y_fixed_clicked), user);
|
||||||
GtkWidget* fit = row_btn("Fit Y axis");
|
GtkWidget* fit_x = row_btn("Fit X axis");
|
||||||
g_signal_connect(fit, "clicked", G_CALLBACK(on_scope_fit_clicked), user);
|
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
|
// 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.
|
// 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());
|
// scope->area->set_figure(&scope->rows->figure());
|
||||||
// }
|
// }
|
||||||
if (scope->detached) sync_scope_attach(*app, *scope);
|
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));
|
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_x = 0.0;
|
||||||
double pointer_y = 0.0;
|
double pointer_y = 0.0;
|
||||||
bool pointer_valid = false;
|
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
|
/// 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
|
/// outlives the widget, and sharing the main window's placeholder would hand two areas
|
||||||
/// one plot the moment the figure mode is ever left.
|
/// one plot the moment the figure mode is ever left.
|
||||||
|
|||||||
Reference in new issue
Block a user