Currently as part of a project I'm working on, I am implementing a component which can be used to visualize a permutation of bits (as part of a cryptographic algorithm). I am doing so by creating two rows of "pins" and connecting them by drawing lines between the tips, creating a sort of web between them.
An important part of this is that I am using this visualization both on its own as well as a part of other visualizations (for example, I may want to include S-Boxes) and therefore I need to be able to turn the pins on and off. My solution to this was using JPanels to put the rows of pins into a header and footer panel which can be made invisible.
I am laying them out vertically in a BoxLayout, but I end up with space between them, even if I add glue above the header and below the footer.
My example looks like this when initialized:
And when I resize it, they come together a bit, but still only touch on one side:
I'm guessing this is some sort of silly mistake converting my user space into device space in terms of component size and layout, but for the life of me I cannot find it. This is my code, although I apologize for the mess:
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Ellipse2D;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PermutationWeb extends JPanel
{
private static enum EndPanelType
{
HEADER, FOOTER
}
private final JPanel header;
private final JPanel mainPanel;
private final JPanel footer;
private double widthFactor;
private double heightFactor;
private int widthMax;
private int heightMax;
private int[] indexMappings;
private Point2D.Double[] endpoints;
private Line2D.Double[] drawingLines;
public PermutationWeb(int indices, boolean endPanelsOn)
{
super();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
widthMax = (indices + 1)*2;
heightMax = (int)Math.round(widthMax*(3.0/17.0));
widthFactor = 1;
heightFactor = 1;
endpoints = new Point2D.Double[indices * 2];
drawingLines = new Line2D.Double[indices];
for(int i=0; i<indices; i++)
{
endpoints[i] = new Point2D.Double(i*2+2, 0);
endpoints[i+indices] = new Point2D.Double(i*2+2, heightMax);
drawingLines[i] = new Line2D.Double();
}
header = new WebEndPanel(EndPanelType.HEADER);
mainPanel = new WebMainPanel();
footer = new WebEndPanel(EndPanelType.FOOTER);
add(Box.createVerticalGlue());
add(header);
add(mainPanel);
add(footer);
add(Box.createVerticalGlue());
setEndPanelsOn(endPanelsOn);
}
public Point2D getEndpoint(int index)
{
return endpoints[index];
}
public void updateMappings(int[] mappings)
{
this.indexMappings = mappings;
for(int i=0; i<indexMappings.length; i++)
{
drawingLines[i].setLine(endpoints[i], endpoints[indexMappings.length + indexMappings[i]]);
}
//paint();
}
public void setEndPanelsOn(boolean endPanelsOn)
{
header.setVisible(endPanelsOn);
footer.setVisible(endPanelsOn);
}
#Override
public Dimension getMaximumSize()
{
int height = mainPanel.getHeight();
if(header.isVisible())
{
height += (header.getHeight() * 2);
}
int width = mainPanel.getWidth();
return new Dimension(width, height);
}
#Override
public Dimension getPreferredSize()
{
return getMaximumSize();
}
public static void main(String[] args)
{
JFrame jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(800, 600);
int[] mappings = {0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15};
PermutationWeb webTest = new PermutationWeb(16, true);
jf.add(webTest);
jf.setVisible(true);
webTest.setVisible(true);
webTest.updateMappings(mappings);
System.out.printf("Header: [%s]\nMainPanel: [%s]\nFooter: [%s]\n",
webTest.header.getSize().toString(),
webTest.mainPanel.getSize().toString(),
webTest.footer.getSize().toString());
}
private class WebMainPanel extends WebSubPanel
{
private static final double HEIGHT_RATIO = 0.25;
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g2);
scaleTo(getSize());
g2.scale(widthFactor, widthFactor);
g2.setStroke(new BasicStroke((float)(2.0/widthFactor)));
for(Line2D line: drawingLines)
{
g2.draw(line);
}
}
#Override
public Dimension getMaximumSize()
{
return new Dimension(MAX_WIDTH_PX, (int)(MAX_WIDTH_PX*HEIGHT_RATIO));
}
}
private class WebEndPanel extends WebSubPanel
{
private static final double HEIGHT_RATIO = 0.125;
private static final double PIN_RADIUS = 0.5;
private final EndPanelType endType;
private Line2D.Double[] edgeLines;
private Ellipse2D.Double[] pinHeads;
public WebEndPanel(EndPanelType endType)
{
super();
this.endType = endType;
this.edgeLines = new Line2D.Double[endpoints.length/2];
this.pinHeads = new Ellipse2D.Double[endpoints.length/2];
for(int i=0; i<edgeLines.length; i++)
{
Point2D pointA;
Point2D pointB;
if(EndPanelType.HEADER.equals(this.endType))
{
pointA = new Point2D.Double(i*2+2, 4);
pointB = new Point2D.Double(i*2+2, 2);
pinHeads[i] = new Ellipse2D.Double(
pointB.getX()-PIN_RADIUS,
pointB.getY()-PIN_RADIUS*2,
PIN_RADIUS*2,
PIN_RADIUS*2);
}
else // FOOTER
{
pointA = new Point2D.Double(i*2+2, 0);
pointB = new Point2D.Double(i*2+2, 2);
pinHeads[i] = new Ellipse2D.Double(
pointB.getX()-PIN_RADIUS,
3-PIN_RADIUS*2,
PIN_RADIUS*2,
PIN_RADIUS*2);
}
edgeLines[i] = new Line2D.Double(pointA, pointB);
}
}
#Override
public Dimension getMaximumSize()
{
return new Dimension(MAX_WIDTH_PX, (int)(MAX_WIDTH_PX*HEIGHT_RATIO));
}
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g2);
scaleTo(getSize());
g2.scale(widthFactor, widthFactor);
g2.setStroke(new BasicStroke((float)(2.0/widthFactor)));
for(Line2D line: edgeLines)
{
g2.draw(line);
}
for(Ellipse2D pin: pinHeads)
{
g2.draw(pin);
}
}
}
private abstract class WebSubPanel extends JPanel
{
protected static final int MAX_WIDTH_PX = 800;
public WebSubPanel()
{
super();
setBorder(null);
setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
}
public void scaleTo(Dimension d)
{
widthFactor = d.getWidth() / (double)widthMax;
heightFactor = d.getHeight() / (double)heightMax;
}
#Override
public Dimension getPreferredSize()
{
return getMaximumSize();
}
}
}
The ultimate goal here is a resizable web where the header and footer WebEndPanels can be invisible, but have 0 space between them and the WebMainPanel when shown (as though they were a single entity).
A BoxLayout will resize a component up to its maximum size if space is available.
So you first need to implement the getPreferredSize() method of the component so it can be packed as displayed at its normal size.
Then if it has the ability to grow (and your custom painting supports this) you override the getMaximumSize() method to return the size.
So the painting needs to be based on the actual size of the panel if you want it to be contiguous with other panels.
Related
I am trying to add a thing like this in my music player application in swing.
I tried to add a rectangle to BorderLayout.SOUTH, but it never appeared on screen. Here is what I did:
public class MyDrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(200,200,200,200);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
MyDrawPanel a = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.SOUTH,a);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1000,1000);
frame.setVisible(true);
}
}
I just did not try 200,200,200,200, but I tried a lot of values, even with the help of a for loop, but it never appeared on screen. If I used CENTER instead of SOUTH it appeared. I read the documentation to check how fillRect works, but it simply said it added x+width and y+height. The point (0,0) is the top left corner. I checked that by adding a rectangle to CENTER layout. How cam I do it?
I did not share the output, because it was just a blank screen.
The values you give to fillRect are wrong. The first two are the top left corner's coordinates, relative to the component you're painting in; in your case the MyDrawPanel. With the code you posted, this drawing area is outside of the container the panel is placed in. You want to do
g.fillRect(0,0,200,200);
A note: You usually want to call frame.pack() after you've finished adding all components, so it can layout itself. In your case, this results in a tiny window because the system doesn't know how large it should be. You probably want to add a method
public Dimension getPreferredSize() {
System.out.println("getting pref size");
return new Dimension(200, 200);
}
to ensure it's always large enough to draw the full rectangle.
Also, you should call frame.getContentPane().setLayout(new BorderLayout()) before. You can print it out without setting it to see it is not the default. EDIT: As VGR points out, the documentation says that it is in fact a BorderLayout. I cannot confirm that is the case - it is in fact a RootLayout. That seems to behave like a BorderLayout though.
I thought this might make a quick little project. Here's the level meter I came up with.
The important parts are the DrawingPanel and the LevelMeterModel. The DrawingPanel takes the information from the LevelMeterModel and paints the bars on a JPanel.
The LevelMeterModel is an int array of levels, a minimum level, and a maximum level. The maximum level could be calculated, but I assumed music has a certain volume and frequency range.
The JFrame holds the DrawingPanel. A Swing Timer varies the levels somewhat randomly. The random numbers are in a small range so the bar heights don't change abruptly.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class LevelMeterGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LevelMeterGUI());
}
private final DrawingPanel drawingPanel;
private final LevelMeterModel model;
public LevelMeterGUI() {
this.model = new LevelMeterModel();
this.drawingPanel = new DrawingPanel(model);
}
#Override
public void run() {
JFrame frame = new JFrame("Level Meter GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
System.out.println("Frame size: " + frame.getSize());
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
model.setRandomLevels();
drawingPanel.repaint();
}
});
timer.start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final int drawingWidth, drawingHeight, margin, rows;
private final Dimension barDimension;
private final LevelMeterModel model;
public DrawingPanel(LevelMeterModel model) {
this.model = model;
this.margin = 10;
this.rows = 20;
this.barDimension = new Dimension(50, 10);
int columns = model.getLevels().length;
drawingWidth = columns * barDimension.width + (columns + 1) * margin;
drawingHeight = rows * barDimension.height + (rows + 1) * margin;
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(drawingWidth, drawingHeight));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int maximum = model.getMaximumLevel();
double increment = (double) maximum / rows;
int peak = rows * 75 / 100;
int x = margin;
for (int level : model.getLevels()) {
int steps = (int) Math.round((double) level / increment);
int y = drawingHeight - margin - barDimension.height;
for (int index = 0; index < steps; index++) {
if (index < peak) {
g.setColor(Color.GREEN);
} else {
g.setColor(Color.RED);
}
g.fillRect(x, y, barDimension.width, barDimension.height);
y = y - margin - barDimension.height;
}
x += margin + barDimension.width;
}
}
}
public class LevelMeterModel {
private final int minimumLevel, maximumLevel;
private int[] levels;
private final Random random;
public LevelMeterModel() {
this.minimumLevel = 100;
this.maximumLevel = 999;
this.levels = new int[8];
this.random = new Random();
setRandomLevels();
}
public void setRandomLevels() {
for (int index = 0; index < levels.length; index++) {
levels[index] = getRandomLevel(levels[index]);
}
}
private int getRandomLevel(int level) {
if (level == 0) {
return random.nextInt(maximumLevel - minimumLevel) + minimumLevel;
} else {
int minimum = Math.max(level * 90 / 100, minimumLevel);
int maximum = Math.min(level * 110 / 100, maximumLevel);
return random.nextInt(maximum - minimum) + minimum;
}
}
public int[] getLevels() {
return levels;
}
public int getMinimumLevel() {
return minimumLevel;
}
public int getMaximumLevel() {
return maximumLevel;
}
}
}
I am having trouble with Swing and the JScrollPane.
I am having a strange behaviour.
I extended JScrollPane. I display an image in it and draw rectangles over it to define areas.
With a big image, I have an Horizontal and a Vertical scrollbars.
I - ok - When I move one scrollbar or the other I see my image move too as it should.
II - not ok - When I move one scrollbar an leave it in between max and min position, then when I move my second scrollbar my image disappears.
With some debug prints, I found out that paintComponent, is not called when in case II.
I would like to know why it is not calling paintComponent and how I can fix it.
Here below is my class:
package GUI;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import javax.swing.JScrollPane;
public class DrawingPanel extends JScrollPane {
private static final long serialVersionUID = 1L;
private static final Color DRAWING_COLOR = new Color(255, 100, 200);
private static final Color FINAL_DRAWING_COLOR = Color.red;
private static final double ZOOMING_STEP = 1.1;
private Image sImg;
private Point startPt;
private Point endPt;
private Point currentPt;
private int prefW;
private int prefH;
private double zoomFactor = 1;
private boolean zoomer = false;
private boolean loaded = false;
public DrawingPanel() {
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void loadImage(Image img) {
sImg = img;
prefW = sImg.getWidth(null);
prefH = sImg.getHeight(null);
zoomFactor = getSize().getWidth() / prefW;
zoomer = true;
loaded = true;
repaint();
revalidate();
}
int countPaint = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("paintComponent " + countPaint);
if (loaded) {
int zoomWidth = (int) (prefW * zoomFactor);
int zoomHeight = (int) (prefH * zoomFactor);
if (zoomer) {
((Graphics2D) g).scale(zoomFactor, zoomFactor);
setSize(zoomWidth, zoomHeight);
zoomer = false;
}
g.drawImage(sImg, 0, 0, zoomWidth, zoomHeight, null);
drawRectangle(g);
}
g.dispose();
countPaint++;
}
#Override
public Dimension getPreferredSize() {
return loaded ?
this.getSize() :
new Dimension((int) (prefW*zoomFactor), (int) (prefH*zoomFactor));
}
private void drawRectangle(Graphics g) {
Point secondPoint = (currentPt != null) ? currentPt : endPt;
Color color = (currentPt != null) ? DRAWING_COLOR : FINAL_DRAWING_COLOR;
if (startPt!=null && secondPoint!=null) {
int x = Math.min(startPt.x, secondPoint.x);
int y = Math.min(startPt.y, secondPoint.y);
int rectangleWidth = Math.abs(startPt.x - secondPoint.x);
int rectangleHeight = Math.abs(startPt.y - secondPoint.y);
g.setColor(color);
g.drawRect(x, y, rectangleWidth, rectangleHeight);
}
}
public void deleteRectangle(){
startPt = null;
endPt = null;
}
public void increaseZoom(Point p) {
double oldZoom = zoomFactor;
zoomFactor *= ZOOMING_STEP;
repositonPointAfterZoom(oldZoom, zoomFactor);
}
public void decreaseZoom(Point p) {
double oldZoom = zoomFactor;
zoomFactor /= ZOOMING_STEP;
repositonPointAfterZoom(oldZoom, zoomFactor);
}
public void repositonPointAfterZoom(double oldZoom, double newZoom) {
double evolution = newZoom/oldZoom;
if (startPt!=null) {
startPt.setLocation(startPt.x * evolution, startPt.y * evolution);
}
if (endPt!=null) {
endPt.setLocation(endPt.x * evolution, endPt.y * evolution);
}
repaint();
}
// Getter et setter
public void setStartPt(Point startPt) {
this.startPt = startPt;
}
public void setEndPt(Point endPt) {
this.endPt = endPt;
}
public void setCurrentPt(Point currentPt) {
this.currentPt = currentPt;
}
public int getZoomCalculateX(int value){
return (int) (value / zoomFactor);
}
public int getZoomCalculateY(int value){
return (int) (value / zoomFactor);
}
public void setZoomer(boolean zoomer) {
this.zoomer = zoomer;
}
}
EDIT : Bellow is the class (simplified) that uses DrawingPanel so you can have a reproducible exemple.
package GUI;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import fileHandler.*;
public class GUI {
private JFrame frame;
private MenuBar menubar;
private DrawingPanel panelImage;
private JScrollPane scroll;
private GroundTruth openFile;
private int[] panelImageDown = new int[2];
private int[] panelImageUp = new int[2];
private Menu CoordinateMenu1 = new Menu();
private Menu CoordinateMenu2 = new Menu();
private int actualPagePdf;
private PDFRenderer renderer;
public static void main(String[] args) throws IOException {
new GUI();
}
public GUI() throws IOException {
JFrame frame = CreateFrame();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JFrame CreateFrame() throws IOException {
frame = new JFrame();
frame.setMenuBar(CreateMenuBar());
frame.setContentPane(SplitScreen());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("GTA - Ground Truth Annotator");
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
return frame;
}
private MenuBar CreateMenuBar() {
menubar = new MenuBar();
menubar.add(CreateFileMenu());
menubar.add(new Menu("Selection coordinates:"));
menubar.add(CoordinateMenu1);
menubar.add(new Menu("Width/Height:"));
menubar.add(CoordinateMenu2);
return menubar;
}
private Menu CreateFileMenu() {
Menu mFile = new Menu("File");
MenuItem miOpenImage = new MenuItem("Open Image/PDF File");
mFile.add(miOpenImage);
miOpenImage.addActionListener(OpenFileActionListener);
mFile.addSeparator();
MenuItem miExit = new MenuItem("Exit Program");
mFile.add(miExit);
miExit.addActionListener(ExitActionListener);
return mFile;
}
private JPanel SplitScreen() throws IOException {
JPanel splittedScreen = new JPanel(new GridLayout(1, 2));
splittedScreen.add(CreateLeftPanel());
splittedScreen.add(CreateRightPanel());
return splittedScreen;
}
private JLayeredPane CreateLeftPanel() throws IOException {
JLayeredPane panel = new JLayeredPane();
panel.setLayout(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
panel.add(CreateImageScrollPane());
return panel;
}
private JScrollPane CreateImageScrollPane() throws IOException {
scroll = new JScrollPane(CreateImagePanel((String) null));
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE);
return scroll;
}
private DrawingPanel CreateImagePanel(String path) throws IOException {
if (panelImage == null) {
panelImage = new DrawingPanel();
}
if (path != null) {
panelImage.loadImage(ImageIO.read(new File(path)));
}
panelImage.addMouseListener(PanelImageMouseListener);
panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
panelImage.setOpaque(false);
panelImage.revalidate();
panelImage.repaint();
panelImage.requestFocus();
return panelImage;
}
private DrawingPanel CreateImagePanel(Image image) throws IOException {
if (panelImage == null) {
panelImage = new DrawingPanel();
}
panelImage.loadImage(image);
panelImage.addMouseListener(PanelImageMouseListener);
panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
panelImage.setOpaque(false);
panelImage.revalidate();
panelImage.repaint();
return panelImage;
}
private JPanel CreateRightPanel() {
JPanel panel = new JPanel(new GridLayout(0, 1));
//...
return panel;
}
ActionListener OpenFileActionListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
OpenFile();
}
};
ActionListener ExitActionListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Object[] options = {"Yes, quit now", "No, go back"};
int n = JOptionPane.showOptionDialog(
frame, "ATTENTION: closing without saving will cause any unsaved files to be lost. Do you want to proceed?",
"Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]
);
switch (n) {
case JOptionPane.YES_OPTION:
System.exit(0);
case JOptionPane.NO_OPTION:
case JOptionPane.CANCEL_OPTION:
}
}
};
MouseListener PanelImageMouseListener = new MouseListener() {
public void mousePressed(MouseEvent me) {
panelImageDown = new int[]{
panelImage.getZoomCalculateX(me.getX()), panelImage.getZoomCalculateY(me.getY())
};
panelImageUp = null;
CoordinateMenu1.setLabel(String.format("%s:%s", panelImageDown[0], panelImageDown[1]));
CoordinateMenu2.setLabel("");
panelImage.setStartPt(me.getPoint());
panelImage.repaint();
}
public void mouseReleased(MouseEvent me) {
panelImageUp = new int[]{
Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
};
CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
panelImage.setEndPt(me.getPoint());
panelImage.setCurrentPt(null);
panelImage.repaint();
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
};
MouseMotionAdapter PanelImageMouseMotionAdapter = new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
panelImageUp = new int[]{
Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
};
CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
panelImage.setCurrentPt(me.getPoint());
panelImage.repaint();
}
};
MouseWheelListener PanelImageMouseWheelListener = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent me) {
if (me.isAltDown()) {
if (me.getWheelRotation() < 0) {
panelImage.setZoomer(true);
panelImage.increaseZoom();
panelImage.repaint();
panelImage.requestFocus();
//scroll.repaint();
//Zoom out
} else if(me.getWheelRotation() > 0) {
panelImage.setZoomer(true);
panelImage.decreaseZoom();
panelImage.repaint();
panelImage.requestFocus();
//scroll.repaint();
}
}
}
};
private void OpenFile() {
openFile = new GroundTruth();
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(
"Images / PDF Scan",
"bmp", "jpeg", "jpg", "png", "tif", "tiff", "pdf"
));
if (fileChooser.showOpenDialog(frame) != 0) {
return;
}
openFile.setFilename(fileChooser.getSelectedFile().getAbsolutePath());
if (getExtension(fileChooser.getSelectedFile().getName()).equals("pdf")) {
try {
PDDocument doc = PDDocument.load(fileChooser.getSelectedFile());
numberPagePdf = doc.getNumberOfPages();
actualPagePdf = 0;
renderer = new PDFRenderer(doc);
setPdfPage(actualPagePdf);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
CreateImagePanel(fileChooser.getSelectedFile().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setPdfPage(int pageNumber){
try {
BufferedImage bim = renderer.renderImageWithDPI(pageNumber, 300);
refreshInfoPageSection();
CreateImagePanel(bim);
} catch (IOException e) {
e.printStackTrace();
}
}
// refresh the label who indicate the page display and set visible or inivisble the previous and next button
private void refreshInfoPageSection(){
panelImage.deleteRectangle();
}
public String getExtension(String filename) {
return FilenameUtils.getExtension(filename);
}
}
Here's a simplified example of a drawing JPanel that's larger than the scrolling JPanel.
You can see in the image that the drawing JPanel is larger both horizontally and vertically than the scrolling JPanel.
I don't call the paintComponent method at all in this code. Because I set up the GUI properly, Swing itself calls the repaint method when you move the scroll bars.
Here are the important things I did.
I started the Swing GUI with a call to the SwingUtilities invokeLater method. This method makes sure that the Swing components are created and executed on the Event Dispatch Thread.
I used a JFrame, two JPanels, and a JScrollPane. I extended JPanel to create the drawing panel. I used a JScrollPane, JPanel, and JFrame. The only time you extend a Swing component, or any Java class, is when you want to override one or more class methods.
I used Swing layout managers. I used a BorderLayout for the JFrame and scrolling JPanel.
Here's the complete runnable code. Why, you can even call it a minimal reproducible example!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LargeDrawingPanel implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LargeDrawingPanel());
}
#Override
public void run() {
JFrame frame = new JFrame("Large Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.setPreferredSize(new Dimension(400, 400));
JScrollPane scrollPane = new JScrollPane(new DrawingPanel());
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(2000, 2000));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
int x = 100;
int y = 100;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
g.fillRect(x, y, 100, 100);
x += 200;
}
y += 200;
x = 100;
}
}
}
}
Edited to add: The OP posted additional questions in a comment.
Thanks for the example but it doesn't show me how to print an image in
a JPanel.
Pretty simple. Read an image using the ImageIO class, save the image in an application model consisting of one or more plain Java getter / setter classes and use the Graphics drawImage method to draw the image.
Your preview has only one scrollbar moved not both - which is my
problem.
Did you actually run the code I provided? I can only move one scrollbar at a time. The drawing JPanel extends both horizontally and vertically.
And it doesn't explain why my example doesn't work.
Your example is riddled with errors. Start over, building a Swing application one Swing component at a time using sound principles. The Oracle tutorial, Creating a GUI With JFC/Swing, will show you the correct way to create a Swing application. You can skip the Netbeans section.
So basically I'm trying to create a reversi game. First of all I created a board populated by buttons and attached ID's to them, so I can access them afterwards if needed. Now I am trying to draw a game piece on each of the buttons, however I can't getGraphics() of the button since I read that is a bad idea and also returns null. Keep in mind that I want to keep all of my entities separate: the board, the cell and the piece, since I developing this using MVC pattern.
board.java
import java.awt.GridLayout;
import javax.swing.JPanel;
public class Board extends JPanel {
private static final int sizeOfBoard = 8;
public Board() {
int id =0;
setLayout(new GridLayout(sizeOfBoard,sizeOfBoard));
for (int i = 0; i < sizeOfBoard; i++) {
for (int j = 0; j < sizeOfBoard; j++) {
Cell cell = new Cell(id++);
Disk disk = new Disk();
cell.add(disk);
add(cell);
}
}
setSize(600,500);
setVisible(true);
}
cell.java
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.Painter;
public class Cell extends JButton{
private int id;
private boolean taken;
private String colour;
private Painter painter;
public Cell(int id){
this.id = id;
}
public int getId(){
return id;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
}
}
disk.java
import java.awt.Graphics;
import javax.swing.JComponent;
public class Disk extends JComponent{
#Override
public void paintComponent ( Graphics g ) {
super.paintComponent(g);
g.drawOval(50,50,50,50);
}
}
TL;DR How should I rewrite my code so it would have an oval on each button.
Thanks in advance.
The simplest solution: create your oval or disk images in a BufferedImage, put it into an ImageIcon, and simply swap Icons on your JButton or JLabel via its setIcon(myIcon) method. I'd create 3 ImageIcons if this were my GUI, a blank one for the initial state, and then two different colored ones for the occupied states.
For example:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class ReversiPanel extends JPanel {
private static final int SIDES = 8;
private static final int ICON_LENGTH = 60;
private static final Color BG = Color.BLACK;
private static final Color LABEL_COLOR = Color.GREEN.darker();
private JLabel[][] labelGrid = new JLabel[SIDES][SIDES];
private Icon blankIcon;
private Icon blackIcon;
private Icon whiteIcon;
public ReversiPanel() {
blankIcon = createIcon(new Color(0, 0, 0, 0));
blackIcon = createIcon(Color.BLACK);
whiteIcon = createIcon(Color.WHITE);
setBackground(BG);
setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
setLayout(new GridLayout(SIDES, SIDES, 1, 1));
MyMouse myMouse = new MyMouse();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
JLabel label = new JLabel(blankIcon);
label.setOpaque(true);
label.setBackground(LABEL_COLOR);
label.addMouseListener(myMouse);
labelGrid[i][j] = label;
add(label);
}
}
}
private Icon createIcon(Color color) {
BufferedImage img = new BufferedImage(ICON_LENGTH, ICON_LENGTH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(color);
int gap = 4;
int w = ICON_LENGTH - 2 * gap;
int h = w;
g2.fillOval(gap, gap, w, h);
g2.dispose();
return new ImageIcon(img);
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Icon icon = label.getIcon();
if (icon == blankIcon) {
label.setIcon(blackIcon);
} else if (icon == blackIcon) {
label.setIcon(whiteIcon);
} else {
label.setIcon(blankIcon);
}
}
}
private static void createAndShowGui() {
ReversiPanel mainPanel = new ReversiPanel();
JFrame frame = new JFrame("ReversiPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I am trying to create a program that uses a JComboBox containing specific shapes (Circle, Square, Oval, Rectangle). After the user clicks on a specified shape, the Panel will display 20 of that shape in random dimensions and locations.
I am having trouble on how to make the shapes have random dimensions and locations. Here is my code so far. Any advice or sources to look at would be appreciated.
Thank you.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
public class HW1b extends JFrame
{
public HW1b()
{
super("Shapes");
final ComboPanel comboPanel = new ComboPanel();
String[] shapeItems = {"Circle", "Square", "Oval", "Rectangle"};
JComboBox shapeBox = new JComboBox<String>(shapeItems);
shapeBox.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent ie)
{
if (ie.getStateChange() == ItemEvent.SELECTED)
{
String item = (String)ie.getItem();
if(shapeBox.getSelectedItem().equals("Circle"))
comboPanel.makeCircles();
if(shapeBox.getSelectedItem().equals("Square"))
comboPanel.makeSquares();
if(shapeBox.getSelectedItem().equals("Oval"))
comboPanel.makeOvals();
if(shapeBox.getSelectedItem().equals("Rectangle"))
comboPanel.makeRectangles();
}
}
});
JPanel southPanel = new JPanel();
southPanel.add(shapeBox);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().add(comboPanel, "Center");
getContentPane().add(southPanel, "South");
setSize( 400, 400 );
setLocation( 200, 200 );
setVisible( true );
}
private class ComboPanel extends JPanel
{
int w, h;
Random rand;
static final int OVAL = 0;
static final int RECTANGLE = 1;
int shapeType = -1;
public ComboPanel()
{
rand = new Random();
setBackground(Color.white);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int x, y;
Shape s = null;
for (int i = 0; i < 20; i++)
{
x = rand.nextInt(width - w);
y = rand.nextInt(width - h);
switch(shapeType)
{
case OVAL: s = new Ellipse2D.Double(x,y,w,h);
break;
case RECTANGLE: s = new Rectangle2D.Double(x,y,w,h);
break;
}
if (shapeType > -1)
g2d.draw(s);
}
}
public void makeCircles()
{
shapeType = OVAL;
w = 75;
h = 75;
repaint();
}
public void makeSquares()
{
shapeType = RECTANGLE;
w = 50;
h = 50;
repaint();
}
public void makeOvals()
{
shapeType = OVAL;
w = 80;
h = 60;
repaint();
}
public void makeRectangles()
{
shapeType = RECTANGLE;
w = 80;
h = 40;
repaint();
}
}
public static void main(String[] args)
{
new HW1b();
}
}
You're hard-coding w and h in your code, and so there's no way for this to vary among your shapes. Instead of doing this, use your Random variable, rand, to select random w and h values that are within some desired range. Myself, I wouldn't create my shapes within the paintComponent method since painting is not fully under my control and can occur when I don't want it to. For instance, in your code, your shapes will vary tremendously if the GUI is resized. Instead I'd create a collection such as an ArrayList<Shape> and fill it with created Shape objects (i.e., Ellipse2D for my circles) when desired, and then iterate through that collection within your paintComponent method, drawing your shapes.
for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
public class SomeShapes extends JPanel {
private ShapePanel shapePanel = new ShapePanel();
private JComboBox<MyShape> myShapeCombo = new JComboBox<>(MyShape.values());
public SomeShapes() {
myShapeCombo.setSelectedIndex(-1);
myShapeCombo.addItemListener(new ComboListener());
JPanel bottomPanel = new JPanel();
bottomPanel.add(myShapeCombo);
setLayout(new BorderLayout());
add(shapePanel, BorderLayout.CENTER);
add(bottomPanel, BorderLayout.PAGE_END);
}
private class ComboListener implements ItemListener {
#Override
public void itemStateChanged(ItemEvent e) {
MyShape myShape = (MyShape) e.getItem();
shapePanel.drawShapes(myShape);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("SomeShapes");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SomeShapes());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum MyShape {
OVAL("Oval"), RECTANGLE("Rectangle"), SQUARE("Square"), CIRCLE("Circle");
private String name;
private MyShape(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return getName();
}
}
class ShapePanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color SHAPE_COLOR = Color.BLUE;
private static final int SHAPE_COUNT = 20;
private static int MIN = 5;
private static int MAX = 200;
private List<Shape> shapeList = new ArrayList<>();
private Random random = new Random();
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public void drawShapes(MyShape myShape) {
shapeList.clear(); // empty the shapeList
switch (myShape) {
case OVAL:
drawOval();
break;
case RECTANGLE:
drawRectangle();
break;
// etc...
default:
break;
}
repaint();
}
private void drawOval() {
// for loop to do this times SHAPE_COUNT(20) times.
for (int i = 0; i < SHAPE_COUNT; i++) {
// first create random width and height
int w = random.nextInt(MAX - MIN) + MIN;
int h = random.nextInt(MAX - MIN) + MIN;
// then random location, but taking care so that it
// fully fits into our JPanel
int x = random.nextInt(getWidth() - w);
int y = random.nextInt(getHeight() - h);
// then create new Shape and place in our shapeList.
shapeList.add(new Ellipse2D.Double(x, y, w, h));
}
}
private void drawRectangle() {
// .... etc
}
//.. .. etc
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// set rendering hints for smooth ovals
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(SHAPE_COLOR);
// iterate through the shapeList ArrayList
for (Shape shape : shapeList) {
g2d.draw(shape); // and draw each Shape it holds
}
}
}
I am working on a project which is a Checkout Simulation. I have the code to make it work run but i am struggling to understand and implement how to add graphics(in my case a square) once a certain condition is true. For example i have made my code so that it goes through random numbers and if 2,4,6 or 8 has been randomly generated, someone will be added to the queue and the same goes for if they are even numbers 1 or 3, someone is removed from the queue. I basically just want to know how to add a square to the screen once i have met my condition (for example, generating a 4 should add a square to the screen but it doesn't)
ANY help would really be appreciated!
public class MainPanel extends JPanel {
private Queue<String> tillQueue;
private int rndNumber;
private int currentLength;
private ArrayList<Integer> lengthList;
private double mean;
private Random rand;
private int MAXLENGTH;
private static Random r = new Random();
private static Random r2 = new Random();
Color colour;
private static final int IMAGE_SIZE = 600;
private Timer timer;
private int delay;
private JButton startButton;
private JButton stopButton;
private BufferedImage buffer;
JToolBar toolbar;
public MainPanel() {
startButton = new JButton("START");
stopButton = new JButton("STOP");
toolbar = new JToolBar();
toolbar.add(startButton);
toolbar.add(stopButton);
this.buffer = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB);
setDoubleBuffered(false);
StartActionHandler start = new StartActionHandler();
StopActionHandler stop = new StopActionHandler();
TimerEvent timerEvt = new TimerEvent();
startButton.addActionListener(start);
stopButton.addActionListener(stop);
delay = 50;
timer = new Timer(delay, timerEvt);
}
public class TimerEvent implements ActionListener {
public void actionPerformed(ActionEvent e) {
//drawNext(buffer.getGraphics());
for (int time = 1; time < 9; time++) {
rndNumber = rand.nextInt(6) + 1; //generates random number
if (rndNumber == 2 || rndNumber == 4 || rndNumber == 6 || rndNumber == 8) {
//time is added to queue
tillQueue.add(String.valueOf(time));
drawNext(buffer.getGraphics());
repaint();
}
}
}
}
public class StartActionHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
timer.start();
}
}
private void drawNext(Graphics g) {
int x = r.nextInt(IMAGE_SIZE);
int y = r.nextInt(IMAGE_SIZE);
int red = r2.nextInt(255);
int green = r2.nextInt(255);
int blue = r2.nextInt(255);
Color randomColour = new Color(red, green, blue);
g.setColor(randomColour);
g.fillRect(x, y, 10, 10);
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(buffer, 0, 0, this);
}
}
Note several changes to get rendering working:
For convenience, use the buffer's createGraphics() method and dispose() it when done.
Initialize the offscreen buffer to a known state.
One instance of Random is usually sufficient.
Limit variable scope to the extent possible, e.g. private class TimerEvent.
Override getPreferredSize() to establish the rendering area size.
As tested:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.Timer;
/**
* #see https://stackoverflow.com/a/21238669/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MainPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
private static class MainPanel extends JPanel {
private static final int SIZE = 500;
private static final int DELAY = 100;
private static final Random r = new Random();
private final Queue<String> tillQueue = new LinkedList<>();
private Timer timer;
private JButton startButton;
private JButton stopButton;
private BufferedImage buffer;
private JToolBar toolbar;
public MainPanel() {
super(new BorderLayout());
startButton = new JButton("START");
stopButton = new JButton("STOP");
toolbar = new JToolBar();
toolbar.add(startButton);
toolbar.add(stopButton);
buffer = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = buffer.createGraphics();
g.clearRect(0, 0, SIZE, SIZE);
g.dispose();
StartActionHandler start = new StartActionHandler();
TimerEvent timerEvt = new TimerEvent();
timer = new Timer(DELAY, timerEvt);
startButton.addActionListener(start);
add(new JLabel(new ImageIcon(buffer)));
add(toolbar, BorderLayout.SOUTH);
}
private class TimerEvent implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int time = 1; time < 9; time++) {
if (r.nextInt(6) % 2 == 0) {
tillQueue.add(String.valueOf(time));
drawNext();
}
}
}
}
private class StartActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
}
}
private void drawNext() {
Graphics2D g = buffer.createGraphics();
int x = r.nextInt(SIZE);
int y = r.nextInt(SIZE);
g.setColor(new Color(r.nextInt()));
g.fillRect(x, y, 10, 10);
g.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(buffer, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
}
How is suposed to work? when you met the condition an item is added to tillQueue, but tillQueue is never readed...
I you want to draw something you can draw it in the method paintComponent.
To draw a rectangle simply use:
http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawRect(int, int, int, int)
You can iterate the tillQueue in the paintComponent method and draw the corresponding rectangles.