Gtk# window movement freezes when multi-threaded - multithreading

I'm currently trying to use multi-threaded Gtk# under Windows.
Everything works fine except for this little bug that makes the application unusable : you can't move or resize the app, because it freezes.
It looks like a potential bug in Gtk#.
Here's a sample that reproduces the issue : a simple window, a label and a button. The label and button are not necessary, but provide the proof that otherwise Gtk is performing normally. Whenever I move the windows, maximize it, it stalls.
using System;
using Gtk;
namespace FreezingWindow
{
class MainClass
{
public static void Main (string[] args)
{
if (GLib.Thread.Supported) {
GLib.Thread.Init ();
}
Gdk.Threads.Init ();
Gdk.Threads.Enter ();
Application.Init ();
Window win = new Window ("test");
var box = new VBox ();
var label = new Label ("Test");
box.PackStart (label);
var btn = new Button ("Test");
btn.Clicked += delegate(object sender, EventArgs e) {
label.Text = DateTime.Now.ToString();
};
box.PackStart (btn);
win.Add (box);
win.ShowAll ();
Application.Run ();
Gdk.Threads.Leave ();
}
}
}

Here's how I work with multi-threading + GUI changes. I completely avoid the threading methods you've utilized and stictly use the System.Threading namespace. It creates true multi-threaded applications with responsive GUI. (Also, I typically create a separate class that is strictly for the form and all of its methods.)
// Create the thread and tell it to execute the DoThreadStuff method.
Thread thread = new Thread(new ThreadStart(DoThreadStuff));
thread.Start();
Update the GUI by utilizing Application.Invoke.
private void DoThreadStuff()
{
// Stuff that is processing on the separate thread.
// Need to use Application.Invoke if updating the GUI from the thread.
Application.Invoke(delegate {
label.Text = string.Format("Did stuff at {0}", DateTime.Now);
});
}
Another method I like to use is to create a generic method that accepts a method name that we need to execute under Application.Invoke.
private void InvokeMethod(System.Action methodName)
{
Application.Invoke(delegate {
methodName();
});
}

Related

construct GUI with event dispatch thread and make assignment

I'd like to do the following without making the variable gui final:
public class MainClass
{
GUIClass gui;
Runnable r = new Runnable()
{
public void run()
{
// this won't work!
gui = new GUIClass();
}
};
SwingUtilities.invokeLater(r);
Controller c = new Controller(gui);
}
How can I achieve that? I want to construct the gui via the EDT. At the same time I want to assign that new instance to the variable gui. But this won't work without making gui final. I don't want to use final because it's not possible in my context. Anyone any idea how this could be solved? The code above is of course executed within the main method. But for some reasons I couldn't post it here as an error occured.
For all those who should be interested in this issue someday, here's the solution:
I made a mistake. Actually you should differentiate between constructing the GUI-Object and making it visible. So here is the way to do that:
public class MainClass
{
final GUIClass gui = new GUIClass();
Runnable r = new Runnable()
{
public void run()
{
gui.pack();
gui.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
Controller c = new Controller(gui);
}

c# wanting multiple ui threads but getting cross-reference errors instead

i'm still very new at c#, threads and forms. i'm writing a small data acquistion program. it has two threads: the main ui thread and a sensor polling/logging/charting thread. when the user clicks the "start-logging" button, it it continuously polls the sensors (over a virtual COM port), writes the response to a file, updates the main form with some basic polling stats (how many pollings per second). if the user has clicked a "monitor" button, it opens a charting form and the polling thread invokes a methods that that adds the sensors values to the chart.
i have a version of this program that works very well but i found that if i have multiple charts open (so that i can view multiple sensors in realtime), the chart updates become sporadic or stop and only the window with the focus updates smoothly. (the comm port is only 56kbaud so it's not like the polling is being swamped with data.)
so i got the "bright" idea to make charting threads, thinking this would provide multiple UI loops and would produce nice smooth charting on multiple chart forms. below is simplified code; e.g. here, the charting thread is started with the polling thread instead of when the user clicks the "monitor" button. it compiles, but when it runs, i get a cross-reference error at the point when the update_chart method is called.
seems i have a fundamental misunderstanding of several things about threads and control ownership. the chart was made in the "charting" thread, but when the "polling" thread invokes the update_chart method, the code shows that update_chart methods is being run by the "main_ui" thread. i'm open to any suggestions/advise that'll give me smooth charting and stats updates. thanks.
namespace WindowsFormsApplication1
{
public partial class Main_Form : Form
{
delegate void UpdateUIStatsDelegate(string update);
UpdateUIStatsDelegate update_stats_delegate;
static BackgroundWorker polling_thread = new BackgroundWorker();
static BackgroundWorker charting_thread = new BackgroundWorker();
public static Chart_Form chart_form = new Chart_Form();
public Main_Form()
{
Thread.CurrentThread.Name = "main_ui";
update_stats_delegate = new UpdateUIStatsDelegate(update_stats);
polling_thread.DoWork += polling_thread_DoWork;
charting_thread.DoWork += charting_thread_start;
}
private void start_logging_Click(object sender, EventArgs e)
{
start_polling_thread();
start_charting_thread();
}
private void start_polling_thread()
{
polling_thread.RunWorkerAsync();
}
private void polling_thread_DoWork(object sender, DoWorkEventArgs e)
{
string sensor_values;
Thread.CurrentThread.Name = "polling";
while (true)
{
sensor_values = poll_the_sensors_and_collect_the_responses();
log_to_file(sensor_values);
// BeginInvoke(chart_form.update_chart_delegate, new object[] { sensor_values });
chart_form.BeginInvoke(chart_form.update_chart_delegate, new object[] { sensor_values });
pps = compute_polling_performance();
BeginInvoke(update_stats_delegate, new object[] { pps.ToString("00") });
}
}
private void update_stats(string stat)
{
string tn = Thread.CurrentThread.Name;
// this says "main_ui", but i don't get a cross-reference error
pollings_per_second.Text = stat;
}
private void start_charting_thread()
{
charting_thread.RunWorkerAsync();
}
private void charting_thread_start(object sender, DoWorkEventArgs e)
{
Thread.CurrentThread.Name = "charting";
Chart_Form chart_form = new Chart_Form();
chart_form.Show();
while (charting_is_active) { }
}
}
public partial class Chart_Form : Form
{
public delegate void UpdateChartDelegate(string sensor_values);
public UpdateChartDelegate update_chart_delegate;
public Chart_Form()
{
string tn = Thread.CurrentThread.Name;
update_chart_delegate = new UpdateChartDelegate(update_chart);
this.Text = "a realtime plot of sensor values";
}
private void update_chart(string sensor_values)
{
string tn = Thread.CurrentThread.Name;
// this says "main_ui" and i get a cross reference error; set below.
int x = extract_x_value(sensor_values);
int y = extract_y_value(sensor_values);
chart1.Series[X_AXIS].Points.AddY(x); // <<--- i get a cross-reference runtime error here...
chart1.Series[Y_AXIS].Points.AddY(y);
}
}
}

JavaFX working with threads and GUI

I have a problem while working with JavaFX and Threads. Basically I have two options: working with Tasks or Platform.runLater. As I understand Platform.runLater should be used for simple/short tasks, and Task for the longer ones. However, I cannot use any of them.
When I call Thread, it has to pop up a captcha dialog in a middle of task. While using Task, it ignores my request to show new dialog... It does not let me to create a new stage.
On the other hand, when I use Platform.runLater, it lets me show a dialog, however, the program's main window freezes until the pop up dialog is showed.
I need any kind of solution for this. If anyone knows how to deal with this or had some similar experience and found a solution I am looking forward to hearing from you!
As puce says, you have to use Task or Service for the things that you need to do in background. And Platform.runLater to do things in the JavaFX Application thread from the background thread.
You have to synchronize them, and one of the ways to do that is using the class CountDownLatch.
Here is an example:
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
//Background work
final CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
//FX Stuff done here
}finally{
latch.countDown();
}
}
});
latch.await();
//Keep with the background work
return null;
}
};
}
};
service.start();
Use a Worker (Task, Service) from the JavaFX Application thread if you want to do something in the background.
http://docs.oracle.com/javafx/2/api/javafx/concurrent/package-summary.html
Use Platform.runLater from a background thread if you want to do something on the JavaFX Application thread.
http://docs.oracle.com/javafx/2/api/javafx/application/Platform.html#runLater%28java.lang.Runnable%29
It's too late to answer but for those who have the error, here is the solution XD
You can use one Thread.
Use the lambda expression for the runnable in the thread and the runlater.
Thread t = new Thread(() -> {
//Here write all actions that you want execute on background
Platform.runLater(() -> {
//Here the actions that use the gui where is finished the actions on background.
});
});
t.start();
You can user directly this code
Don't forget you can't send non-final variable in thread .
you can send final variable in thread
//final String me="ddddd";
new Thread(new Runnable() {
#Override
public void run() {
// me = me + "eee";
//...Your code....
}
}).start();
Use in
your code
try/catch

Is it a good idea to use repaint() as a game loop?

class MyCanvas extends Canvas{
protected void paint(Graphics g) {
//Process keyboard
//Update movement/position
//Draw
repaint(); //loop
}
}
Until now I used the Canvas's paint() for the game loop, but I came across some article in the web that says that another thread should be used here
Now I'm wondering if paint() is a good/safe place to process all the data.
So can I continue doing it like this?
Or should I make another thread for that?
I'm not sure of pros and cones of each so I'm not sure which method to choose but I got used to repaint method
I would not use paint() that way, no. paint() should be for painting ... drawing. I would split your monitoring of user input, and game logic processing, outside that method.
Have you considered using the GameCanvas subclass of Canvas?
It gives you some nice double-buffering features. You would create another thread, which would call your GameCanvas' run() method, where it would check for user input, update the game logic, then draw to the off-screen buffer, and finally trigger repainting of the on-screen buffer.
Something like this:
class MyGameCanvas extends GameCanvas implements Runnable {
/** start this game! */
public void start() {
Thread worker = new Thread(this);
worker.start();
}
/** run the game loop */
public void run() {
// Get the Graphics object for the off-screen buffer
Graphics g = getGraphics();
while (true) {
// Check user input and update positions if necessary
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED) != 0) {
sprite.move(-1, 0);
}
else if ((keyState & RIGHT_PRESSED) != 0) {
sprite.move(1, 0);
}
// Clear the background to white
g.setColor(0xFFFFFF);
g.fillRect(0,0,getWidth(), getHeight());
// Draw the Sprite
sprite.paint(g);
// Flush the off-screen buffer
flushGraphics();
try {
// TODO: of course, you might want a more intelligent
// sleep interval, that reflects the amount of time
// remaining (if any) in the cycle ...
Thread.sleep(10); //sleep 10 ms
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Note that I put basically everything in the run() method, for brevity. I'm assuming your game is more complicated, and would warrant splitting off separate methods for getting user input, updating logic, and then calling graphics/paint methods. Those would all be called from run().
Usage
Start this in your MIDlet's startApp() method:
MyGameCanvas gameCanvas = new MyGameCanvas();
gameCanvas.start();
exitCommand = new Command("Exit", Command.EXIT, 1);
gameCanvas.addCommand(exitCommand);
gameCanvas.setCommandListener(this);
Display.getDisplay(this).setCurrent(gameCanvas);
References
http://www.codeproject.com/Articles/35833/Programming-2D-Games-in-J2ME
an example that shows a better implementation, if you don't use GameCanvas and just use Canvas.
PowerPoint overview of Game APIs and looping , with code and general theory

repainting multiple JPanel from a single "control" panel

so i'm trying to set up an application where i have multiple panels inside a jframe. lets say 3 of them are purely for display purposes, and one of them is for control purposes. i'm using a borderLayout but i don't think the layout should really affect things here.
my problem is this: i want the repainting of the three display panels to be under the control of buttons in the control panel, and i want them to all execute in sync whenever a button on the control panel is pressed. to do this, i set up this little method :
public void update(){
while(ButtonIsOn){
a.repaint();
b.repaint()
c.repaint();
System.out.println("a,b, and c should have repainted");
}
}
where a,b, and c are all display panels and i want a,b,and c to all repaint continously until i press the button again. the problem is, when i execute the loop, the message prints in an infinite loop, but none of the panels do anything, ie, none of them repaint.
i've been reading up on the event dispatch thread and swing multithreading, but nothing i've found so far has really solved my problem. could someone give me the gist of what i'm doing wrong here, or even better, some sample code that handles the situation i'm describing? thanks...
The java.util.concurrent package provides very powerful tools for concurrent programing.
In the code below, I make use of a ReentrantLock (which works much like the Java synchronized keyword, ensuring mutually exclusive access by multiple threads to a single block of code). The other great thing which ReentrantLock provides are Conditions, which allow Threads to wait for a particular event before continuing.
Here, RepaintManager simply loops, calling repaint() on the JPanel. However, when toggleRepaintMode() is called, it blocks, waiting on the modeChanged Condition until toggleRepaintMode() is called again.
You should be able to run the following code right out of the box. Pressing the JButton toggle repainting of the JPanel (which you can see working by the System.out.println statements).
In general, I'd highly recommend getting familiar with the capabilities that java.util.concurrent offers. There's lots of very powerful stuff there. There's a good tutorial at http://docs.oracle.com/javase/tutorial/essential/concurrency/
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class RepaintTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel()
{
#Override
public void paintComponent( Graphics g )
{
super.paintComponent( g );
// print something when the JPanel repaints
// so that we know things are working
System.out.println( "repainting" );
}
};
frame.add( panel );
final JButton button = new JButton("Button");
panel.add(button);
// create and start an instance of our custom
// RepaintThread, defined below
final RepaintThread thread = new RepaintThread( Collections.singletonList( panel ) );
thread.start();
// add an ActionListener to the JButton
// which turns on and off the RepaintThread
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
thread.toggleRepaintMode();
}
});
frame.setSize( 300, 300 );
frame.setVisible( true );
}
public static class RepaintThread extends Thread
{
ReentrantLock lock;
Condition modeChanged;
boolean repaintMode;
Collection<? extends Component> list;
public RepaintThread( Collection<? extends Component> list )
{
this.lock = new ReentrantLock( );
this.modeChanged = this.lock.newCondition();
this.repaintMode = false;
this.list = list;
}
#Override
public void run( )
{
while( true )
{
lock.lock();
try
{
// if repaintMode is false, wait until
// Condition.signal( ) is called
while ( !repaintMode )
try { modeChanged.await(); } catch (InterruptedException e) { }
}
finally
{
lock.unlock();
}
// call repaint on all the Components
// we're not on the event dispatch thread, but
// repaint() is safe to call from any thread
for ( Component c : list ) c.repaint();
// wait a bit
try { Thread.sleep( 50 ); } catch (InterruptedException e) { }
}
}
public void toggleRepaintMode( )
{
lock.lock();
try
{
// update the repaint mode and notify anyone
// awaiting on the Condition that repaintMode has changed
this.repaintMode = !this.repaintMode;
this.modeChanged.signalAll();
}
finally
{
lock.unlock();
}
}
}
}
jComponent.getTopLevelAncestor().repaint();
You could use SwingWorker for this. SwingWorker was designed to perform long running tasks in the background without blocking the event dispatcher thread. So, you need to extend SwingWorker and implement certain methods that will make sense to you. Note that all long running action should happen in the doInBackground() method, and the Swing UI elements should be updated only on the done() method.
So here is an example :
class JPanelTask extends SwingWorker<String, Object>{
JPanel panel = null;
Color bg = null;
public JPanelTask(JPanel panel){
this.panel = panel;
}
#Override
protected String doInBackground() throws Exception {
//loooong running computation.
return "COMPLETE";
}
#Override
protected void done() {
panel.repaint();
}
}
Now, in your "control" button's action performed event, you could do the following :
controlButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
JPanelTask task1 = new JPanelTask(panel1);
task1.execute();
JPanelTask task2 = new JPanelTask(panel2);
task2.execute();
//so on..
}
});
Another way is using javax.swing.Timer. Timer helps you to fire a change to your ui elements in a timely fasthion.This may not be the most appropriate solution. But it gets the work done too.
Again you should be careful about updating UI elements in right places.

Resources