Thanks everyone for their help with this.

I finally figured out what was going on.

I wrote my example to call invalidatePaint from the correct thread
(the EDT) for one node and the incorrect thread for the other node.  I
expected one node to update and the other not to update, however
neither node updated.


When calling invalidatePaint() on a node the call propogates up all
the parents towards PRoot, setting the childPaintInvalid on every
parent on the way.  The call stops if a parents childPaint is already
flagged as invalid - as it knows from this that a previous call has
already scheduled a processInputs for execution on the EDT.

If you call invalidatePaint from the wrong thread it is ignored when
it finally reaches PRoot, however in getting to PRoot
childPaintInvalid is set for every parent on the way.  As PRoot
ignores the request a processInputs Runnable is never put in the event
queue and the children remain with invalid paint.

A subsequent call to invalidatePaint, even from the correct thread,
never gets as far as PRoot as it stops (as it should) when it reaches
a parent whose child paint is already invalid, however with no
processInputs scheduled this and all future calls to invalidatePaint
get ignored.

This is exactly what was happening in my example.  I included updates
from the wrong thread so I could see the difference between the wrong
thread and the correct thread, not realising that these very updates
from the wrong thread were breaking updates from the correct thread.


The situation only arises after calling invalidatePaint from the wrong
thread.

Furthermore if you set

debugThread = true

in PDebug

the "scene graph manipulated on wrong thread" warning is never
displayed as PRoot.scheduleProcessInputsIfNeeded() returns if being
called from the wrong thread before getting as far as calling
PDebug.scheduleProcessInputs();

Nigel

On Nov 4, 12:24 am, nls...@googlemail.com wrote:
> The problem seems to be caused by updating the scene graph by a thread
> other than the Swing event dispatch thread. This is not allowed, a
> solution is explained in the Piccolo2D Patterns.
>
> A consequence of updates by wrong threads is that PRoot discards the
> update and repaint process (it checks if the right thread invoked the
> update). Subsequent invalidatePaints possibly dont reach the root.
>
> Am Dienstag 03 November 2009 02:36:21 schrieb Nigel:
>
> > The comment in PNode.java reads
>
> >     // When you do create you own nodes the only method that you
> > will // normally need to call isinvalidatePaint. This method marks
> > the // nodes as having invalid paint, the root node's UI cycle will
> > then
> >     // later discover this damage and report it to the Java repaint
> > manager.
>
> > However, I find that after callinginvalidatepaint() the repaint
> > manager does not discover the damage unless other events are
> > occurring (such as moving the mouse around).
>
> > I was expecting that callinginvalidatePaintwould result in the
> > node being repainted some time later - am I doing something wrong
> > or is Piccolo?
>
> > Here is a contrived example (ClockNode.java) - It displays 4 custom
> > nodes, each showing a clock's second hand.  The top 2 clocks are
> > updated from within the event dispatch thread, the lower 2 clocks
> > are updated from another thread.  The 2 clocks on the left are
> > redrawn via a call to repaint and the 2 on the right via a call to
> >invalidatePaint.
>
> > if you run it you'll see the clock on the right (calling
> >invalidatePaint) are only redrawn if you are moving your mouse over
> > the window.
>
> > ClockNode.java
>
> > import java.awt.*;
> > import java.awt.geom.*;
> > import edu.umd.cs.piccolo.*;
> > import edu.umd.cs.piccolo.util.*;
>
> > import edu.umd.cs.piccolox.PFrame;
>
> > public class ClockNode extends PNode {
>
> >    private GeneralPath secondHand;
> >    private int tseconds;   // 10ths of seconds
>
> >    public void tick() {
> >            tseconds++;
> >            if ( tseconds >= 600 ) { tseconds = 0; }
> >            // inform Piccolo that the node needs to be redrawn
> >            if ( useRepaint ) {
> >                    repaint();
> >            } else {
> >                    invalidatePaint();
> >            }
> >    }
>
> >    private boolean useRepaint;
> >    public ClockNode(boolean useRepaint) {
> >            this.useRepaint = useRepaint;
> >            // create the needle shape
> >            secondHand = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
> >            secondHand.moveTo(-0.1,0);
> >            secondHand.lineTo(0,1);
> >            secondHand.lineTo(0.1,0);
> >            secondHand.closePath();
> >    }
>
> >    public void paint(PPaintContext aPaintContext) {
> >            Graphics2D g2 = (Graphics2D)aPaintContext.getGraphics().create();
> > // create - as we mess with the transform
> >            g2.setPaint(Color.BLACK);
> >            //draw the face
> >            g2.draw( new Ellipse2D.Double( getX(), getY(), getWidth(),
> > getHeight () ));
> >            //draw the second hand
> >            g2.translate(getX()+getWidth()/2,getY()+getHeight()/2); //
> > translate hand so 0,0 is centre of bounds
> >            g2.scale(getWidth()/2,getHeight()/2); // scale the needle
> >            g2.rotate ( Math.toRadians((tseconds*6)/10f+180) );
> >            g2.fill(secondHand);
> >    }
>
> >    public static void main(String args[]) {
> >            // top left clock - uses repaint
> >            final ClockNode clockNode1 = new ClockNode(true);
> >            clockNode1.setBounds(0,0,200,200);
> >            // top right clock - usesinvalidatePaint
> >            final ClockNode clockNode2 = new ClockNode(false);
> >            clockNode2.setBounds(210,0,200,200);
> >            // lower left clock - uses repaint
> >            final ClockNode clockNode3 = new ClockNode(true);
> >            clockNode3.setBounds(0,210,200,200);
> >            // lower right clock - usesinvalidatePaint
> >            final ClockNode clockNode4 = new ClockNode(false);
> >            clockNode4.setBounds(210,210,200,200);
>
> >            PFrame pFrame = new PFrame() {
> >                                    public void initialize() {
> >                                            
> > getCanvas().getLayer().addChild(clockNode1);
> >                                            
> > getCanvas().getLayer().addChild(clockNode2);
> >                                            
> > getCanvas().getLayer().addChild(clockNode3);
> >                                            
> > getCanvas().getLayer().addChild(clockNode4);
> >                                            setSize(500,500);
> >                                    }
> >                            };
>
> >            // tick clocks 1 and 2 in the event dispatch thread
> >            new javax.swing.Timer(100, new java.awt.event.ActionListener() {
> >                                          public void actionPerformed
> > (java.awt.event.ActionEvent evt) {
> >                                                  clockNode1.tick();
> >                                                  clockNode2.tick();
> >                                          }
> >                                  }).start();
>
> >            // tick clocks 3 and 4 in the timer thread
> >            java.util.Timer timer = new java.util.Timer(true);
> >            timer.schedule( new java.util.TimerTask() {
> >                                    public void run() {
> >                                            clockNode3.tick();
> >                                            clockNode4.tick();
> >                                    }
> >                            },0,100);
> >    }
>
> > }
--~--~---------~--~----~------------~-------~--~----~
Piccolo2D Developers Group: http://groups.google.com/group/piccolo2d-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to