2

I am working on a relatively complicated java project that involves a GUI as well as communicating with two different arduinos across a network.

I have a rotary encoder on one of the arduinos that will tell the java program how far a wheel has turned. I have a thread that deals with retrieving the data from the arduino and rotating the image.

The issue arises when I try to rotate the rotary encoder quickly. The image will flash white as seen here.

I use the following code to rotate the image:

public static void setimageto(int degree){

     stageRotation.stageangel = degree;
    try {
        BufferedImage localBufferedImage = ImageIO.read(new File("CircleStagePNG.png"));

        JPanel rotatepanel = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(localBufferedImage.getWidth(), localBufferedImage.getHeight());
            }

            @Override
            protected void paintComponent(Graphics g) {
                if (g != null) {
                    super.paintComponent(g);
                    Graphics2D g2 = (Graphics2D) g;
                    g2.rotate(Math.toRadians(degree), localBufferedImage.getWidth() / 2, localBufferedImage.getHeight() / 2);
                    g2.drawImage(localBufferedImage, 0, 0, null);
                }
            }
        };

        stageRotation.pnPanel3.removeAll();
        rotatepanel.setBackground(stageRotation.color);
        stageRotation.pnPanel3.add(rotatepanel);
        stageRotation.pnPanel0.setBackground(stageRotation.color);
        stageRotation.pnPanel0.updateUI();
    } catch (IOException localIOException) {

    }
}

I have also uploaded the full code to this link

the main java file is called stageRotation.java while javatest.java should be an example of what it should look like.


Any explanation or solution would be helpful as would any suggestions to make this question more user friendly.

[EDIT] This is my main class and constructor with a lot of code removed. I attempted to shorten it down the best I could but may have left some unnecessary items and this is also why it may not run correctly. Thanks again for any assistance

import javax.swing.UIManager.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.net.*;
import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.net.ServerSocket;
import javax.swing.filechooser.*;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.TimeUnit;



public class stageRotation extends JPanel {
    static stageRotation thestageRotation;
    public static JFrame mainFrame;
    public static JPanel pnPanel0;
    public static JPanel pnPanel1;
    public static JPanel pnPanel3;
    public static Object rotationobject;
    public static Object[] queadd;
    public static Thread motorcontrollerthread;
    public static Thread sensorcontrollerthread;
    public static Thread rotationthread =  new Thread();
    public static Thread inputthread =  new Thread();
    public static Boolean endinsight = false;
    public static Boolean rotaterflag = true;
    public static Boolean imagechange = false;
    public static volatile Boolean sensorinput = true;
    public static volatile Boolean motorinput = true;
    public static Color color = Color.WHITE;
    public static PrintWriter motorsocketout;
    public static PrintWriter sensorsocketout;
    public static Socket echoSocket;
    public static BufferedImage localBufferedImage;




    public static void main(String[] args) {
        //Makes the whole program look a lot better but there are some inexcusable errors that need to be worked out

        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception e) {
            // If Nimbus is not available, you can set the GUI to another look and feel.
        }
        thestageRotation = new stageRotation();
    }



  public stageRotation()    {
        /*while(motorconnected == 0 || sensorconnected == 0) {
            if (motorconnected == -1 || sensorconnected == -1) {
                System.exit(0);
            }
        }*/

    //frame definition and layoutmanager definition
    JFrame mainFrame = new JFrame("Que thingy");
    pnPanel0 = new JPanel();
    GridBagLayout localGridBagLayout1 = new GridBagLayout();
    GridBagConstraints localGridBagConstraints1 = new GridBagConstraints();
    pnPanel0.setLayout(localGridBagLayout1);
    pnPanel3 = new JPanel();
    pnPanel3.setLayout(localGridBagLayout1);





     // create image for display
      try {
        final BufferedImage localBufferedImage = ImageIO.read(new File("CircleStagePNG.png"));

        pnPanel1 = new JPanel()   {
            public Dimension getPreferredSize() {
                return new Dimension(localBufferedImage.getWidth(), localBufferedImage.getHeight());
            }

            protected void paintComponent(Graphics paramAnonymousGraphics) {
                super.paintComponent(paramAnonymousGraphics);
                Graphics2D localGraphics2D = (Graphics2D)paramAnonymousGraphics;
                localGraphics2D.rotate(0.0D, localBufferedImage.getWidth() / 2, localBufferedImage.getHeight() / 2);
                localGraphics2D.drawImage(localBufferedImage, 0, 0, null);
            }

        };

        //set up graphical properties for image
        GridBagLayout localGridBagLayout2 = new GridBagLayout();
        pnPanel1.setBackground(color);
        pnPanel3.add(pnPanel1);
        pnPanel3.setLayout(localGridBagLayout2);
        localGridBagConstraints1.gridx = 0;
        localGridBagConstraints1.gridy = 0;
        localGridBagConstraints1.gridwidth = 1;
        localGridBagConstraints1.gridheight = 2;
        localGridBagConstraints1.fill = GridBagConstraints.BOTH;
        localGridBagConstraints1.weightx = 1;
        localGridBagConstraints1.weighty = 1;
        localGridBagConstraints1.anchor = GridBagConstraints.NORTH;
        localGridBagLayout1.setConstraints(pnPanel3, localGridBagConstraints1);
        pnPanel3.setBackground(Color.WHITE);
        pnPanel0.add(pnPanel3);




        mainFrame.setContentPane(pnPanel0);
        mainFrame.pack();
        mainFrame.setBackground(Color.WHITE);
        mainFrame.setVisible(true);
        tbJquetable.requestFocus();
      } catch (IOException localIOException) {}
   }








    //changes background will sometimes report nullpointerexception error however has no effect on code

   public static void setimageto(int degree){

         stageRotation.stageangel = degree;
        try {
            localBufferedImage = ImageIO.read(new File("CircleStagePNG.png"));

            pnPanel1 = new JPanel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(localBufferedImage.getWidth(), localBufferedImage.getHeight());
                }

                @Override
                protected void paintComponent(Graphics g) {
                        super.paintComponent(g);
                        Graphics2D g2 = (Graphics2D) g;
                        g2.rotate(Math.toRadians(degree), localBufferedImage.getWidth() / 2, localBufferedImage.getHeight() / 2);
                        g2.drawImage(localBufferedImage, 0, 0, null);

                }
            };

            stageRotation.pnPanel3.removeAll();
            pnPanel1.setBackground(stageRotation.color);
            stageRotation.pnPanel3.add(pnPanel1);

            stageRotation.pnPanel0.setBackground(stageRotation.color);
            stageRotation.pnPanel0.updateUI();
        } catch (IOException localIOException) {

        }
  }


}

[EDIT2]

While the following code does not flicker, the image will hang at points. I was unable to reproduce the flickering with url imagaes and only with local files; however, I believe the image hanging and flickering are the same problem.

    import javax.swing.UIManager.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.net.*;
import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.net.ServerSocket;
import javax.swing.filechooser.*;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.TimeUnit;



public class stageRotation extends JPanel {
    static stageRotation thestageRotation;
    public static JFrame mainFrame;
    public static JPanel pnPanel0;
    public static JPanel pnPanel1;
    public static JPanel pnPanel3;
    public static Object rotationobject;
    public static Object[] queadd;
    public static Thread motorcontrollerthread;
    public static Thread sensorcontrollerthread;
    public static Thread rotationthread =  new Thread();
    public static Thread inputthread =  new Thread();
    public static Boolean endinsight = false;
    public static Boolean rotaterflag = true;
    public static Boolean imagechange = false;
    public static volatile Boolean sensorinput = true;
    public static volatile Boolean motorinput = true;
    public static Color color = Color.WHITE;
    public static PrintWriter motorsocketout;
    public static PrintWriter sensorsocketout;
    public static Socket echoSocket;
    public static BufferedImage localBufferedImage;

    public static URL url;





    public static void main(String[] args) {
        //Makes the whole program look a lot better but there are some inexcusable errors that need to be worked out

        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception e) {
            // If Nimbus is not available, you can set the GUI to another look and feel.
        }
        thestageRotation = new stageRotation();
        for (int i = 0; i < 360; i++){
                setimageto(i);
        }
    }



  public stageRotation()    {

    try{
        url = new URL("https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/CIRCLE_LINES.svg/220px-CIRCLE_LINES.svg.png");
    } catch(MalformedURLException a){}
    JFrame mainFrame = new JFrame("Que thingy");
    pnPanel0 = new JPanel();
    GridBagLayout localGridBagLayout1 = new GridBagLayout();
    GridBagConstraints localGridBagConstraints1 = new GridBagConstraints();
    pnPanel0.setLayout(localGridBagLayout1);
    pnPanel3 = new JPanel();
    pnPanel3.setLayout(localGridBagLayout1);





     // create image for display
      try {
        final BufferedImage localBufferedImage = ImageIO.read(url);

        pnPanel1 = new JPanel()   {
            public Dimension getPreferredSize() {
                return new Dimension(localBufferedImage.getWidth(), localBufferedImage.getHeight());
            }

            protected void paintComponent(Graphics paramAnonymousGraphics) {
                super.paintComponent(paramAnonymousGraphics);
                Graphics2D localGraphics2D = (Graphics2D)paramAnonymousGraphics;
                localGraphics2D.rotate(0.0D, localBufferedImage.getWidth() / 2, localBufferedImage.getHeight() / 2);
                localGraphics2D.drawImage(localBufferedImage, 0, 0, null);
            }

        };

        //set up graphical properties for image
        GridBagLayout localGridBagLayout2 = new GridBagLayout();
        pnPanel1.setBackground(color);
        pnPanel3.add(pnPanel1);
        pnPanel3.setLayout(localGridBagLayout2);
        localGridBagConstraints1.gridx = 0;
        localGridBagConstraints1.gridy = 0;
        localGridBagConstraints1.gridwidth = 1;
        localGridBagConstraints1.gridheight = 2;
        localGridBagConstraints1.fill = GridBagConstraints.BOTH;
        localGridBagConstraints1.weightx = 1;
        localGridBagConstraints1.weighty = 1;
        localGridBagConstraints1.anchor = GridBagConstraints.NORTH;
        localGridBagLayout1.setConstraints(pnPanel3, localGridBagConstraints1);
        pnPanel3.setBackground(Color.WHITE);
        pnPanel0.add(pnPanel3);


        mainFrame.setExtendedState(6);

        WindowListener exitListener = new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                try {
                    System.out.println("EXIT");
                    stageRotation.motorsocketout.println("EXIT");
                    sensorsocketout.println("EXIT");
                    sensorinput = false;
                    motorinput = false;


                    mainFrame.setVisible(false);
                    mainFrame.dispose();
                    System.exit(0);
                } catch (NullPointerException p){
                    System.exit(0);
                }
            }
        };
        mainFrame.addWindowListener(exitListener);

        mainFrame.setContentPane(pnPanel0);
        mainFrame.pack();
        mainFrame.setBackground(Color.WHITE);
        mainFrame.setVisible(true);
      } catch (IOException localIOException) {}
   }








    //changes background will sometimes report nullpointerexception error however has no effect on code

   public static void setimageto(int degree){

        try {
            localBufferedImage = ImageIO.read(url);

            pnPanel1 = new JPanel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(localBufferedImage.getWidth(), localBufferedImage.getHeight());
                }

                @Override
                protected void paintComponent(Graphics g) {
                        super.paintComponent(g);
                        Graphics2D g2 = (Graphics2D) g;
                        g2.rotate(Math.toRadians(degree), localBufferedImage.getWidth() / 2, localBufferedImage.getHeight() / 2);
                        g2.drawImage(localBufferedImage, 0, 0, null);

                }
            };

            stageRotation.pnPanel3.removeAll();
            pnPanel1.setBackground(stageRotation.color);
            stageRotation.pnPanel3.add(pnPanel1);

            stageRotation.pnPanel0.setBackground(stageRotation.color);
            stageRotation.pnPanel0.updateUI();
        } catch (IOException localIOException) {

        }
  }


}
MathMXC
  • 319
  • 1
  • 3
  • 14

2 Answers2

2

OK.. the MCVE above demonstrates a variety of misunderstandings.

  1. The way to do animation is to include an 'animation loop' in the code. It is better, for many reasons, to use a Swing based Timer. The ActionListener (used in the constructor of the timer) should then perform a single frame of the animation. Usually using a loop to animate will block the Event Dispatch Thread, with the end result that the 'animation' will only appear as the first frame - direct to switching to the last frame.
  2. That the image should be loaded for each frame. (It was ironic that doing so hid the 'block the EDT' mentioned in the previous point.) Instead the image should be loaded at start-up, and stored as an attribute of the class.
  3. That the drawing surface should be created fresh for each new frame. It is more optimal to create it once but provide a method to change the state, then repaint the (original, single) instance of it.

There are a number of other improvements that might be made to the code, but I stopped there since it seems to address the main problem.

Here is code that implements the above advice:

import javax.swing.UIManager.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

public class stageRotation extends JPanel {

    private static stageRotation thestageRotation;
    public static JPanel pnPanel0;
    public static PnPanel1 pnPanel1;
    public static JPanel pnPanel3;
    public static volatile Boolean sensorinput = true;
    public static volatile Boolean motorinput = true;
    public static Color color = Color.WHITE;
    public static PrintWriter motorsocketout;
    public static PrintWriter sensorsocketout;
    public static BufferedImage localBufferedImage;

    public static URL url;

    public static void main(String[] args) {
        //Makes the whole program look a lot better but there are some inexcusable errors that need to be worked out
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception e) {
            // If Nimbus is not available, you can set the GUI to another look and feel.
        }
        setThestageRotation(new stageRotation());
        ActionListener actionListener = new ActionListener() {

            int degree = 0;

            @Override
            public void actionPerformed(ActionEvent e) {
                if (degree<360) {
                    degree++;
                    setimageto(degree);
                }
            }
        };
        Timer timer = new Timer(10,actionListener);
        timer.start();
    }

    /**
     * @param aThestageRotation the thestageRotation to set
     */
    public static void setThestageRotation(stageRotation aThestageRotation) {
        thestageRotation = aThestageRotation;
    }

    public stageRotation() {

        try {
            url = new URL("https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/CIRCLE_LINES.svg/220px-CIRCLE_LINES.svg.png");
            localBufferedImage = ImageIO.read(url);
        } catch (Exception ex) {
            Logger.getLogger(stageRotation.class.getName()).log(Level.SEVERE, null, ex);
        }
        JFrame mainFrame = new JFrame("Que thingy");
        pnPanel0 = new JPanel();
        GridBagLayout localGridBagLayout1 = new GridBagLayout();
        GridBagConstraints localGridBagConstraints1 = new GridBagConstraints();
        pnPanel0.setLayout(localGridBagLayout1);
        pnPanel3 = new JPanel();
        pnPanel3.setLayout(localGridBagLayout1);
        pnPanel1 = new PnPanel1();
        pnPanel3.add(pnPanel1);

        // create image for display
        try {
            final BufferedImage localBufferedImage = ImageIO.read(url);

            //set up graphical properties for image
            GridBagLayout localGridBagLayout2 = new GridBagLayout();
            pnPanel1.setBackground(color);
            pnPanel3.setLayout(localGridBagLayout2);
            localGridBagConstraints1.gridx = 0;
            localGridBagConstraints1.gridy = 0;
            localGridBagConstraints1.gridwidth = 1;
            localGridBagConstraints1.gridheight = 2;
            localGridBagConstraints1.fill = GridBagConstraints.BOTH;
            localGridBagConstraints1.weightx = 1;
            localGridBagConstraints1.weighty = 1;
            localGridBagConstraints1.anchor = GridBagConstraints.NORTH;
            localGridBagLayout1.setConstraints(pnPanel3, localGridBagConstraints1);
            pnPanel3.setBackground(Color.WHITE);
            pnPanel0.add(pnPanel3);

            WindowListener exitListener = new WindowAdapter() {

                @Override
                public void windowClosing(WindowEvent e) {
                    try {
                        System.out.println("EXIT");
                        stageRotation.motorsocketout.println("EXIT");
                        sensorsocketout.println("EXIT");
                        sensorinput = false;
                        motorinput = false;

                        mainFrame.setVisible(false);
                        mainFrame.dispose();
                        System.exit(0);
                    } catch (NullPointerException p) {
                        System.exit(0);
                    }
                }
            };
            mainFrame.addWindowListener(exitListener);

            mainFrame.setContentPane(pnPanel0);
            mainFrame.pack();
            mainFrame.setBackground(Color.WHITE);
            mainFrame.setVisible(true);
        } catch (IOException localIOException) {
        }
    }

    //changes background will sometimes report nullpointerexception error however has no effect on code
    public static void setimageto(int degree) {
        pnPanel1.setDegree(degree);
        pnPanel1.repaint();
    }

    class PnPanel1 extends JPanel {

        int degree;

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(localBufferedImage.getWidth(), localBufferedImage.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.rotate(Math.toRadians(degree), localBufferedImage.getWidth() / 2, localBufferedImage.getHeight() / 2);
            g2.drawImage(localBufferedImage, 0, 0, this);
        }

        public void setDegree(int degree) {
            this.degree = degree;
        }
    };
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • After testing earlier today I figured out that the problem was centered around this line `mainFrame.setExtendedState(6);` Without it the image worked perfectly but as I enlarged the frame it got worse as the frame got bigger. Any explanation would be helpful – MathMXC Aug 25 '17 at 18:13
  • Does the example I posted show the slow-down on maximise? (It didn't on my machine.) – Andrew Thompson Aug 26 '17 at 06:27
1

I think issue is for every degree change, you are clearing stageRotation and re-setting background.

You should call all init code only once and just redraw image only when a change in degree is required.

  • Sorry but I am still new at java. Could you post an example of what you mean? – MathMXC Aug 24 '17 at 04:57
  • Please post complete class code, I will update that. – Zaki Anwar Hamdani Aug 24 '17 at 04:58
  • The complete class code is found [here](https://drive.google.com/file/d/0B1oajMSxpWB5V055RVZQSEJQNms/view?usp=sharing) I dont want to past it in the question as it is 932 lines long – MathMXC Aug 24 '17 at 05:01
  • I'm guessing @Zaki is right about repeated calls to `setBackground()`, but `updateUI()` is also wrong in this context. This self-contained [example](https://stackoverflow.com/a/3420651/230513) is only 100 lines, and it doesn't flicker even with nine images. Click one to see the effect. What happens when you try it with your image? – trashgod Aug 25 '17 at 01:41
  • *"The complete class code is found here.."* No! Most people won't, some people **can't**, follow external links. If you're not prepared or able to make an MCVE and post it as an edit to the question (so the question is self contained - independent of external links that might go stale), don't expect help from SO. It should not take more than 100 lines of code to express this problem as an MCVE, get onto it. – Andrew Thompson Aug 25 '17 at 02:27
  • @AndrewThompson I am unable to create a simplified example of the problem because I do not have that much knowledge in Java. My code incorporates a bunch of pieces from around the internet. My question is as much about solving the problem as it is finding out what is going on. I would love to try and make a MCVE but when I do create one there is no issue with the code. I have been unable to recreate this problem so I can not _get onto it_ as you put it. Thank you for your formatting help earlier but now I would like some coding help that isn't a vague mention to a different layout manager. – MathMXC Aug 25 '17 at 03:34
  • Sorry, I have restricted network to access google drive. – Zaki Anwar Hamdani Aug 25 '17 at 04:00
  • @ZakiAnwarHamdani No worries what is the easiest way to get you the code? As it is to many characters for the question – MathMXC Aug 25 '17 at 04:09
  • Please post all your classes. Remove any irrelevant code if its too big. – Zaki Anwar Hamdani Aug 25 '17 at 04:11
  • @ZakiAnwarHamdani *"Remove any irrelevant code.."* What a pity nobody had advised the OP to do that 23 hours ago. OP: *"I would love to try and make a MCVE but when I do create one there is no issue with the code."* You have 2 options for recreating the problem - in order to discover what the problem is. 1) Keep adding code to the working version, bit by bit, until the problem reappears. 2) Start stripping parts from the original code until it disappears. - By identifying the last small part that causes / alleviates the problem, you'll have a better chance of solving it, and a .. – Andrew Thompson Aug 25 '17 at 04:18
  • .. ***much*** better chance than we have. *"now I would like some coding help that isn't a vague mention to a different layout manager."* I don't intend to write a novel in every comment. If you do not understand something in a comment from me, the onus is on you to ask (& make it a specific question(s)). After all, our interest in this is purely academic. You are the only one with a vested interest in solving the problem. If you're not capable of making the effort, why should we bother? – Andrew Thompson Aug 25 '17 at 04:20
  • @AndrewThompson I am sorry for getting mad earlier I have been working on this problem for months at this point. I was able to find the problem thanks to your help and have updated my code with the bit that is causing trouble. It is from a completely separate part of code and do not know why it is causing problems. thanks again for your help and sorry for getting mad – MathMXC Aug 25 '17 at 04:27
  • That's OK, but can you tie those parts into a single code that compiles cleanly when copied to a new project, and either hot links (loads by URL) an image off the internet or generates an image in the code? – Andrew Thompson Aug 25 '17 at 04:31