2012-08-07 13 views
6

Al probar algún código de simulación en tiempo real que utiliza un Swingworker noté que mi GUI siempre parece funcionar a 30 fps, ni más ni menos. Actualizo la GUI cada vez que el usuario interactúa con la aplicación (como un movimiento del mouse) o cuando se llama al método process() del Swingworker. El Swingworker no hace nada ahora, simplemente toma la ubicación del mouse desde la GUI y la envía de vuelta como un clon a través de los métodos publish() y process() (solo hago esto para ver lo que puedo y puedo No se puede hacer cuando se comunica entre subprocesos porque la multithreading todavía es bastante nueva para mí). No tengo temporizadores en ninguna parte, el método process() del Swingworker llama a repintado() en la GUI, así que me preguntaba ¿por qué la GUI se actualiza a 30 fps? ¿Hay quizás como un vsync activo en la GUI por defecto o es algún comportamiento del método process() en Swingworker? Y finalmente: ¿hay alguna manera de obtener mayores tasas de fotogramas?GUI funcionando a 30 fps?

He aquí un sscce que muestra este comportamiento:

public class SimGameTest implements Runnable { 
    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new SimGameTest()); 
    } 

    @Override 
    public void run() { 
     MainWindow mainWindow = new MainWindow(new Game()); 

     mainWindow.setLocationRelativeTo(null); 
     mainWindow.setVisible(true); 
    } 
} 

public class MainWindow extends JFrame { 
    private Game  game; 
    private GamePanel gamePanel; 

    public MainWindow(Game game) { 
     this.game = game; 

     createAndShowGUI(); 

     startGame(); 
    } 

    private void startGame() { 
     GameSim gameSim = new GameSim(game, gamePanel); 
     gameSim.execute(); 
    } 

    private void createAndShowGUI() { 
     setTitle("Sim game test"); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     setResizable(false); 

     JPanel contentPane = new JPanel(new GridBagLayout()); 

     gamePanel = new GamePanel(game); 
     contentPane.add(gamePanel); 

     add(contentPane); 
     pack(); 
    } 
} 

public class Game { 
    public Point   mouseLocation  = new Point(); 
    public Point   clonedMouseLocation = new Point(); 
} 

public class GameSim extends SwingWorker<Point, Point> { 
    private Game    game; 
    private GamePanel   gamePanel; 

    public GameSim(Game game, GamePanel gamePanel) { 
     this.game = game; 
     this.gamePanel = gamePanel; 
    } 

    @Override 
    protected Point doInBackground() throws Exception { 
     while (true) { 
      publish((Point) game.mouseLocation.clone()); 
     } 
    } 

    @Override 
    protected void process(List<Point> pointList) { 
     game.clonedMouseLocation = pointList.get(pointList.size() - 1); 
     gamePanel.repaint(); 
    } 
} 

public class GamePanel extends JPanel { 
    private Game    game; 
    private long    lastTime; 

    public GamePanel(Game game) { 
     this.game = game; 
     setPreferredSize(new Dimension(512, 512)); 
    addMouseMotionListener(new GamePanelListener();); 
    } 

    @Override 
    public void paintComponent(Graphics g) { 
     // draw background 
     g.setColor(new Color(0, 0, 32)); 
     g.fillRect(0, 0, getWidth(), getHeight()); 

     g.setColor(new Color(192, 192, 255)); 
     g.drawString(game.clonedMouseLocation.x + ", " + game.clonedMouseLocation.y, 10, 502); 

     long now = System.nanoTime(); 
     long timePassed = now - lastTime; 
     lastTime = now; 
     double fps = ((double) 1000000000/timePassed); 
     g.drawString("fps: " + fps, 10, 482); 
    } 

    private class GamePanelListener extends MouseInputAdapter { 
     @Override 
     public void mouseMoved(MouseEvent e) { 
      if (contains(e.getPoint())) { 
       game.mouseLocation = e.getPoint(); 
      } 
     } 
    } 
} 

creé otra versión donde acabo de contar el número de veces que se repintó la interfaz gráfica de usuario y mostrar el recuento en la pantalla, y que parece ir en aumento a una tasa de 30 por segundo, así que supongo que el cálculo de fps no es el problema.

+2

Por favor, edite su pregunta para incluir una [sscce] (http://sscce.org/) que muestre la tasa que describe. – trashgod

+2

¿Cómo está obteniendo la velocidad de cuadros? –

+0

lo siento, agregué un sscce – FinalArt2005

Respuesta

10

Resulta SwingWorker mensajes casos de Runnable a la EventQueue utilizando un javax.swing.Timer con exactamente que DELAY.

private static class DoSubmitAccumulativeRunnable 
     extends AccumulativeRunnable<Runnable> implements ActionListener { 
    private final static int DELAY = (int) (1000/30); 
    ... 
} 

se puede obtener una mayor velocidad de cuadro, pero no con javax.swing.SwingWorker. Puede compilar SwingWorker desde source, pero eventualmente saturará el hilo con instancias de Runnable. Una alternativa es ejecutar el modelo a máquina y muestrearlo periódicamente, como se muestra en here.

+0

No sabía de 'javax.swing.SwingWorker' usando' javax.swing.Timer'. – trashgod

+1

Ahora todos lo hacemos, ¡gracias! –

+0

Descubrí que ayer al responder [esta pregunta] (http://stackoverflow.com/questions/11825281/java-swing-running-on-edt) – Robin