Results 1 to 5 of 5
  1. #1
    manji51 is offline Member
    Join Date
    May 2011
    Posts
    8
    Rep Power
    0

    Question Multi-Panel multi-class GUI communication

    Hello everyone! I'm building a (at this point) simple GUI that uses three JPanels. I'm trying to be modular and using different classes for each of the JPanels and the JFrame. I have a button in one JPanel that needs to update text in a label in another JPanel, but I can't figure out how.

    I've done a lot of searching on Google and in my Murach's Java SE 6 book, but all the examples build the JPanels in the same class, thus they're able to just use variables for components in other classes directly.

    Am I just going in the wrong direction with pulling the nested JPanels out to their own classes? It definitely seems to be easy if I just built all of my panels within the DatabasePanel class, but that doesn't feel very modular.

    Below is what feels like the important bits of the code, in-line. I zipped up my Eclipse project (with a few data security-related snips) just in case someone wants to give it a more in-depth.

    DatabaseAccessApp.java
    Java Code:
    import presentation.DatabaseFrame;
    
    public class DatabaseAccessApp  {
    	public static void main (String args[]) {
    		DatabaseFrame frame = new DatabaseFrame();
    		frame.setVisible(true);
    	}
    }
    DatabaseFrame.java
    Java Code:
    package presentation;
    import javax.swing.*;
    import java.awt.*;
    
    @SuppressWarnings("serial")
    public class DatabaseFrame extends JFrame{	
    	private final int FRAME_WIDTH = 300;
    	private final int FRAME_HEIGHT = 300;
    	
    	public DatabaseFrame() {
    		setTitle("Dashboard");
    		setSize(FRAME_WIDTH, FRAME_HEIGHT);
    		centerFrame();
    		setDefaultCloseOperation(EXIT_ON_CLOSE);
    		
    		JPanel panel = new DatabasePanel();
    		this.add(panel);
    	}
    	
    	private void centerFrame() {
    		Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
    		Dimension screenDimensions = defaultToolkit.getScreenSize();
    		
    		int xPos = (screenDimensions.width - getWidth()) / 2;
    		int yPos = (screenDimensions.height - getHeight()) / 2;
    		
    		setLocation(xPos, yPos);
    	}
    }
    DatabasePanel.java
    Java Code:
    package presentation;
    
    import javax.swing.*;
    import java.awt.BorderLayout;
    
    
    @SuppressWarnings("serial")
    public class DatabasePanel extends JPanel {
    	JPanel headerPanel;
    	JPanel outputPanel;
    	JPanel buttonPanel;
    
    	JLabel output;
    	
    	public DatabasePanel() {
    		this.setLayout(new BorderLayout());
    		
    		headerPanel = new HeaderPanel();
    		outputPanel = new OutputPanel();
    		buttonPanel = new ButtonPanel();
    		
    		this.add(headerPanel, BorderLayout.NORTH);
    		this.add(outputPanel, BorderLayout.CENTER);
    		this.add(buttonPanel, BorderLayout.SOUTH);
    	}
    }
    Skipping HeaderPanel.java, as it doesn't apply to this question...

    OutputPanel.java
    Java Code:
    package presentation;
    
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    
    @SuppressWarnings("serial")
    class OutputPanel extends JPanel {
    	JLabel output;
    	
    	OutputPanel() {		
    		output = new JLabel();
    		add(output);
    	}
    }
    ButtonPanel.java
    Java Code:
    package presentation;
    
    import javax.swing.JPanel;
    import javax.swing.JButton;
    
    import business.QueryDatabase;
    
    import java.awt.FlowLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    @SuppressWarnings("serial")
    class ButtonPanel extends JPanel implements ActionListener {
    	JButton exit;
    	JButton query;
    	
    	ButtonPanel() {
    		this.setLayout(new FlowLayout(FlowLayout.RIGHT));
    		
    		query = new JButton("Run Query");
    		query.addActionListener(this);
    		this.add(query);
    		
    		exit = new JButton("Exit");
    		exit.addActionListener(this);
    		this.add(exit);
    	}
    
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		if (e.getSource() == exit) {
    			System.exit(0);
    		} else if (e.getSource() == query) {
    			//TODO: Cause query results to appear in output panel
    			
    			/*
    			String queryResults = QueryDatabase.query();
    			query.setText("Query Run");
    			query.setEnabled(false);
    			*/
    		}
    	}
    }
    Attached Files Attached Files

  2. #2
    manji51 is offline Member
    Join Date
    May 2011
    Posts
    8
    Rep Power
    0

    Default The Solution I found...

    Here's what I came up with.

    Create an interface for the top-level JPanel like Observer
    Java Code:
    public interface Observer {
    	void trigger(String content);
    }
    Then I implement Observer on DatabasePanel with method...
    Java Code:
    public void trigger(String content) {
    	outputPanel.setText(content);
    }
    Adding the mutator method to the OutputPanel, which is below pasting here...

    And after all that work, I caved and collapsed the sub-panels into the DatabasePanel so I could just work with it directly.

    I would love to see someone help me find an elegant way of using my multi-class arrangement. It felt like I was going the right way, but that I'm missing something technical to pull it together.

  3. #3
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    25

    Default

    A mutator method for the output panel would work great. Sinc you're just displaying text in a JLabel, a public setText(String text) method would work fine, but If this JPanel held a JTextArea that displayed the the text and you wanted to keep the old text while displaying the new, then you would also probably give it a public void append(String text) method to allow outside classes to append to the JTextArea without having to directly interact with the JTextArea.

    Then instead of having your GUI class implement an ActionListener, consider creating a Control class of some sort to handle key actions and to interact between classes. At some point we'll need to talk about avoiding having classes extend GUI components needlessly.

    For instance:

    Java Code:
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Dashboard {
       private static void createAndShowUI() {
          JFrame frame = new DatabaseFrame();
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          java.awt.EventQueue.invokeLater(new Runnable() {
             public void run() {
                createAndShowUI();
             }
          });
       }
    }
    
    // all these classes would go in their own file, but are kept together here for
    // the purposes of brevity's sake
    @SuppressWarnings("serial")
    class ButtonPanel extends JPanel {
       private JButton exit;
       private JButton query;
       private DatabaseControl control;
    
       ButtonPanel() {
          this.setLayout(new FlowLayout(FlowLayout.RIGHT));
    
          query = new JButton("Run Query");
          query.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent arg0) {
                if (control != null) {
                   control.query();
                }
             }
          });
          this.add(query);
    
          exit = new JButton("Exit");
          exit.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent arg0) {
                if (control != null) {
                   control.exit();
                }
             }
          });
          this.add(exit);
       }
    
       public void setControl(DatabaseControl control) {
          this.control = control;
       }
    
    }
    
    class DatabaseControl {
       private DatabasePanel databasePanel;
       private OutputPanel outputPanel;
       private int index = 0; // just added so you can see that action has an effect
       
       public DatabaseControl(DatabasePanel databasePanel) {
          this.databasePanel = databasePanel;
       }
    
       public void setOutputPanel(OutputPanel outputPanel) {
          this.outputPanel = outputPanel;
       }
    
       public void query() {
          if (outputPanel != null) {
             outputPanel.setText("Output number: " + index);
             index++;
          }
       }
    
       public void exit() {
          if (databasePanel != null) {
             databasePanel.exit();
          }
       }
    
    }
    
    @SuppressWarnings("serial")
    class DatabaseFrame extends JFrame { 
       private final int FRAME_WIDTH = 300;
       private final int FRAME_HEIGHT = 300;
    
       public DatabaseFrame() {
          setTitle("Dashboard");
          setPreferredSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT)); 
          setDefaultCloseOperation(EXIT_ON_CLOSE);
    
          JPanel panel = new DatabasePanel();
          this.add(panel);
       }
    }
    
    @SuppressWarnings("serial")
    class DatabasePanel extends JPanel {
       private DatabaseControl control = new DatabaseControl(this);
       private JPanel headerPanel;
       private OutputPanel outputPanel;
       private ButtonPanel buttonPanel;
    
       public DatabasePanel() {
          this.setLayout(new BorderLayout());
    
          headerPanel = new HeaderPanel();
          outputPanel = new OutputPanel();
          buttonPanel = new ButtonPanel();
          
          buttonPanel.setControl(control);
          control.setOutputPanel(outputPanel);
    
          this.add(headerPanel, BorderLayout.NORTH);
          this.add(outputPanel, BorderLayout.CENTER);
          this.add(buttonPanel, BorderLayout.SOUTH);
       }
       
       public void setControl(DatabaseControl control) {
          this.control = control;
       }
    
       public void exit() {
          Window win = SwingUtilities.getWindowAncestor(this);
          win.dispose();
       }
    }
    
    @SuppressWarnings("serial")
    class HeaderPanel extends JPanel {
       private JLabel description;
    
       HeaderPanel() {
          description = new JLabel("Searching for records and displaying");
          add(description);
       }
    }
    
    @SuppressWarnings("serial")
    class OutputPanel extends JPanel {
       private JLabel output;
    
       OutputPanel() {
          output = new JLabel();
          add(output);
       }
       
       // mutator method would work great
       public void setText(String text) {
          output.setText(text);
       }
    }

  4. #4
    manji51 is offline Member
    Join Date
    May 2011
    Posts
    8
    Rep Power
    0

    Default

    Thank you for your reply, Fubarable! I've actually spent the time between then and now thinking on the topic and repeatedly coming back to your reply code for thought. After bouncing around with a couple different thoughts which didn't pan out (as I tried to find something that gave the right amount of loose-coupling and required knowledge to fit the solution), I came to the below code (attached as well). Instead of using a controller class, it uses a Factory class (that I don't think is really a factory, now that I think of it) which is an attempt to abstract the knowledge into a central point and use it as a sort of object dictionary. Comments please? (Oh, I'm going to compress all classes/interface into a single code block, for brevity. And there's extra complexity because your poke that I didn't thread creation made me look into threading the query a little differently.) (Oh, and I went crazy with the keywords on that "Factory" class because I know that there are lots of people that say static classes are teh evil so I thought I'd go over the edge.)

    Java Code:
    public class Dashboard {
       private static void createAndShowUI() {
          JFrame frame = new DashboardFrame();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          java.awt.EventQueue.invokeLater(new Runnable() {
             public void run() {
                createAndShowUI();
             }
          });
       }
    }
    
    @SuppressWarnings("serial")
    public class DashboardFrame extends JFrame{	
    	private final int FRAME_WIDTH = 300;
    	private final int FRAME_HEIGHT = 300;
    	
    	public DashboardFrame() {
    		setTitle("Dashboard");
    		setSize(FRAME_WIDTH, FRAME_HEIGHT);
    		setLocationRelativeTo(null);
    		setDefaultCloseOperation(EXIT_ON_CLOSE);
    		
    		JPanel panel = new DashboardPanel();
    		this.add(panel);
    	}
    }
    
    @SuppressWarnings("serial")
    public class DashboardPanel extends JPanel {	
       public DashboardPanel() {
    	   PanelFactory.initialize(this);
    	   
    	   this.setLayout(new BorderLayout());
    	   add(PanelFactory.getHeaderPanel(), BorderLayout.NORTH);
    	   add(PanelFactory.getOutputPanel(), BorderLayout.CENTER);
    	   add(PanelFactory.getButtonPanel(), BorderLayout.SOUTH);
       }
    }
    
    public class PanelFactory {
    	private static JPanel parentPanel;
    	private static JPanel headerPanel;
    	private static Output outputPanel;
    	private static JPanel buttonPanel;
    	
    	public static synchronized final void initialize(JPanel parent) {
    		parentPanel = parent;
    		headerPanel = new HeaderPanel();
    		outputPanel = new OutputPanel();
    		buttonPanel = new ButtonPanel();
    	}
    	
    	public static synchronized final JPanel getParentPanel() {
    		return parentPanel;
    	}
    	
    	public static synchronized final JPanel getHeaderPanel() {
    		return headerPanel;
    	}
    	
    	public static synchronized final JPanel getOutputPanel() {
    		return (JPanel) outputPanel;
    	}
    	
    	public static synchronized final JLabel getOutputTarget() {
    		return outputPanel.getOutputTarget();
    	}
    	
    	public static synchronized final JPanel getButtonPanel() {
    		return buttonPanel;
    	}
    }
    
    @SuppressWarnings("serial")
    public class HeaderPanel extends JPanel {
    	JLabel descriptionLabel;
    	
    	public HeaderPanel() {
    		descriptionLabel = new JLabel("Searching for AuctionPay records and displaying");
    		add(descriptionLabel);
    	}
    }
    
    public interface Output {
    	JLabel getOutputTarget();
    }
    
    @SuppressWarnings("serial")
    public class OutputPanel extends JPanel implements Output {
    	private JLabel outputLabel;
    	
    	OutputPanel() {
    		outputLabel = new JLabel();
    		this.add(outputLabel);
    	}
    
    	@Override
    	public JLabel getOutputTarget() {
    		return outputLabel;
    	}
    }
    
    @SuppressWarnings("serial")
    public class ButtonPanel extends JPanel {
    	private JButton queryButton;
    	private JButton exitButton;
    	
    	ButtonPanel() {
    		setLayout(new FlowLayout(FlowLayout.RIGHT));
    		
    		queryButton = new JButton("Run Query");
    		queryButton.addActionListener(new QueryButtonListener(PanelFactory.getOutputTarget()));
    		add(queryButton);
    			
    		exitButton = new JButton("Exit");
    		exitButton.addActionListener(new ExitButtonListener());
    		add(exitButton);
    	}
    }
    
    class QueryButtonListener implements ActionListener {
    	private JButton button;
    	private JLabel outputTarget;
    	
    	QueryButtonListener(JLabel outputTarget) {
    		this.outputTarget = outputTarget;
    	}
    	
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		button = (JButton) e.getSource();
    		button.setText("Processing...");
    		(new OutputQuery()).execute();
    	}
    	
    	class OutputQuery extends SwingWorker<String, Object> {
    
    		@Override
    		protected String doInBackground() throws Exception {
    			return QueryDatabase.query();
    		}
    		
    		@Override
    		protected void done() {
    			try {
    				outputTarget.setText(get());
    				button.setText("Query Completed");
    				button.setEnabled(false);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    public class ExitButtonListener implements ActionListener {
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		JPanel parentPanel = PanelFactory.getParentPanel();
    		Window win = SwingUtilities.getWindowAncestor(parentPanel);
    		   win.dispose();
    	}
    
    }
    As an aside: I would love to have that discussion of which you suggested on how I'm needlessly subclassing Swing components! I consider myself a professional incompetent, and am always trying to rectify this status.
    Attached Files Attached Files

  5. #5
    manji51 is offline Member
    Join Date
    May 2011
    Posts
    8
    Rep Power
    0

    Default Why not a mutator

    You probably noticed that there's no mutator in my last post. The reason being that if I tried to call a setOutput() mutator method from the done() method of SwingWorker, a NullPointerException would be thrown.

Similar Threads

  1. how creat multi object for one class
    By ahmed80 in forum New To Java
    Replies: 0
    Last Post: 01-07-2011, 09:57 AM
  2. Jcombo/multi class help
    By skuzzie in forum New To Java
    Replies: 3
    Last Post: 11-21-2010, 11:46 PM
  3. Multi core
    By neeti in forum Threads and Synchronization
    Replies: 2
    Last Post: 10-22-2009, 03:56 AM
  4. How come multi thread don't look like it?
    By jkhoa in forum Threads and Synchronization
    Replies: 1
    Last Post: 09-22-2007, 04:25 AM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •