I have this code in which a Gantt Chart will be shown on another JFrame, other than the main UI frame. Is there any way such that the drawn Graphics here will be shown on a JPanel on the main UI frame?
chart= new JFrame();
final ArrayList<Task> tasks = taskList;
final int ET = processET;
chart.setSize(300, 300);
chart.setContentPane(new JPanel(){
public void paint(Graphics g){
for(int j=0;j < tasks.size();j++)
{
int ST = 0;
if(j!= tasks.size()-1) ST = tasks.get(j + 1).getStartTime();
else ST = ET;
int Product = ST * 20;
g.drawRect(50,50,Product,30);
g.drawString("p"+tasks.get(j).getPID(),(55+(tasks.get(j).getStartTime()*20)),70);
g.drawString(""+tasks.get(j).getStartTime(), (50+(tasks.get(j).getStartTime()*20)),100);
}
g.drawString(""+ET,50+(ET*20),100);
}
});
chart.pack();
chart.setLocationRelativeTo(null);
chart.setVisible(true);
I tried this code so that it would put it on a JPanel, but obviously it did not work, hence this inquiry. Please help and thanks :)
panel_2 = new JPanel(){ "same thing" };
panel.add(panel_2);
Is there any way such that the drawn Graphics here will be shown on a JPanel on the main UI frame?
There are a number of ways. See this answer for details. I'm thinking either a CardLayout or JTabbedPane might be the easiest, given that code is already rendered in a JPanel (that is currently added to a frame for display).
Related
I am a beginner with programming and I have been struggling with the following problem for some time.
I have a list of BufferedImages (this.docList). At the moment I am trying to
put the images onto a JPanel and put that panel inside a JScrollPane.
If I have more than one image, the second image seems to cover the first one.
With the following code I make and send the ScrollPane to the method getPanel which, when it is finished returns a JPanel containing the ScrollPane which contains the Panel(s) containing the BufferedImages. Which are then added to the JFrame.
public void processView(SecondaryProcessPanel secondary){
this.setLayout(new BoxLayout(this.getContentPane(),BoxLayout.Y_AXIS));
JScrollPane imageHolder = new JScrollPane();
//imageHolder.setLayout(new BoxLayout(imageHolder BoxLayout.Y_AXIS));
JScrollPane panelBack = secondary.getPanel(imageHolder);
panelBack.setPreferredSize(new Dimension(800,800));
this.add(panelBack, 0);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(600, 800);
this.pack();
}
The method getPanel goes through the list of BufferedImages. When it has finished the JPanel that this class is extended from is added to the JScrollPane and sent back to the processView method.
public JScrollPane getPanel(JScrollPane imageHolder) {
for (int j = 0; j< this.docList.size(); j++){
image = this.docList.get(j);
}
imageHolder = new JScrollPane(this);
return imageHolder;
}
#Override
public void paint(Graphics g) {
g.drawImage(image, 0,0, null);
}
What ever way around I put it all I can get back from a list of two images is the second image.
Any help greatly appreciated.
After your kind answers I have changed my code back to what I had originally!
public void processView(SecondaryProcessPanel secondary){
this.setLayout(new BoxLayout(this.getContentPane(),BoxLayout.Y_AXIS));
for (int i = 0; i < this.docList.size(); i++){
System.out.println("i = " + i);
JScrollPane imageHolder = new JScrollPane();
JScrollPane panelBack = secondary.getPanel(this.docList.get(i), imageHolder);
panelBack.setPreferredSize(new Dimension(800,800));
this.add(panelBack, i);
}
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(600, 800);
this.pack();
}
public JScrollPane getPanel(BufferedImage image, JScrollPane imageHolder){
this.image = image;
imageHolder = new JScrollPane(this);
return imageHolder;
}
#Override
public void paint(Graphics g){
g.drawImage(image, 0,0, null);
}
My problem remains that if the docList ArrayList contains more than one BufferedImage then I only get output from the second image.
The first thing that jumps out at me is the fact that you're violating the painting process...
#Override
public void paint(Graphics g) {
g.drawImage(image, 0,0, null);
}
First of all, assuming that you're extending from a JPanel, you should be overriding the paintComponent method instead, second of all, you should be calling the methods super method
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0,0, this);
}
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing.
Second is, WHY? JLabel can display an image just fine. Have a look at How to Use Labels for more details...
And finally...
public void processView(SecondaryProcessPanel secondary){
this.setLayout(new BoxLayout(this.getContentPane(),BoxLayout.Y_AXIS));
for (int i = 0; i < this.docList.size(); i++){
System.out.println("i = " + i);
JScrollPane imageHolder = new JScrollPane();
JScrollPane panelBack = secondary.getPanel(this.docList.get(i), imageHolder);
panelBack.setPreferredSize(new Dimension(800,800));
this.add(panelBack, i);
}
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(600, 800);
this.pack();
}
public JScrollPane getPanel(BufferedImage image, JScrollPane imageHolder){
this.image = image;
imageHolder = new JScrollPane(this);
return imageHolder;
}
This all just looks very suspicious, it look like you're trying to add the same instance of a component to multiple JScrollPane's, assigning a different image to the single instance each time...
So, this basically will remove the instance of the component from it's previous parent and then add it to the new container (JScrollPane) and supply a new instance of a BufferedImage for it to draw, basically ignoring every that came before it.
In the future, consider providing a runnable example which demonstrates your problem. This is not a code dump, but an example of what you are doing which highlights the problem you are having. This will result in less confusion and better responses
So, what you'll end up with is a lot of empty JScrollPanes, but with one which is displaying the last image in the list...
This takes the last image on the list - I dont understand what logic you are trying to implement but keeping the last image only is what it does - maybe you want to paint each image (you have a box layout add them there vertically) as it is being read
for (int j = 0; j< this.docList.size(); j++){
image = this.docList.get(j);
}
--
My suggestion is as follows
GridLayout gr=new GridLayout(0, 6);
gr.setRows(0); gr.setColumns(6);
JPanel cpanel=new JPanel();
cpanel.setLayout(gr);
for (int j = 0; j< this.docList.size(); j++){
image = this.docList.get(j);
JLabel jl=new JLabel(new ImageIcon(image));
cpanel.add(jl);
}
this.add(cpanel); // assuming 'this' is the scrollpane you want
and delete the paint method
I am currently making a pain program and have encountered into a problem when I am attempting to make g2D alpha-friendly. The problem is, that as soon as the paint method is refreshed, it draws on what the user drew, as well as GUI components that the user has hovered over. This is the code in the pain method:
public void paintComponent(Graphics comp)
{
Graphics2D board = (Graphics2D) comp;
AlphaComposite ac=AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f);
Composite oldComp=board.getComposite();
board.setComposite(ac); //used composite as a suggestion found on stackover flow;
//did not work.
for(int i = 0; i < al.size(); i++)
{
board.setColor(cl.get(i));
board.setStroke(new BasicStroke(tl.get(i),lineEnd.get(i),juncture.get(i)));
board.draw((Shape) al.get(i));
}
board.setComposite(oldComp);
}
Picture of what it looks like:
I have a feeling that the absolute position of the drawing board is in the left hand corner, so it draws it on as you update the paint method. I do not know how to solve that though. Here is my code for the setup of the drawing board if you need it:
public Container(String name, String text, String text2)
{
addMouseListener(mouseListener);
addMouseMotionListener(this);
setBorder(BorderFactory.createLineBorder(Color.black));
}
and in the main JFrame:
items[0].addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JTextField name = new JTextField();
JTextField width = new JTextField();
JTextField height = new JTextField();
final JComponent[] inputs = new JComponent[] { new JLabel("Creating new image: please fill in the required text fields."), new JLabel("Name: "), name, new JLabel("Width: "), width, new JLabel("Height: "), height};
name.addAncestorListener( new RequestFocusListener() );
JOptionPane.showMessageDialog(null, inputs, "New Image", JOptionPane.PLAIN_MESSAGE);
Container cont = new Container(name.getText(), width.getText(), height.getText());
addContainer(cont);
cont.setPreferredSize(new Dimension(Integer.parseInt(width.getText()),Integer.parseInt(height.getText())));
JScrollPane scroll = new JScrollPane();
JPanel pane = new JPanel();
pane.add(cont);
scroll.setViewportView(pane);
pane.setBackground(Color.lightGray);
tabbed.addTab(name.getText(), scroll);
setCursor("pen");
}
});
Thanks for your help!
EDIT:
Here are the array lists:
static ArrayList<Shape> al = new ArrayList<Shape>();
static ArrayList<Color> cl = new ArrayList<Color>();
static ArrayList<Integer> tl = new ArrayList<Integer>();
static ArrayList<Integer> lineEnd = new ArrayList<Integer>();
static ArrayList<Integer> juncture = new ArrayList<Integer>();
Basically, you've broken the paint chain.
When a paint cycle runs, a top level method is called (typically paint), which then calls a chain of events to perform the actual painting.
Each element in the chain does a particular job and builds on each other, failure to honour this chain will cause you problems.
Generally, the Graphics context for a window is a shared resource, meaning that during any given paint cycle, all the components that are painted share the same Graphics context
To fix the initial problem of " it draws on what the user drew, as well as GUI components that the user has hovered over", you need to call super.paintComponent, for example...
public void paintComponent(Graphics comp) {
super.paintComponent(comp);
Graphics2D board = (Graphics2D) comp;
This will clear the Graphics context from what was painted to it previous.
paintComponent should remain protected as the should no reason to allow anybody else to ever call it directly.
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
private static final long serialVersionUID = 1L;
String[] options = {"1","2","4","8","16","20","40","100","400"} ;
int[] optionsNum = {1,2,4,8,16,20,40,100,400};
JComboBox<String> box = new JComboBox<>(options);
JLabel prompt = new JLabel("How complex do you want the circle to be?");
ImageIcon image;
Circle p = new Circle(1);
int boxindex = 0;
public CircleDrawer(){
image = new ImageIcon(p.getImage());
box.setSelectedIndex(boxindex);
setLayout(new FlowLayout());
add(new JLabel(image));
add(prompt);
add(box);
pack();
setSize(851, 950);
setTitle("Circle Drawer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
box.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == box){
boxindex = box.getSelectedIndex();
p.setComplexity(optionsNum[boxindex]);
image = new ImageIcon(p.getImage());
add(new JLabel(image));
validate();
}
}
public static void main(String[] args) {
CircleDrawer f = new CircleDrawer();
f.setVisible(true);
}
Basically, I have this code. It references a class called Circle, which calculates some points on the outer edge of a circle and draws them using a paintComponent. In this class, there is a method called getImage, which takes what the paintComponent method drew and puts it into a BufferedImage.
public BufferedImage getImage() {
BufferedImage hello = new BufferedImage(805, 805, BufferedImage.TYPE_INT_ARGB);
Graphics g = hello.getGraphics();
paintComponent( g );
return hello;
}
Like so.
The problem I'm running into is that I can't find a way for it to completely re-draw the JFrame. I've tried clearing the JFrame within the actionPerformed method using removeAll() and then COMPLETELY setting up the frame all over again (add all of the components, pack, setSize, setTitle, etc) and then repaint, revalidate, or just validate it.
If I simply have it add the image and then validate it, I can see the image being updated, but it's just getting tacked on at the end of the JFrame (just like I would expect it to using a FlowLayout) but that's not the behavior I need. It just shows that it is sort of working.
My question is this: How do I make it re-draw the JFrame when the user changes options inside of the JComboBox?
Graphics g = hello.getGraphics();
paintComponent( g );
Don't use getGraphics() and never invoke paintComponent() directly. Swing will invoke the proper painting methods are required.
add(new JLabel(image));
validate();
When you add (remove) components from a visible GUI the general structure of the code is:
add(...);
revalidate(); // to invoke the layout manager
repaint(); to repaint the components
I have searched (I think) thoroughly for an answer to my problem. I'm a beginner so I may just not know what to look for. I am trying to make an overview of an office layout (tables, chairs), which I coded using Graphics2D and GeneralPath, and JLabels with names by each chair.
If this has already been answered I apologize but I did look.
(Note: the graphics are super simple for now: table is just a square and chairs are just lines.)
public class DemoReception extends JApplet{
#Override
public void paint(Graphics g){
//draws table
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(4.0f));
g2.setColor(Color.BLACK);
int[] xPoints={150,700,700,150};
int[] yPoints={250,250,550,550};
GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD,xPoints.length);
path.moveTo(xPoints[0], yPoints[0]);
for (int i = 0; i < xPoints.length; i++) {
path.lineTo(xPoints[i], yPoints[i]);
}
path.closePath();
g2.draw(path);
//draws chairs
g2.setColor(Color.RED);
path = new GeneralPath(GeneralPath.WIND_NON_ZERO);
path.moveTo(260,240);//Person1
path.lineTo(310,240);
path.moveTo(510,240);//Person2
path.lineTo(560,240);
path.moveTo(260,560);//Person3
path.lineTo(310,560);
path.moveTo(510,560);//Person4
path.lineTo(560,560);
path.closePath();
g2.draw(path);
}
And here is the main method:
public static void main(String[] args) {
int labelwidth = 50;
int labelheight = 10;
JFrame testFrame = new JFrame("Test Layout");
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JApplet demo = new DemoReception();
testFrame.setBackground(Color.white);
testFrame.getContentPane().add(demo);
testFrame.pack();
testFrame.setMinimumSize(new Dimension(1000,710));
testFrame.setSize(new Dimension(1000,710));
JPanel testPanel = new JPanel();
testPanel.setAlignmentX(0);
testPanel.setAlignmentY(0);
label1 = new JLabel("Person1");
label2 = new JLabel("Person2");
label3 = new JLabel("Person3");
label4 = new JLabel("Person4");
label1.setAlignmentX(260);
label1.setAlignmentY(235);
label1.setSize(labelwidth, labelheight);
label1.setVisible(true);
testPanel.add(label1);
label2.setAlignmentX(510);
label2.setAlignmentY(235);
label2.setSize(labelwidth, labelwidth);
label2.setVisible(true);
testPanel.add(label2);
label3.setAlignmentX(260);
label3.setAlignmentY(565);
label3.setSize(labelwidth, labelwidth);
label3.setVisible(true);
testPanel.add(label3);
label4.setAlignmentX(510);
label4.setAlignmentY(565);
label4.setSize(labelwidth, labelwidth);
label4.setVisible(true);
testPanel.add(label4);
testFrame.getContentPane().add(testPanel);
testFrame.setVisible(true);
}
When I run it all I get is the JFrame with the graphics but the JLabels don't show up.
Any help would be appreciated.
The JLabel does not appear in the JApplet as JFrame#pack is called before all labels have been added. The result is that those components are not validated so dont appear
The solution is to invoke the method before calling setVisible
testFrame.pack();
testFrame.setVisible(true);
However further changes are necessary as the applet window itself will not appear when this is done. This is because the statement
testFrame.getContentPane().add(testPanel);
will cause the JPanel testPanel to be displaced as implemented in the earlier statement
testFrame.getContentPane().add(demo);
BorderLayout can only contain one component at the CENTER location.
To fix, remove the testPanel and add the JLabel components directly to the JApplet demo instead.
Also add
super.paint(g);
to the paint method to ensure that the JLabels are painted by Swing.
Of course paint should never be used for custom painting in Swing. Rather use paintComponent
As a future exercise, make sure to replace the paint functionality by using a JComponent based class instead and overriding paintComponent. Remember to invoke super.paintComponent(g). Follow the steps outlined in Performing Custom Painting
I want to update the board configuration by clicking on a JButton. However, sometimes the image is displayed on the frame. sometimes is not. Everytime there is a significant delay after I click on the button. I tried debugging and found there might be a endless loop in :EventDispatchThread.class
(this is the class from java library)
void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
addEventFilter(filter);
doDispatch = true;
while (doDispatch && cond.evaluate()) {
if (isInterrupted() || !pumpOneEventForFilters(id)) {
doDispatch = false;
}
}
removeEventFilter(filter);
}
The endless loop is the while loop above.
Below is my listener class:
public class PlaceListener implements ActionListener{
private JTextField _text1;
private Board board;
private JTextField _text2;
private ArrayList<NewGUI> _guiList;
private int _numOfPlayer;
public PlaceListener(JTextField text1, JTextField text2, Board b,ArrayList<NewGUI> guiList, int numOfPlayer,NewGUI gui)
{
_text1 = text1;
_text2 = text2;
board = b;
_guiList = guiList;
_numOfPlayer = numOfPlayer;
}
#Override
public void actionPerformed(ActionEvent e) {
int x = Integer.parseInt(_text1.getText());
int y = Integer.parseInt(_text2.getText());
board.Place(y, x);
for(int j = 0;j<_numOfPlayer;j++)
{
NewGUI gui = _guiList.get(j);
gui.updateBoard();
gui.updateCurrTile();
gui.updateScore();
gui.updateTurn();
}
}
}
The basic idea is: I have an array of GUI. after each click, the listener will call all the GUIs within the array to update their configuration.
I also tried to update the board configuration within the GUI class directly, and it turns out to work well. I'm extremely confused! Can anyone help me out? Thanks!!
This is the main GUI class:
public class NewGUI {
private JFrame _frame;
private Board _board;
private JLabel _turnLabel;
private JTextArea _textArea;
private JLabel _currTileLabel;
private JPanel _boardPanel;
public NewGUI(Board board,int whos,ArrayList<NewGUI> guiList,int numOfPlayer)
{
_board = board;
_frame = new JFrame("Metro");
//turnLabel
_turnLabel = new JLabel();
_turnLabel.setText("Current player is: "+_board.getCurrPlayer());
_turnLabel.setSize(110, 40);
_turnLabel.setLocation(0, 0);
_frame.add(_turnLabel);
//mainPlayerLabel
JLabel mainPlayerLabel = new JLabel("Player"+whos+" 's window");
mainPlayerLabel.setSize(120, 20);
mainPlayerLabel.setLocation(400,0);
_frame.add(mainPlayerLabel);
//JTextArea to hold scores
_textArea = new JTextArea();
_textArea.setText(_board.displayScore());
_textArea.setSize(160,140);
_textArea.setLocation(730, 170);
_frame.add(_textArea);
_boardPanel = new JPanel();
_boardPanel.setSize(560, 560);
_boardPanel.setLocation(170, 80);
_boardPanel.setLayout(null);
// _boardPanel.setBackground(java.awt.Color.BLACK);
_frame.add(_boardPanel);
//Button Panel
JPanel buttonPanel = new JPanel();
buttonPanel.setSize(300, 150);
buttonPanel.setLocation(280, 650);
buttonPanel.setBackground(java.awt.Color.blue);
_frame.add(buttonPanel);
//Current Tile Label
_currTileLabel = new JLabel("Current Tile is: ");
_currTileLabel.setIcon(new ImageIcon(NewGUI.class.getResource(_board.getCurrTile().tileType()+".png")));
_currTileLabel.setSize(170, 60);
_currTileLabel.setLocation(20, 620);
_frame.add(_currTileLabel);
//2 input JTextField
JTextField text1 = new JTextField(3);
JTextField text2 = new JTextField(3);
text1.setSize(20, 20);
text2.setSize(20, 20);
text1.setLocation(620, 680);
text2.setLocation(640, 680);
_frame.add(text1);
_frame.add(text2);
//Buttons
JButton buttonPlace = new JButton("Place");
JButton buttonCommit = new JButton("Commit");
JButton buttonRemove = new JButton("Remove");
JButton buttonResign = new JButton("Resign");
buttonPlace.addActionListener(new PlaceListener(text1,text2,_board,guiList,numOfPlayer,this));
buttonCommit.addActionListener(new CommitListener(_board,guiList,numOfPlayer));
buttonRemove.addActionListener(new RemoveListener(_board,guiList,numOfPlayer,this));
buttonResign.addActionListener(new ResignListener(_board));
//Add buttons onto buttonPanel
buttonPanel.add(buttonCommit);
buttonPanel.add(buttonResign);
buttonPanel.add(buttonRemove);
buttonPanel.add(buttonPlace);
buttonPanel.setLayout(new FlowLayout());
_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_frame.setSize(900, 900);
_frame.setLayout(null);
_frame.setVisible(true);
}
public void updateBoard()
{
_boardPanel.removeAll();
//scan and refresh the board configuration.
for(int i = 1; i<13;i++)
{
for(int j = 1; j<13;j++)
{
if(_board.getBoard()[i][j]!=null)
{
for(int e = 65; e<89;e++){
char temp = (char)e;
if(_board.getBoard()[i][j].tileType()==temp)
{
JLabel label = new JLabel(new ImageIcon(NewGUI.class.getResource(temp+".png")));
label.setSize(40,40);
label.setLocation(40+(i-1)*40, 40+(j-1)*40);
_boardPanel.add(label);
break;
}
}
}
}
}
}
public void updateTurn()
{
_turnLabel.setText("Current player is: "+_board.getCurrPlayer());
}
public void updateScore()
{
_textArea.setText(_board.displayScore());
}
public void updateCurrTile()
{
_currTileLabel.setIcon(new ImageIcon(NewGUI.class.getResource(_board.getCurrTile().tileType()+".png")));
}
public static void main(String[] args)
{
Board b = new Board(3,true);
NewGUI gui = new NewGUI(b,1);
b.Place(4, 4);
b.Commit();
b.Place(12, 12);
b.Commit();
b.Place(3, 3);
gui.updateBoard();
}
}
See the last static main class? when I test it , all the update methods work well! But when I use listener to perform the method all. The updateBoard refuses to work.
So it looks like your code might be the problem here. Again EventDispatchThread uses an endless loop to work to pump events so that is obvious, and can be disregarded as the actual problem. You're problem comes from using removeAll(), and instantiating a few thousand labels every time they click the button (What's 13 x 13 x 89-65? 4056!). That's going to cause a lot of redrawing and relayout unnecessarily. So the pause you see is the performance of your code because it's not efficient. Don't believe try this:
public void updateBoard() {
long start = System.currentTimeInMillis();
// existing code goes here
long duration = System.currentTimeMillis() - start;
System.out.printf("UpdateBoard timing: %,d ms%n", duration );
}
If your code is anywhere over 10-100ms it will feel glitchy. In fact 100ms is on the slow side and a human can detect a delay of 100ms.
You probably need to re-evaluate your design, and either reuse the existing labels and simply call setImage() to change them. After all that's the whole point of using a persistent UI component model over using raw paint calls. Instantiate them once and reuse.
You are also creating thousands of images with new ImageIcon() calls too. You probably only need one icon and just have all labels point to the same image that will dramatically reduce your memory usage as well. In fact if you follow my advice I think you'll see dramatic speed and memory improvements.
If you can't find a suitable way to reuse JLabel then consider writing your own component by subclassing JComponent or JPanel (if you plan on using a container) and override paintComponent(). I see you're NOT using a LayoutManager and instead choosing to do everything using absolute positioning. If you are going to do absolute positioning you might want to just paint it yourself. Painting is more lower level interface, but you have full control. You'll have to handle positioning, word wrapping, all yourself. But, it will be very efficient, and you can redraw from a data model.
Since all your doing is draw images in a grid pattern I think drawing those with the Java2D api would be a better idea than instantiating that many JLabels and ImageIcons. If you subclass JPanel you can add JComponents to the panel for things like score, etc. But, draw the grid in the paintComponent() method.