I've been trying to figure out some method to cause a GtkTreeView to redraw after I update the bound GtkListStore from a background thread created with pthreads.
Generally, the widget does not update until something obscures an existing row (even a mouse cursor ).
Most of my searches for this problem has "your tree model doesn't/isn't generating the correct signals" ....
I'm running an old Red Hat 9 with gtk+ 2.0.0, for industrial embedded applications. Most of the data comes from ipc/socket/pipes and gets displayed by a GTK app. Unfortunately so does CRITICAL alarms, which has a habit of not showing when they should. We will (one day) move to a current kernel, but I need to get something working with the existing software.
I've tried emiting the "row-changed" signals, tried calling the gtk_widget_queue_draw and also tried connecting to the "expose-event", where I've tried various things that don't work or seg fault.
server.c
bool Server::Start()
{
// ....
// pthread_t _id;
//
pthread_create( & _id, NULL, &StaticServerThread, this );
// ....
}
viewer.c
bool Viewer::ReadFinished( SocketArgs * args )
{
gdk_threads_enter();
// Populate the buffer and message
//
// GtkListStore *_outputStore;
// gchar *buffer;
// gchar *message;
GtkTreeIter iter;
gtk_list_store_insert_with_values( _outputStore, &iter, 0,
0, buffer, 1, message, -1 );
// ....
gdk_threads_leave();
}
You can perform the updates to the list store in the main thread. For example, you can use g_idle_add() in the worker thread.
Related
I'm following this tutorial to use Lottie animations library on WatchOS. It's working until I try to change the animation while the app is running. Even though I change it, the animation stays the same until I run the watch app from Xcode. (If I simply close the app and open again it doesn't change.)
What I tried:
clearing the cache from the watch file system
clearing URLCache
clearing SDImageCache memory and disk
You were right that caching causes your problem. However, you didn't go deep enough.
You use SDWebImageLottieCoder to display your animations, which in turn uses rlottie.
According to their readme:
"rlottie is a platform independent standalone c++ library for rendering vector based animations and art in realtime."
The point is that the animation is cached by the rlottie framework, so neither clearing URLCache nor SDImageCache will solve this.
The easiest way to solve this is to modify the code in the rlottie C++ library. In your Xcode project navigate to Pods -> Pods -> librlottie and look for the following file: lottieanimation_capi.cpp
In this file search for the following code:
RLOTTIE_API Lottie_Animation_S *lottie_animation_from_data(const char *data, const char *key, const char *resourcePath)
{
if (auto animation = Animation::loadFromData(data, key, resourcePath) ) {
Lottie_Animation_S *handle = new Lottie_Animation_S();
handle->mAnimation = std::move(animation);
return handle;
} else {
return nullptr;
}
}
This is the code that loads your Lottie animation on your device and is responsible for caching. loadFromData takes an additional fourth parameter called cachePolicy, which is a boolean, and nothing is passed for it in this case.
So if you don't want your animation to be cached then pass false into this call.
Modify the code to this and it should work:
RLOTTIE_API Lottie_Animation_S *lottie_animation_from_data(const char *data, const char *key, const char *resourcePath)
{
if (auto animation = Animation::loadFromData(data, key, resourcePath, false) ) {
Lottie_Animation_S *handle = new Lottie_Animation_S();
handle->mAnimation = std::move(animation);
return handle;
} else {
return nullptr;
}
}
I'm using Gtk2 to make a small tool, it works like this:
Several Scales and Spinboxes control parameters of an algorithm.
When parameter changes, the algorithm will execute, and the updated result is rendered as a picture, shown in UI.
As the algorithm's workload is heavy, I don't want it run frequently during frequent parameter change. Specifically, during Scales are dragged or Spinbox's arrows buttons are pressed. Instead, I want the algorithm to be run "after" users have determined the parameters.
Currently, I listened the button-release event of the Scales, so the algorithm will run only on Scale dragging is done. However, this not fit for the Spinboxes, as they have separate entry and button sub-area. If I listen to Spinbox's button-release, it would behave weirdly.
So what event (or events) should I listen to obtain the occation that a continuous value update is finished for a Spinbox?
Could I see the code you have? The button-release works great for me. However, depending on your algorithm, you may be getting 'feedback'. Are you sure the rest of the code is not updating your spinbutton in some way?
I'd use a deferred computation, independent from the device you use to modify the data. In this way you can also input the numbers with the keyboard or copy and paste their content and the program will still work as expected.
A way to do this in GTK+ is by leveraging the main loop and using a timeout GSource, e.g.:
#include <gtk/gtk.h>
typedef struct {
guint event;
GSourceFunc callback;
GtkWidget *spin_button;
} Algorithm;
static gboolean your_callback(Algorithm *algorithm)
{
g_print("Your heavy computations go here...\n");
/* ... */
algorithm->event = 0;
return FALSE;
}
static void postpone(Algorithm *algorithm)
{
if (algorithm->event > 0) {
g_source_remove(algorithm->event);
}
/* Default delay is 1 second (1000 milliseconds) */
algorithm->event = g_timeout_add(1000, algorithm->callback, algorithm);
}
int main(int argc, char **argv)
{
GtkWidget *window, *spin_button;
Algorithm algorithm;
gtk_init(&argc, &argv);
spin_button = gtk_spin_button_new_with_range(0, 100, 0.1);
g_signal_connect_swapped(spin_button, "value-changed",
G_CALLBACK(postpone), &algorithm);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_add(GTK_CONTAINER(window), spin_button);
algorithm.event = 0;
algorithm.callback = (GSourceFunc) your_callback;
algorithm.spin_button = spin_button;
gtk_widget_show_all(window);
gtk_main();
return 0;
}
I have the following pseudo code to clarify my problem and a solution. My original posting and detailed results are on Stack Overflow at: Wait() & Sleep() Not Working As Thought.
public class PixelArtSlideShow { // called with click of Menu item.
create List<File> of each selected pixelArtFile
for (File pixelArtFile : List<File>) {
call displayFiles(pixelArtFile);
TimeUnits.SECONDS.sleep(5); }
}
public static void displayFiles(File pixelArtFile) {
for (loop array rows)
for (loop array columns)
read-in sRGB for each pixel - Circle Object
window.setTitle(....)
}
// when above code is used to Open a pixelArtFile, it will appear instantly in a 32 x 64 array
PROBLEM: As detailed extensively on the other post. Each pixelArtFile will display the setTitle() correctly and pause for about 5 secs but the Circle’s will not change to the assigned color except for the last file, after the 5 secs have passed. It's like all the code in the TimeUnits.SECONDS.sleep(5); are skipped EXCEPT the window.setTitle(...)?
My understanding is the TimeUnits.SECONDS.sleep(5); interrupts the UI Thread uncontrollable and I guess must somehow be isolated to allow the displayFiles(File pixelArtFile) to fully execute.
Could you please show me the most straight forward way to solve this problem using the pseudo code for a more completed solution?
I have tried Runnables, Platform.runLater(), FutureTask<Void>, etc. and I'm pretty confused as to how they are meant to work and exactly coded.
I also have the two UI windows posted on the web at: Virtual Art. I think the pixelArtFile shown in the Pixel Array window may clarify the problem.
THANKS
Don't sleep the UI thread. A Timeline will probably do what you want.
List<File> files;
int curFileIdx = 0;
// prereq, files have been appropriately populated.
public void runAnimation() {
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(5), event -> {
if (!files.isEmpty()) {
displayFile(curFileIdx);
curFileIdx = (curFileIdx + 1) % files.size();
}
})
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
// prereq, files have been appropriately populated.
public void displayFile(int idx) {
File fileToDisplay = files.get(idx);
// do your display logic.
}
Note, in addition to the above, you probably want to run a separate task to read the file data into memory, and just have a List<ModelData> where ModelData is some class for data you have read from a file. That way you wouldn't be continuously running IO in your animation loop. For a five second per frame animation, it probably doesn't matter much. But, for a more frequent animation, such optimizations are very important.
Ok, I'm writing a method that creates an entire panel and it's containing contents and adds it to the form. The panels are stored in an array.
Here's the basic idea.
void vscale1Event(GtkWidget *widget, int *vscale_id)
{
int value = gtk_range_get_value(GTK_RANGE(vscale_struct[*vscale_id]->vscale1));
do stuff with this value;
}
void add_vscale_panel(int vscale_id)
{
vscale_struct[vscale_id]->vscale1 = ..... ;
vscale_struct[vscale_id]->vscale2 = ..... ;
add buttons to form;
gtk_signal_connect(GTK_OBJECT(vscale_struct[button_id]), "value_changed", (GtkSignalFunc)vscale1Event, &vscale_id);
gtk_signal_connect(GTK_OBJECT(vscale_struct[button_id]), "value_changed", (GtkSignalFunc)vscale2Event, &vscale_id);
}
int main()
{
for (i = 0; i<n; i++)
{
add_vscale_panel(i);
}
}
The problem I'm having, is that &vscale_id that I'm passing in, later becomes junk (it's value is a junk number around 32000) when I move the scale.
But - the gtk_signal_connect is only being called that once.
Ok, I get that it's probably something to do with the call stack, that bit of memory no longer being reserved.
But I did this same thing earlier for another panel, and it's working fine.
what I've changed - is trying to make things a bit tidier.
The previous version I had all the panels and widgets each in seperate arrays.
eg
GtkWidget **outerPanel;
GtkWidget **innerPanel1;
GtkWidget **vscale1;
whereas this one I'm doing it:
typedef struct
{
GtkWidget **vscale1;
Gtkwidget **vscale2;
} V_Panel;
V_Panel **vscale_struct;
Not bothering putting the panels into arrays or structs - because I figure I don't need to access them later? ( I found that you can 'recycle' labels so I figure panels (h and vboxes), are the same.
Also - an interesting clue - when I run valgrind - it works fine. Some how valgrind changes the way the program uses it's memory.
Any help here?
If you can perhaps explain what's happening when you call gtk_signal_connect. -
Here's my actual code: http://pastebin.com/MGfUihjM
relevant lines are
45, 145, 274, 308, 391
The problem is that your taking the address of a variable on the stack - in this case the parameter to the function. That address in memory is definitely not guaranteed to continue to hold the value you expect it to since it is just part of the stack frame
The correct way to pack your integer value_id into the callback userdata pointer is to use GINT_TO_POINTER and to reverse it using GPOINTER_TO_INT.
So your signal connection would be:
gtk_signal_connect(GTK_OBJECT(vscale_struct[button_id]),
"value_changed",
(GtkSignalFunc)vscale1Event,
GINT_TO_POINTER(value_id));
And in your signal handler would look like:
void vscale1Event(GtkWidget *widget, gpointer userdata)
{
int vscale_id = GPOINTER_TO_INT (userdata);
int value = gtk_range_get_value(GTK_RANGE(vscale_struct[vscale_id]->vscale1));
do stuff with this value;
}
I'm writing my first gtk program, using gtkmm, and glade.
I made a filechooserbutton and it has a signal called file-set
So I set that to what I assume is the function name I want it to call when the file is chosen.
But then I see here:
http://library.gnome.org/devel/gtkmm-tutorial/unstable/sec-builder-accessing-widgets.html.en
That they're manually getting the dialog widget and setting a button signal handler in the code.
Which is the right way to do it?
And while I'm here any links to good examples would be handy, they seem to be few and far between. Thanks.
This is how I did it:
// create the UI
refUI = Gtk::Builder::create();
refUI->add_from_file(grq::GLADE_FILE);
// grab your widget
refUI->get_widget("but_new", but_new); // Gtk::ToolButton *but_new;
but_new->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_new_game));
// your signal handler looks something like this :)
void MainWindow::on_new_game() {}
edit:
Basically the *this is the object on which you will be calling the function your signal handler.
This is what my main looks like:
int main(int argc, char **argv) {
Gtk::Main kit(argc, argv);
MainWindow main_window;
kit.run(*main_window.window);
return 0;
}
MainWindow is basically a class that wraps GtkWindow and defines the widgets, a. la.:
class MainWindow
{
private:
Glib::RefPtr<Gtk::Builder> refUI;
//
// Widgets
//
Gtk::ToolButton *but_about;
public:
// The window. This is public so we can hook into events and
// call kit.run(window) against it, if needed.
Gtk::Window *window;
MainWindow()
{
// Load the data for this window and it's widgets.
refUI = Gtk::Builder::create();
refUI->add_from_file(grq::GLADE_FILE);
// The window
refUI->get_widget("main_window", window);
// Widgets
refUI->get_widget("but_about", but_about);
but_about->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_about));
...
}
virtual ~MainWindow()
{
if (window != NULL)
{
delete window; // Frees all the children for the window, too.
}
}
virtual void on_about()
{
// stuff
}
};
Hope this helps!
I found the answer to my question as an afterthought in another stackoverflow question.
But I don't remember which one it was.
The answer seems to be that you have to programmatically add the signal handler to the widget in your code, the gtkbuilder won't do it for you.