0
I need some help understanding why a bug is happening in some code using a GtkTreeView, and how to fix it.
In the program, the user opens an image and runs a function to detect stars in the image. This is all reliable and works fine. Data about each star is populated into a GtkTreeView.
The user then clicks on a star in the display which triggers a callback:
gboolean on_drawingarea_button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
that in turn calls the function shown below:
void set_iter_of_clicked_psf(double x, double y) {
GtkTreeSelection *selection = GTK_TREE_SELECTION(gtk_builder_get_object(gui.builder, "treeview-selection"));
GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget("Stars_stored"));
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
GtkTreeIter iter;
gboolean valid;
gboolean is_as;
const double radian_conversion = ((3600.0 * 180.0) / M_PI) / 1.0E3;
double invpixscalex = 1.0;
double bin_X = gfit.unbinned ? (double) gfit.binning_x : 1.0;
if (com.stars && com.stars[0]) {// If the first star has units of arcsec, all should have
is_as = (strcmp(com.stars[0]->units,"px"));
} else {
return; // If com.stars is empty there is no point carrying on
}
if (is_as) {
invpixscalex = 1.0 / (radian_conversion * (double) gfit.pixel_size_x / gfit.focal_length) * bin_X;
}
valid = gtk_tree_model_get_iter_first(model, &iter);
while (valid) {
gdouble xpos, ypos, fwhmx;
gtk_tree_model_get(model, &iter, COLUMN_X0, &xpos, -1);
gtk_tree_model_get(model, &iter, COLUMN_Y0, &ypos, -1);
gtk_tree_model_get(model, &iter, COLUMN_FWHMX, &fwhmx, -1);
fwhmx *= invpixscalex;
gdouble distsq = (xpos - x) * (xpos - x) + (ypos - y) * (ypos - y);
gdouble psflimsq = 6. * fwhmx * fwhmx;
if (distsq < psflimsq) {
GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
if (!path) return;
gtk_tree_selection_select_path(selection, path);
gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.0);
gtk_tree_path_free(path);
gui.selected_star = get_index_of_selected_star(xpos, ypos);
display_status();
redraw(REDRAW_OVERLAY);
return;
}
valid = gtk_tree_model_iter_next(model, &iter);
}
siril_debug_print("Point clicked does not correspond to a known star\n");
return;
}
The code steps through each iter in the GtkTreeView and checks whether the location clicked is close enough to the centre of a star: the key bit of the code then follows inside the conditional towards the end of the function. If the location matches a star, a GtkTreePath is declared and initialised based on the iter, changes the selection to that path and calls gtk_tree_view_scroll_to_cell() which is supposed to scroll the GtkTreeView so that the selected star is shown.
For some users the code works every time and causes the tree view to scroll to the selected star. For other users sometimes it works, sometimes it does not work. When it doesn’t work I can tell the correct code path is followed because gui.selected_star is updated correctly (it triggers a drawing event in the subsequent redraw(REDRAW_OVERLAY) call. It is only gtk_tree_view_scroll_to_cell() that appears to function erractically.
Following some advices I got on the web, I tried using an idle to get rid of this bug:
static gboolean attempt_scroll(GtkTreeView *treeview) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
GList *selected_rows = gtk_tree_selection_get_selected_rows(selection, NULL);
if (selected_rows) {
GtkTreePath *path = (GtkTreePath*) g_list_nth_data(selected_rows, 0);
gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.0);
g_list_free_full(selected_rows, (GDestroyNotify) gtk_tree_path_free);
return FALSE; // Stop callback after successful scroll
} else {
return TRUE; // Retry if path is not yet selected
}
}
static gboolean idle_scroll_to_selected_star(GtkTreeView *treeview) {
gtk_widget_queue_draw(GTK_WIDGET(treeview));
while (gtk_events_pending())
gtk_main_iteration();
return attempt_scroll(treeview);
}
void set_iter_of_clicked_psf(double x, double y) {
GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget("Stars_stored"));
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
GtkTreeIter iter;
gboolean valid;
gboolean is_as;
const double radian_conversion = ((3600.0 * 180.0) / M_PI) / 1.0E3;
double invpixscalex = 1.0;
double bin_X = com.pref.binning_update ? (double) gfit.keywords.binning_x : 1.0;
if (com.stars && com.stars[0]) {
is_as = (strcmp(com.stars[0]->units, "px"));
} else {
return;
}
if (is_as) {
invpixscalex = 1.0 / (radian_conversion * (double) gfit.keywords.pixel_size_x
/ gfit.keywords.focal_length) * bin_X;
}
valid = gtk_tree_model_get_iter_first(model, &iter);
while (valid) {
gdouble xpos, ypos, fwhmx;
gtk_tree_model_get(model, &iter, COLUMN_X0, &xpos, -1);
gtk_tree_model_get(model, &iter, COLUMN_Y0, &ypos, -1);
gtk_tree_model_get(model, &iter, COLUMN_FWHMX, &fwhmx, -1);
fwhmx *= invpixscalex;
gdouble distsq = (xpos - x) * (xpos - x) + (ypos - y) * (ypos - y);
gdouble psflimsq = 6.0 * fwhmx * fwhmx;
if (distsq < psflimsq) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
if (!path) {
g_printerr("Failed to get path from model\n");
return;
}
gtk_tree_selection_unselect_all(selection);
gtk_tree_selection_select_path(selection, path);
g_idle_add((GSourceFunc) idle_scroll_to_selected_star, treeview);
gui.selected_star = get_index_of_selected_star(xpos, ypos);
gtk_window_present(GTK_WINDOW(lookup_widget("stars_list_window")));
display_status();
redraw(REDRAW_OVERLAY);
gtk_tree_path_free(path);
return;
}
valid = gtk_tree_model_iter_next(model, &iter);
}
siril_debug_print("Point clicked does not correspond to a known star\n");
}
Unfortunately, even if the result seems a little better, there are plenty of times when the selected line is not shown on screen.
So if any GTK gurus could enlighten me I’d be most grateful!