Optional Ref (Regarding bulding gui and widgets in java)
AS said here i am making a simple clock widget. I chose SWt because i found it easier to learn and implement. The widget consists of several layers of concentric circles to impart different colors to each layer. The color of each layer depends on the time of the day and is controlled by a color function. The layers are ready but color function still needs to be made.
I was going through the docs of Java and found that all graphic objects ( like rectangles and circles) must be manually disposed to free system resources. Now my basic problem is this :
Basically i want the widget to run indefinitely until the window containing widget is closed( because there is a minute layer and hour layer which change colors).
How will i free the system resources and will the widget be a memory monster coz of infinteness ? Please answer with ref to Swt.
Additionally i wanted to know if this tyoe of animation strategy is suitable for this widget ? If not please suggest other alternatives keeping in mind my beginner level.
For the clock you need:
A widget to display the time (for example org.eclipse.swt.widgets.Canvas with a PaintListener to draw the clock)
A (daemon) thread which redraws your clock each second. The redraw call must be delegated to the EDT (event dispatch thread)
The thread should run as long as your widget isn't disposed.
To clean up any resources (fonts, colors, etc.) when your widget is disposed, use DisposeListener.
Code template:
public class ClockWidget extends Canvas {
public ClockWidget (Composite parent, int style) {
super(parent, style | SWT.DOUBLE_BUFFERED);
addPaintListener(new PaintListener() {
#Override
public void paintControl (PaintEvent e) {
GC gc = e.gc;
// paint clock on the graphics context
}
});
addDisposeListener(new DisposeListener() {
#Override
public void widgetDisposed (DisposeEvent e) {
// dispose all fonts and colors you created
}
});
final Display display = Display.getCurrent();
Thread timer = new Thread() {
#Override
public void run () {
while (!isDisposed()) {
display.syncExec(new Runnable() {
#Override
public void run () {
if (!isDisposed()) {
redraw();
}
}
});
long msToNextSec = 1000 - (System.currentTimeMillis() % 1000);
try {
Thread.sleep(msToNextSec);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
timer.setPriority(Thread.MIN_PRIORITY);
timer.setDaemon(true);
timer.start();
}
}
Related
want to animate my logo by zooming it to an splash screen.
tried the following so far:
#Override
protected void postSpash1(Form f) {
findLogoLabel(f).setPreferredW(findLogoLabel(f).getPreferredW() * 5);
findAnimateContainer(f).animateLayoutAndWait(2000);
findLogoLabel(f).setPreferredSize(null);
}
The image doesn't animate and enlarges itself. However it changes its size abruptly after 2 sec which is not what I'm looking for. I want the zooming effect(the img should grow bigger slowly to a certain extent)
I tried other ways too, but the label moves from up to down smoothly instead of bg img getting bigger
findLogoLabel(f).setPreferredH(findLogoLabel(f).getPreferredH() * 3);
findAnimateContainer(f).animateLayoutAndWait(2000);
findLogoLabel(f).setPreferredSize(null);
PS: I set the bg image in logoLabel (Label) , Animatecontainer is the container that contains logoLabel.
Blocking API's are somewhat tricky as they might collide with some other behaviors preventing the rest of the current dispatch thread from finishing its job.
Also you set the null after animating instead of before...
So in your case this should work (with old Java 5 syntax):
#Override
protected void postSpash1(final Form f) {
findLogoLabel(f).setPreferredW(findLogoLabel(f).getPreferredW() * 5);
Display.getInstance().callSerially(new Runnable() {
public void run() {
findLogoLabel(f).setPreferredSize(null);
findAnimateContainer(f).animateLayoutAndWait(2000);
}
});
}
With cool Java 8 lambdas:
#Override
protected void postSpash1(final Form f) {
findLogoLabel(f).setPreferredW(findLogoLabel(f).getPreferredW() * 5);
Display.getInstance().callSerially(() -> {
findLogoLabel(f).setPreferredSize(null);
findAnimateContainer(f).animateLayoutAndWait(2000);
});
}
I am writing the Sugarscape simulation in Java and need a working GUI. Sugarscape is a spatial landscape consisting of tiles (of sugar), and agents moving and consuming sugar. For simplicity, I have only one agent and no sugar- I just want to see the agent moving.
For the past 2 weeks I have read into painting in java, concurrency in java, concurrency in swing, I have read filthy rich clients and countless StackOverflow threads, but I must resort to asking a question here.
I need my model separate from the GUI. This presents a problem since 99% of tutorials suggest to call for repaint within other methods. My idea was to run one "tick" of the simulation: all agents move, and then send an Event (my GUI class extends Observer) which then triggers a repaint(); request and update the GUI. However the problem (the misunderstanding) lies with the SwingUtilities.InvokeLater method. My code is:
public void setupGUI()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
System.out.println("GUI is being setup, on EDT now? " + SwingUtilities.isEventDispatchThread());
SugarFrame frame = new SugarFrame(simulation.getWorld());
frame.setVisible(true);
}
});
}
For understanding what is happening I have inserted println's everywhere. The order of events is what confuses me:
Console output:
1.Agent created. Starting Position: X= 19 Y= 46 // This is in the Agent constructor
2.Simulation start. Experiment number: 0
GUI is being setup, on EDT now? true // As you see above, this is WITHIN the SwingUtilities.InvokeLater section. But then the EDT pauses and the real model continues:
Tick number 0
Invoke Agent Actions, fire TickStart Event
TickStartEvent created
Invoke Agent Actions, for-loop starting now
Agent number 0 moving now:
Consuming Sugar now.
Moving now.
Sleeping now.
The Sugarframe has been created and Grid added. All on EDT? true // And there it is back again. The paint component follows and the window with the Agent visible appears.
paintComponent called, on EDT? true
Now, I have read that by putting the main thread to sleep, you give the EDT time to run the repaint. However, this only happens once. Repaint is never called again, and I only ever see one iteration of the model.
I simply do not understand what piece of information I am missing to work with the EDT properly. Swingworker and Swingtimer are suggested regularly, but for every suggestion there is a notion that they are not needed for a model such as mine. Either paintComponent is not called at all, or queued up until the end (and then still not repainting, even if I use thread.sleep).
I'd appreciate any help. Apologies for the long post.
//Edit: as per request some more code.
The entire main method:
public class SimulationController {
static Simulation simulation;
public static final int NUM_EXPERIMENTS = 1;
public SimulationController()
{
Random prng = new Random();
SimulationController.simulation = new Simulation(prng);
}
public void run() {
setupGUI();
for(int i=0; i<NUM_EXPERIMENTS; i++) {
System.out.println("Simulation start. Experiment number: " + i);
simulation.getWorld().addObserver(simulation);
simulation.addObserver(simulation.getWorld());
simulation.run();
}
}
public void setupGUI()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
System.out.println("GUI is being setup, on EDT now? " + SwingUtilities.isEventDispatchThread());
SugarFrame frame = new SugarFrame(simulation.getWorld());
frame.setVisible(true);
}
});
}
public static void main(String[] args) {
SimulationController controller = new SimulationController();
controller.run();
}
}
The paint override in my JPanel class:
#Override
public void paintComponent(Graphics g) {
System.out.println(">>>>>>>>paintComponent called, on EDT? " + SwingUtilities.isEventDispatchThread()+"<<<<<<<<<<");
super.paintComponent(g);
//g.clearRect(0, 0, getWidth(), getHeight());
rectWidth = getWidth() / world.getSizeX();
rectHeight = getHeight() / world.getSizeY();
for (int i = 0; i < world.getSizeX(); i++)
{
for (int j = 0; j < world.getSizeY(); j++)
{
// Upper left corner of this terrain rect
x = i * rectWidth;
y = j * rectHeight;
Tile tile = world.getTile(new Position(i, j));
if (tile.hasAgent())
{
g.setColor(Color.red);
} else
{
g.setColor(Color.black);
}
g.fillRect(x, y, rectWidth, rectHeight);
}
}
}
JPanel class again, update methods:
public void update(Observable o, Object arg)
{
if (arg instanceof TickEnd)
{
TickEvent tickEndevent = new TickEvent();
this.addTickEvent(tickEndevent);
}
}
}
private final BlockingQueue<TickEvent> TICK_EVENTS = new LinkedBlockingQueue<TickEvent>();
/**Runnable object that updates the GUI (I think)**/
private final Runnable processEventsRunnable = new Runnable()
{
public void run()
{
TickEvent event = new TickEvent();
while ((event = TICK_EVENTS.poll()) != null)
{
System.out.println("This is within processEventsRunnable, inside the While loop. Repaint is called now.");
repaint();
}
}
};
/**Add Event to the processing-Events-queue**/
public void addTickEvent(TickEvent event)
{
//System.out.println("This is in the Add TickEvent method, but before the adding. "+TICK_EVENTS.toString());
TICK_EVENTS.add(event);
System.out.println("TickEvent has been added! "+TICK_EVENTS.toString() + "On EDT?" + SwingUtilities.isEventDispatchThread());
if (TICK_EVENTS.size() >= 1)
{
SwingUtilities.invokeLater(processEventsRunnable);
}
}
And last but not least, the JFrame constructor:
/** Sugarframe Constructor**/
public SugarFrame(World world)
{
super("Sugarscape"); // creates frame, the constructor uses a string argument for the frame title
grid = new Grid(world); // variable is declared in the class
add(grid);
setDefaultCloseOperation(EXIT_ON_CLOSE); // specifies what happens when user closes the frame. exit_on_close means the program will stop
this.setContentPane(grid);
this.getContentPane().setPreferredSize(new Dimension(500, 500));
this.pack(); // resizes frame to its content sizes (rather than fixed height/width)
System.out.println("The Sugarframe has been created and Grid added. All on EDT? "+ SwingUtilities.isEventDispatchThread());
this.setVisible(true); // makes the Frame appear on screen
}
The sentences,
I need my model separate from the GUI. This presents a problem since 99% of tutorials suggest to call for repaint within other methods.
and
Now, I have read that by putting the main thread to sleep, you give the EDT time to run the repaint.
don't sound quite right to me, so I'll try to clear things up a bit and maybe If you reevaluate the fundamental ideas you had behind those statements you can find the piece of information that you were missing.
First of all, always keep in mind this scheduling model that we were talking about. You can not say "EDT do this for me now!". It is always "EDT here's one more task you need to do, do it when you are done with whatever you are doing". So the EDT has a queue of "tasks" to do and goes through it consuming one by one.
These tasks are usually created by events: pressing a button gives the EDT a task to do, when the state of a component of the GUI changes some listeners may be notified and enqueue some work in the EDT. However, you can also straight up say "EDT execute this piece of code, later". This is what you do with invokeLater, you schedule a work to do in the EDT whenever it's free. Even if you call invokeLater from the EDT the task is scheduled, not executed at the moment.
The same happens with invokeAndWait yes, the code is executed sequentially as if it was executed at the moment, but it is still an scheduled work. So repaint() is no exception to this. repaint() doesn't repaint the GUI, but rather schedules the repainting of the GUI.
However repaint() is exceptional in the sense that it can be called from outside the EDT! This is not surprising now that we know that the only thing that does is scheduling a certain work, it does not actually mess with the GUI so you can call it wherever you want.
This means that the line
SwingUtilities.invokeLater(processEventsRunnable);
where processEventsRunnable basically executes a repaint() is meaningless and the whole tick system overly complex and unnecesary. You just have to call repaint() when you change something on the GUI or on the data that the GUI feeds on so the changes are reflected on the screen.
Furthermore, if you wanted to do something that needs to be executed in the EDT (like changing the text of a Label with the score) you can just put that code in an invokeLater block in your main thread. That will queue and execute the task properly, you don't need to do your own event queue system.
Keeping all this in mind the following makes no sense:
I have read that by putting the main thread to sleep, you give the EDT time to run the repaint
The GUI will be updated on its own shortly after you call repaint(). The main doing a lot of things and calling a lot of repaints does not prevent the GUI from being updated. However, if you want to "sleep" the main so the pace of the changes is slow so the user can appreciate it on the screen, you should use a timer.
So, as long as your main is not accessing GUI values and methods, feel free to call repaint whenever you are done changing the data, periodically or not.
Edit: Also it sounds a little bit weird that you have a main thread doing things. As you read in the concurrency chapter, usually you just create the GUI in the EDT and then the application is mostly event-driven when buttons are pressed and such. If you need to do changes periodically use a timer. You can use auxiliar threads to do specific non-GUI related heavy work, like reading a file. But you don't usually have an auxiliar thread permanently active as part of the design.
The following is a very simple program that moves an square periodically. I just use a timer to change the data and call repaint(). Note that I'm using a SwingTimer (it is executed in the EDT) since I wanted to check the panel width. Otherwise I could run the code of the timer in any thread.
In your case you probably have your "map" stored independently of the GUI, so you just need to check that data to properly move the coordinates of the agent whenever you want (on keyboard press, periodically...).
It looks like this:
Full code:
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingSquareTest
{
int x, y, size, step;
MyPanel panel;
Timer timer;
public static final void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
MovingSquareTest app = new MovingSquareTest();
app.createAndShowGUI();
app.timer.start();
}
});
}
public MovingSquareTest()
{
x = 0;
y = 150;
size = 50;
step = 50;
timer = new Timer(500, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
x += step;
if (x < 0) x = 0;
if (x + size > panel.getWidth()) x = panel.getWidth() - size;
if (x == 0 || x + size == panel.getWidth()) step *= -1;
panel.repaint();
}
});
}
public void createAndShowGUI()
{
JFrame frame = new JFrame("Dance, my square!");
panel = new MyPanel();
frame.add(panel);
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private class MyPanel extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawRect(x, y, size, size);
}
}
}
So I have seven panels of different colors that need to be rotated in order. My code is working well for the most part but the first and last panels always have same color. How do I fix this?
I have already checked that each of my panels have a different color upon start.
Code:
public void run()
{
TimerTask colorAction = new TimerTask(){
public void run()
{
redPanel.setBackground(orangePanel.getBackground());
orangePanel.setBackground(yellowPanel.getBackground());
yellowPanel.setBackground(greenPanel.getBackground());
greenPanel.setBackground(bluePanel.getBackground());
bluePanel.setBackground(indigoPanel.getBackground());
indigoPanel.setBackground(violetPanel.getBackground());
violetPanel.setBackground(redPanel.getBackground());
}
};
java.util.Timer utilTimer = new java.util.Timer();
utilTimer.scheduleAtFixedRate(colorAction, START_AFTER, DELAY );
}
Snapshot (before change):
Snapshot (after change)
The basic problem, other then the fact that you are violating the single thread rules of Swing, is you are relying on a value from component whose background has already changed...
violetPanel.setBackground(redPanel.getBackground());
redPanel's background is now set to orangePanel background by the time you call this.
Instead, first grab redPanel's background color before you change anything, then apply it to violetPanel
Color redBackground = redPanel.getBackground();
redPanel.setBackground(orangePanel.getBackground());
//...
violetPanel.setBackground(redBackground);
Take a look (and get your teacher to do the same) at Concurrency in Swing and How to Use Swing Timers for more details...
If you MUST use a java.util.Timer, you should be wrapping your changes to UI in an invokeLater call, for example...
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Color redBackground = redPanel.getBackground();
redPanel.setBackground(orangePanel.getBackground());
//...
violetPanel.setBackground(redBackground);
}
});
I'm trying to set the background color of an ImageView over a specified amount of time (pauses in between each switch of color) based on a certain criteria (such as string = "a" or "b"). My problem is I can't seem to make the entire application wait for one change until it moves on to the next, so to the user it just looks like the color starts then is immediately the last color.
I've tried CountDownTimers (which just continue to execute while the other timer is running), handlers (postDelayed(null, 5000)), Thread.Sleeps etc.
Here's an example of what I'm trying to do:
Set color on ImageView to red
sleep for 500ms
for(int i = 0; i < stringArray.length; i++){
if(stringCompare = "a") {
Set color on ImageView to blue
sleep for 500ms }
else if(stringCompare = "b") {
Set color on ImageView to blue
sleep for 1000ms
}
Set color on ImageView to red
sleep for 500ms
}
I know that's kind of crazy, but I've tried all the above methods I could think of with no success on actually making the program wait, but not completely stopping it.
Thanks.
For the past two decades, most GUIs have been based on event-driven models. Some, such as Android, use a single-threaded event-driven model. Your request to change a color does not take immediate effect; rather, it puts a message on a work queue, to be processed by the main application thread. That main application thread is also what is running your code, unless you specifically move that code to a background thread.
As a result, never sleep on the main application thread. Otherwise, that thread is tied up and cannot process any sort of GUI events, let alone your request to change the color.
postDelayed() (on View or Handler) will allow you to schedule code to be run in the future on the main application thread, after a delay, without tying up the main application thread during that delay.
Put this in your activity (make it an inner class):
private class ColorChanger extends Thread
{
final ImageView imageView;
public ColorChanger(ImageView imageView)
{
this.imageView = imageView;
}
private void changeColor(final int color)
{
YourActivity.this.runOnUiThread(new Runnable() {
public void run() {
// Set imageView to color here
}
});
}
#Override
public void run() {
try {
Thread.sleep(500);
changeColor(0xffff0000);
Thread.sleep(500);
changeColor(0xff0000ff);
// Etc.
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Then you can just do:
new ColorChanger(/*Your imageview*/).start();
Anywhere in your activity.
Create a separate thread that does your pause and run. From that thread post messages to the UI thread to change the color of its imageview.
I'm trying to create a program in Java that would display set of images one after another adjusting the size of the frame for each one.
I'm extending JPanel to display an image like this:
public class ImagePanel extends JPanel{
String filename;
Image image;
boolean loaded = false;
ImagePanel(){}
ImagePanel(String filename){
loadImage(filename);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if(image != null && loaded){
g.drawImage(image, 0, 0, this);
}else{
g.drawString("Image read error", 10, getHeight() - 10);
}
}
public void loadImage(String filename){
loaded = false;
ImageIcon icon = new ImageIcon(filename);
image = icon.getImage();
int w = image.getWidth(this);
int h = image.getHeight(this);
if(w != -1 && w != 0 && h != -1 && h != 0){
setPreferredSize(new Dimension(w, h));
loaded = true;
}else{
setPreferredSize(new Dimension(300, 300));
}
}
}
Then in event thread I'm doing main work:
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
createGUI();
}
});
In createGUI() I'm going through the set of images:
ImagePanel imgPan = new ImagePanel();
add(imgPan);
for(File file : files){
if(file.isFile()){
System.out.println(file.getAbsolutePath());
imgPan.loadImage(file.getAbsolutePath());
pack();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The problem is that my program does the resizing properly, so images are loaded correctly but it doesn't display anything.
If I display only 1 image it works also it works for the last image. I think the problem is that Thread.sleep() is called before image painting is finished.
How can I wait for my ImagePanel to finish painting and start waiting after that?
Or is there another way to solve the problem?
Thanks!
Leonty
All of your code is executing on the Event Dispatch Thread. This effectively causes all user interaction to sleep, since the Event Dispatch Thread is responsible for all user interaction - both input (events) and output (painting).
You need to get your waiting to happen outside of the EDT. You need to know how to execute events on and off the EDT. You do this by creating a new Runnable and then either calling new Thread(runnable) to execute it off the EDT or SwingUtilities.invokeLater(runnable) to make it execute on the EDT. All interaction with Swing components need to take place on the EDT, since Swing objects are not thread safe. All sleeping, waiting, file access, network access, database access, or anything else that may block for indeterminate periods of time must take place off the EDT.
There are many questions on Stack Overflow having to do with the Event Dispatch Thread. I recommend you review these to find more information and code samples to do what you are looking to do in different ways.
Instead of making the thread sleep use a timer and kick off the next image as an event.
It sounds like you want to images appear one by one as they gets loaded (ie as in a web page), is that correct? If so, you wouldn't get that effect with your current code because your UI won't be updated until all images are loaded, this is because you're loading images on EDT (Event Dispatch Thread) which is the same thread that'll do the painting. Painting occurs "when there's time", which in this case means after all images are loaded.
To solve your problem, I'll suggest you create a sub class of SwingWorker like:
public class MyImageLoader extends SwingWorker<Void, String>
{
// Is executed in background thread, EDT can meanwhile load and paint images
#Override
protected Void doInBackground() throws Exception
{
for(File file : files)
{
if(file.isFile()) // Maybe some more check to see if it's an image
{
publish(file.getAbsolutePath());
Thread.sleep(500);
}
}
}
// Executed on EDT
#Override
protected void process(List<String> filePaths)
{
for(String path : filePaths)
{
// Load ImageIcon to UI
}
}
}