Drag and Drop changes in canvas only visible after minimization - java

public void dropAccept(final DropTargetEvent event)
{
if (TextTransfer.getInstance().isSupportedType(event.currentDataType))
{
final String d=(String)TextTransfer.getInstance().nativeToJava(event.CurrentDataType);
GC gc = new(text);
//text is the name assigned to the Canvas
text.addPaintListener(new PaintListener()
{
public void paintControl(PaintEvent e)
{
int x= event.x- shell.getBounds().x - text.getBounds().x;
int y=event.y - shell.getBounds().y - text.getBounds().y;
e.gc.drawString(d, x, y);
}
}); } }
This code snippet is part of a larger class that implements drag drop of text onto a canvas. The problem is that, the actual dropping of text is not seen on the canvas after I drop it but only after I minimize the shell and then maximize it again. Can anyone please tell me how I can make drop actions immediately visible by modifying this code?

You have not done anything to cause the control to be redrawn. Call
text.redraw();
to request that the control is redrawn (by calling the paint listener).
Note: If you add paint listeners on every drop you are going to end up with lots of listeners registered.

Related

Java SWT update canvas from dialog

I am working on a GUI application with Java and SWT.I have createad a main window with a canvas in which I load an image (well it's a PDF page converted to image).I have attached a PaintListener to the canvas and when i drag the mouse onte the canvas I am able to draw a rectangle.
When i release the left button on mouse, I want a dialog window to came out to "fine setting" the rectangle area, so I made a dialog with 4 spinner (x, y, width and height).
Now I want to click on the spinners and the canvas redraw the changed rectangle area.I tried to pass the canvas object to the dialog window and attach a second paint listener to it (So I have one paintlistener from MainWindow.java and another from Dialog.java), but it's no working.
The problem is that I have multiple rectangle drawn on the canvas, and if I call canvas.redraw() from dialog window, the rectangles already drawn on canvas "disappeared".What is the best practise in such situation?
I thinks to put the dialog window "in the toolbar", that is put the 4 spinners in the toolbar (or another area in the main window) so I have only one paint listener and get rid of the dialog, but I prefer the dialog 'cause it is impratical to drag a rectangle onto canvas and then move the mouse to click on toolbar.
Thanks in advance, Mauro
Your main window should implement custom listener for example:
public interface PreferencesListener {
public void updatePreference(PreferencesEvent e)
}
Then create a manager which control updates:
public class PreferencesController {
private static PreferencesController instance = new PreferencesController();
private List<PreferencesListener> preferencesListeners;
private PreferencesController() {
preferencesListeners = new ArrayList<PreferencesListener>();
}
public static PreferencesController getInstance() {
return instance;
}
public void addPreferenceListener(PreferencesListener listener) {
preferencesListeners.add(listener);
}
public void notify(int x, int y, int w, int h) {
PreferencesEvent e = new PreferencesEvent(x, y, w, h);
for(final PreferencesListener listener: preferencesListeners) {
listener.updatePreference(e);
}
}
}
At the moment of creation main window register it in controller:
PreferencesController.getInstance().addPreferenceListener(this);
When you change value in dialog trigger notify:
PreferencesController.getInstance().notify(x,y,w,h);
And then finally implement updatePreference(PreferencesEvent e) from PreferencesListener. From this moment whenever values x y w h are changed your window will be notified. From my point of view it's a good solution because your window and dialog don't even know each other exists, and you could easily add another component that react to preference change.

Understanding the EDT in a real model with frequent updates

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);
}
}
}

Re-painting a component in java swing

I would like to re-paint a square after a mouse click but the re-paint method will be invoiked 10 times.
For example in the square is in x,y it will be repainted after a mouse click in:
x+1,y+1
x+2,y+2
x+3,y+3
...
...
x+10,y+10
I tried to loop the repaint method 10 times but the result was the final paint instead of the whole process.
public MyPanel()
{
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
for(int i=0;i<10;i++)
moveSquare(redSquare.getX(),redSquare.getY());
}
});
}
private void moveSquare(int x, int y)
{
final int CURR_X = redSquare .getX();
final int CURR_Y = redSquare.getY();
final int CURR_W = redSquare.getWidth();
final int CURR_H = redSquare.getHeight();
final int OFFSET = 1;
// The square is moving, repaint background
// over the old square location.
repaint(CURR_X,CURR_Y,CURR_W+OFFSET,CURR_H+OFFSET);
// Update coordinates.
redSquare.setX(x+1);
redSquare.setY(y+1);
// Repaint the square at the new location.
repaint(redSquare.getX(), redSquare.getY(),
redSquare.getWidth()+OFFSET,
redSquare.getHeight()+OFFSET);
}
If I understand correctly, you want to click somewhere, and have the square move there, but have the square have some type of moving animation towards that new location.
You're moving your square and repainting it too fast that it will seem as if the square has only moved from it's initial position to it's new final position. You can if you want, set x and y pixel velocities, and update the square's position in a loop that moves the square towards it's final spot you want it to based on how much time has elapsed between the last loop iteration times those x and y velocities.
Use a Swing Timer to schedule animation. Read the section from the Swing tutorial on How to Use Timers for more information.
The call to repaint will not immediately cause the component to be repainted. It only tells the rendering system: "Repaint this area as soon as possible". But the rendering system is busy with iterating through your for loop.
The reason is that the mousePressed method is executed by the same thread that is also responsible for repainting - namely by the Swing Event Dispatch Thread (EDT). So this thread is running through your for loop and triggering repaints, but only after it has finished the for loop it is able to actually execute the repaint - and then, only the last state will be visible.
The solution here should be to execute the movement in an own thread. The straightforward solution could look like this
public void mousePressed(MouseEvent e)
{
moveInOwnThread();
}
private void moveInOwnThread()
{
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
move();
}
});
t.setDaemon(true);
t.start();
}
private void move()
{
for(int i=0;i<10;i++)
{
moveSquare(redSquare.getX(),redSquare.getY());
try
{
Thread.sleep(20);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
return;
}
}
}
But you should read something about concurrency in swing: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/

Determining which shape is being hovered over - Java

So I have 3 rectangles drawn on my JPanel that are acting as buttons, and what I would like to do is when the mouse is hovered over one of them, the Jpanel will repaint, and change the color of only that rectangle to red.
The only way I can think to do it is to have 3 separate variables that determines if the mouse is over each component. Then there would be code in the repaint method that, if rect1hover is true, then draw using g.setColor to red.
#Override
public void mouseMoved(MouseEvent e) {
if(rect1.contains(e.getX(), e.getY())){
rect1hover = true;
}
}
But this seems really inefficient. Is there a better way to do this?
Sure. Put your drawn rectangles in a List. Then you can write this code.
#Override
public void mouseMoved(MouseEvent e) {
for (DrawnRectangle r : rectangles) {
if (r.contains(e.getX(), e.getY())){
r.setHoverBackground();
}
}
}
You'll have to create a DrawnRectangle class with a setHoverBackground method.
You can use real components, then just add a MouseListener to each component and you don't need to do any lookup or do custom painting.
See Playing With Shapes for more information.

Java Swt - resize animation. Howto redraw after each layout change?

I have a MyComposite class, where I want to animate the Size change.
For that I am changing the size in a loop.
After each loopteration I call layout().
Unfortunately the Composite doesnÄt repaint after each iteration, but jumps directly to the final size of my Composite.
How can I force the Widget to redraw on every size change?
MyComposite and Animation:
//start
new Animation().start(myComposite);
...
public MyComposite(Composite parent, int style, int bgcolor) {
super(parent, style);
this.setBackground(getDisplay().getSystemColor(bgcolor));
}
#Override
public Point computeSize(int wHint, int hHint, boolean changed) {
return super.computeSize(width, height, changed);
}
class Animation{
public void start(MyComposite composite){
for(int i=0; i<1000; i++){
composite.width++;
composite.getParent().layout(true, true);
}
}
}
MyComposite:
The redrawing works as following:
layout() marks forces the repositioning of all composite-children. This will become visible on next repaint, which will be done somewhere in the future, when composite's screen-area will be redrawn
redraw() marks the widget an invalidated. On next redraw-system-action this area will be repainted.
update() forces all outstanding redraw() requests to be completed NOW.
So the problem was, that I did not trigger the repaint request to be done IMMEDIATELY.
The correct animation function looks like:
//layout of the composite doesn't work
//composite.layout(true, true);
//layout of parent works
composite.getParent().layout(true, true);
//marks the composite's screen are as invalidates, which will force a
composite.redraw(); redraw on next paint request
//tells the application to do all outstanding paint requests immediately
composite.update();
I believe your problem is that everything is being executed on the single display thread. So what happens is your code rapidly calls width++ and .layout, then that call ends and the display thread is finally given a chance to actually perform the .layout.
I would recommend looking at java.util.Timer which runs in its own thread, then using Display.getDefault().asyncExec or .syncExec to queue these events back onto the display thread.

Categories