This week's book giveaway is in the Artificial Intelligence and Machine Learning forum.
We're giving away four copies of TensorFlow 2.0 in Action and have Thushan Ganegedara on-line!
See this thread for details.
Win a copy of TensorFlow 2.0 in Action this week in the Artificial Intelligence and Machine Learning forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Paul Clapham
  • Bear Bibeault
  • Jeanne Boyarsky
Sheriffs:
  • Ron McLeod
  • Tim Cooke
  • Devaka Cooray
Saloon Keepers:
  • Tim Moores
  • Tim Holloway
  • Jj Roberts
  • Stephan van Hulst
  • Carey Brown
Bartenders:
  • salvin francis
  • Scott Selikoff
  • fred rosenberger

How to get an array of filled squares to display correctly on a grid

 
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi all,

I am trying to create a Swing GUI for the Game of Life implementation I wrote.  This is a challenge from https://hyperskill.org, so it comes with some restrictions, such as I have to have a class called "GameOfLife" that extends JFrame.  I hear that's not the best way to do things, but oh well.

So the problem.  In the Game of Life, you have an n x n grid with cells that die or come to life based on some simple rules.  My problem is how to display this correctly. I currently have a GameOfLifeGrid class that extends JPanel that prints lines in a nice grid.  Then I have another class, GameOfLifeCell, that extends JPanel and draws a filled square when the cell is "alive" and clears when it's dead. I have an array of arrays of GameOfLifeCells that correspond to a similar structure of booleans that inform me whether to make the filled square visible or not.

Problem is, it doesn't work.  First, the cells don't line up with the grid.  Second, I can only get one cell to become visible.  It's not always the same cell, though.  I have the whole application at https://github.com/ksnortum/game-of-life, but here are some snippets:

[Edit: the above URL now points to the latest version.  To see the version at the point in time this post was written, see https://github.com/ksnortum/game-of-life/tree/8dd7e1015bc884a332d50c43630566d9747fb4f6 ]




 
Rancher
Posts: 3208
29
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

In the Game of Life, you have an n x n grid


So I would guess you use a JPanel with a GridLayout to display all your cell components.

When you use a GridLayout, then each component is given a location/size so the component can be painted.

So the size should be the same for every cell and the location will be different depending on the row/column position.

Therefore when you do custom painting the custom painting should always be done relative to (0, 0) of the component.

Your painting code is using:


So all the painting is done outside the bounds of the cell except for the first cell on the grid.

The code should be:



Although in reality I would suggest you should not even be using components to do this.

Instead you have your 2D array that contains the state of each cell.

Then you have a GameOfLifePanel that paints the entire game.

So in the paintComponent() method you:

1. paint the lines
2. iterate through the 2D array to paint all the cells that are alive. Now in this case you would change the x/y value dynamically for each cell you iterate through the array.

So basically all the painting logic is in one class.

 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I was thinking of painting everything all over again for each generation, but would this cause a flicker?  I'll give it a try.
 
Marshal
Posts: 70696
288
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Make sure to enable double‑buffering to reduce flickering.
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, that did it!  Thanks!

I still have the problem of the screen updating in a "jerky" manner.  Instead of using gameOfLifePanel.repaint() I used gameOfLifePanel.paintImmediately(0, 0, length, length).  This improved things, but not entirely.  
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell: just saw you post.  My understanding is that double-buffering is enabled by default.  Is that correct?
 
Campbell Ritchie
Marshal
Posts: 70696
288
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I thought it was enabled by default, but I have had flickering problems myself in the past.
 
Marshal
Posts: 25965
70
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator


This is possibly the source of your flickering problem. You shouldn't cause the Swing EDT thread to sleep. To have something update the view every X seconds in Swing, use a javax.swing.Timer object.
 
Bartender
Posts: 4109
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Was my thought as well. But I just had a look at the GitHub code. Where does the code get launched on the EDT? I could not find anyting like : SwingUtilities.invokeLater(..).
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I saw that bit of code on some examples and not on others so I "tried" it and it "didn't work."  I know that's vague but it was many iterations ago in the version of my code.  I'll try launching with the invokeLater()  now in conjunction with  timer code and see how that works.
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Okay, I just tried this:
... and the display was messed up.  The text part (JLabels) didn't display until the last generation was done, and the graphical part was shifted a few pixels ro the right.  I have no idea why this would be so.
 
Piet Souris
Bartender
Posts: 4109
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A slight simplification is to use opaque JLabels instead of JPanels. You can update the backgroud of a JLabel, and it will nicely repaint itself, without any need of a paintComponent in the whole code.
 
Paul Clapham
Marshal
Posts: 25965
70
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Piet Souris wrote:Was my thought as well. But I just had a look at the GitHub code. Where does the code get launched on the EDT? I could not find anyting like : SwingUtilities.invokeLater(..).



It happens automatically when the JFrame's visible() method is called.
 
Piet Souris
Bartender
Posts: 4109
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That doesn't mean that all methods are run from the EDT. If a method does not run from the EDT, then a Thread.sleep does not affect the EDT. You can check it with the method
 
Rob Camick
Rancher
Posts: 3208
29
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

You can update the backgroud of a JLabel,



Not sure how using a JLabel will help.

A JLabel has more complex painting logic than a JPanel. You can also just set the background of a JPanel.

If you want a simple component, then extend JComponent and add the custom painting. This will be the most efficient. However, you would now be responsible for the painting code to paint the background.

The text part (JLabels) didn't display until the last generation was done


This indicates all your code is executing on the Event Dispatch Thread. The GUI can't repaint itself until all the code is finished executing.

You should create your GUI and display the components in its default state.

Then you have a Swing Timer to schedule the stage in the "Game Of Life". Each time the timer fires you update the state of each cell and then repaint the entire panel.

There is no need to change the UIManager property. Not sure what that property does, but it is possible it used a different font for some components when may result in the preferred size of some components changing.

 
Paul Clapham
Marshal
Posts: 25965
70
Eclipse IDE Firefox Browser MySQL Database
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That is true, Piet. And I couldn't tell from the posted code what was being called from the EDT either. However the presence of Thread.sleep() in Swing code is usually a red flag, and replacing it by using javax.swing.Timer should do no harm.
 
Piet Souris
Bartender
Posts: 4109
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Absolutely agree.
 
Piet Souris
Bartender
Posts: 4109
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Rob Camick wrote:

You can update the backgroud of a JLabel,


Not sure how using a JLabel will help.


It simplifies the code a little, as I said.
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Updated my code so that it both starts by the SwingUtilities.invokeLater() and uses the Swing Timer.  The display is "correct" (no distortion of the graphics) but still jerky (there are noticeable pauses between each time the screen repaints).  I have updated GitHub.
 
Rob Camick
Rancher
Posts: 3208
29
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

It simplifies the code a little, as I said.


I gave reasons why I don't think it does simplify the code:

1. you need to add the setOpaque( false ) statement in order for the JLabel to paint the background. This is done automatically for a JPanel.
2. the UI for a JLabel is more complicated than the UI for a JPanel. All the paintComponent() method does is paint the background color. A JLabel has to check for for text and icons etc.

I know it is not my question but I am always willing to learn something new. It appears I am missing the point of the suggestion. What are your reasons for making that statement.

All I see from your original suggestion is: "You can update the background of a JLabel,"

How is setting the background of a JLabel simpler than setting the background of a panel?

 
Piet Souris
Bartender
Posts: 4109
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sigh...

for the second time: it makes the code a little easier. Now I'm facing a dilemma: should I believe the convincing reasons you give, or should I believe the simple code I'm staring at? And, I wonder, what have I to do with that complexx UI that I did not notice? But please, don't explain.
 
Rob Camick
Rancher
Posts: 3208
29
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

there are noticeable pauses between each time the screen repaints)


That is because you have the "time to generations" set to 300. Try something like 15 to increase the animation.

You would also want to increase the "number of generation" to something like 200 to have the same elapsed time.

A couple other suggestions.

Even though you stop updating the panel after a fixed number of generations the Timer still fires events.

You can stop the Timer with code like:



Also in your painting code I question if you need:


since the drawing of the gridlines should paint the outline of each cell.

At least the painting looks the same to me when I remove the code.
 
Paul Clapham
Marshal
Posts: 25965
70
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Piet Souris wrote:Now I'm facing a dilemma: should I believe the convincing reasons you give, or should I believe the simple code I'm staring at?



Well, who ya gonna believe me or your own eyes?
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Rob Camick wrote:

there are noticeable pauses between each time the screen repaints)


That is because you have the "time to generations" set to 300. Try something like 15 to increase the animation.


Decreasing the time between generations doesn't change the effect.  Say we're at 300 ms.  I will see two or three generations go by with 300 ms between them.  Then I will see a generation that takes 500 ms or so, then two or three at 50 ms and so on.  This effect seems to go away after about 100 generations, so it may have to do with caching.
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Rob Camick wrote:Even though you stop updating the panel after a fixed number of generations the Timer still fires events.

You can stop the Timer with code like:


Thank you, this works.  However, I now have instructions to be able to pause the generation display, unpause, and restart, meaning to wipe the "universe" clean and start with a new random universe.  Timer has a restart, but this is different than what I need.  There doesn't seem to be pause and unpause methods.  Is there an idiom in Swing for pausing the event loop?  Someone here said Thread.sleep() and Swing don't play well together, and that seems to be the case, as this code just freezes the UI:
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Rob Camick wrote:Also in your painting code I question if you need:


since the drawing of the gridlines should paint the outline of each cell.

At least the painting looks the same to me when I remove the code.


That's weird, because when I remove that line, almost all the grid line disappear.  I am on Linux Ubuntu, if that makes a difference.
 
Knute Snortum
Sheriff
Posts: 7108
184
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

I wrote:I now have instructions to be able to pause the generation display, unpause, and restart, meaning to wipe the "universe" clean and start with a new random universe.


From my research it looks like I will have to run the evolve code in a Thread and communicate to that thread from the EDT.  I'm not good at Threads.  Maybe I should start a new topic since we're talking about something different than the subject of this topic?
 
Rob Camick
Rancher
Posts: 3208
29
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

I am on Linux Ubuntu, if that makes a difference.


It appears it does make a difference. I am using JDK11 on Windows 10.

It doesn't appear to be jerky or lag when painting. When I use 300 as the delay. The painting is at a constant interval and the entire frame refreshes at once.

I suggested a faster rate because I thought you might to trying to get more of a "flowing" effect from one generation to another which can only be achieved by a faster animation.


Is there an idiom in Swing for pausing the event loop?


You should be able to use stop/start for your pause/resume functionality.

I believe all the restart does is allow you to have an initial delay that is different from the ongoing delay. So yes you would use this if you want to clear the board and wait a short time before starting a new generation of events.

I remove that line, almost all the grid line disappear.


That is interesting. I have no idea why that would happen. The painting should be incremental. That is the grid lines should be drawn and then the live cells should be filled in.

So another suggestion is to keep the code that either draws the filled rectangle or empty rectangle and get rid of the code that draws the grid lines.

That is if you always paint every cell in a filled or empty state then there is no need for grid lines. That was only needed for your original approach where you painted background grid lines and then painted visible components on top of the grid.
 
Paul Clapham
Marshal
Posts: 25965
70
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If I wanted to implement pause and unpause, I would leave the existing display timer in place and provide it with a boolean value which suppresses the display part. Setting the boolean to true would then be "pause" and setting it to false would be "unpause". That's based on the assumption that the display frequence  is quite small (e.g. less than a second).
 
Piet Souris
Bartender
Posts: 4109
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In the GameOfLife class, I added a JPanel containing four JButtons: start, stop, reset and continue, and added that panel to the JFrame.

To these buttons I added an ActionListener. For instance:

and added the method:

In the universeController I added the method 'processStart()',
that invokes the evolve-method.
And likewise for the stop-button. The method 'processStop()' in the universeController stops the timer, and reset stops the timer, set it to null and invokes evolve() again.

IIt works, but I get the impression that an enum with the different states of the universeController makes life easy.

And the initial 1000 ms-delay in the timer works confusing when clicking on a button: it appears as if the buttons don't work!

 
Hold that thought. Tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
reply
    Bookmark Topic Watch Topic
  • New Topic