diff --git a/src/activeSegmentation/gui/CreateOpenProjectGUI.java b/src/activeSegmentation/gui/CreateOpenProjectGUI.java index 4e90cfe6..a23ed9f4 100644 --- a/src/activeSegmentation/gui/CreateOpenProjectGUI.java +++ b/src/activeSegmentation/gui/CreateOpenProjectGUI.java @@ -62,12 +62,11 @@ public CreateOpenProjectGUI(ProjectManager projectManager) { @Override public void run() { - mainFrame = new JFrame(); + mainFrame = new JFrame("Active Segmentation"); mainFrame.setIconImage(Toolkit.getDefaultToolkit().getImage(CreateOpenProjectGUI.class.getResource("logo.png"))); mainFrame.getContentPane().setBackground(Color.GRAY); - mainFrame.setSize(frameWidth, frameHeight); + mainFrame.setMinimumSize(new Dimension(600, 520)); mainFrame.setLocationRelativeTo(null); - mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // Prevent the default close operation // Confirm Exit to intercept the window close event @@ -131,16 +130,18 @@ private void doAction( final ActionEvent event ){ //fileChooser.setAcceptAllFileFilterUsed(false); fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("JSON", "json" )); - + // Remember last-used directory + if (currentDir != null) { + fileChooser.setCurrentDirectory(currentDir.getParentFile()); + } + int rVal = fileChooser.showOpenDialog(null); - if (currentDir!=null) - fileChooser.setSelectedFile(currentDir); if (rVal == JFileChooser.APPROVE_OPTION) { currentDir = fileChooser.getSelectedFile(); String file = currentDir.toString(); if (projectManager.loadProject(file)) { - System.out.println(" GuiPanel "); +// System.out.println(" GuiPanel "); // Updates mainFrame by replacing its content with the main panel of a new GuiPanel instance GuiPanel guiPanel = new GuiPanel(projectManager); @@ -338,14 +339,15 @@ private JPanel createProjectPanel() { trainingImageP.setColumns(200); trainingImageP.setBounds(200, 270, 250, 30); panel.add(trainingImageP); - panel.add(addButton("Folder", null, 240, 310, 100, 30, TRAININGF_BUTTON_PRESSED)); - panel.add(addButton("Image Stack", null, 350, 310, 100, 30, Tiff_BUTTON_PRESSED)); + panel.add(addButton("Folder", null, 200, 310, 120, 35, TRAININGF_BUTTON_PRESSED)); + panel.add(addButton("Image Stack", null, 330, 310, 120, 35, Tiff_BUTTON_PRESSED)); + } - panel.add(addButton("Finish", null, 80, 360, 120, 30, FINISH_BUTTON_PRESSED)); - panel.add(addButton("Cancel", null, 240, 360, 100, 30, CANCEL_BUTTON_PRESSED)); - panel.add(addButton("Back", null, 350, 360, 100, 30, BACK_BUTTON_PRESSED)); - panel.add(addButton("Exit", null, 460, 360, 100, 30, EXIT_BUTTON_PRESSED)); + panel.add(addButton("Finish", null, 200, 375, 120, 35, FINISH_BUTTON_PRESSED)); + panel.add(addButton("Cancel", null, 330, 375, 120, 35, CANCEL_BUTTON_PRESSED)); + panel.add(addButton("Back", null, 200, 420, 120, 35, BACK_BUTTON_PRESSED)); + panel.add(addButton("Exit", null, 330, 420, 120, 35, EXIT_BUTTON_PRESSED)); return panel; } diff --git a/src/activeSegmentation/gui/LearningPanel.java b/src/activeSegmentation/gui/LearningPanel.java index 7fb6939a..31da975a 100644 --- a/src/activeSegmentation/gui/LearningPanel.java +++ b/src/activeSegmentation/gui/LearningPanel.java @@ -1,347 +1,329 @@ -package activeSegmentation.gui; - - -import java.awt.Color; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.util.ArrayList; -//import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeMap; - -import javax.swing.BorderFactory; -import javax.swing.ButtonGroup; -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.ImageIcon; - -import activeSegmentation.ASCommon; -import activeSegmentation.learning.ClassifierManager; -import activeSegmentation.learning.weka.WekaClassifier; -import weka.classifiers.AbstractClassifier; -import weka.classifiers.Classifier; -import weka.core.OptionHandler; -import weka.core.Utils; -import weka.gui.GenericObjectEditor; -import weka.gui.PropertyPanel; - -import activeSegmentation.IClassifier; -import activeSegmentation.IDataSet; -import activeSegmentation.IFeatureSelection; -import activeSegmentation.prj.LearningInfo; -import activeSegmentation.prj.ProjectInfo; -import activeSegmentation.prj.ProjectManager; -//import activeSegmentation.util.GuiUtil; -//import ijaux.Util; - - -/** - * This is a Weka-specific panel, so it is OK to expose Weka classes. - * @author Sumit Vohra, Dimiter Prodanov - * - */ -public class LearningPanel extends JFrame implements Runnable, ASCommon { - - //platform-level variables - private ProjectManager projectManager; - private ProjectInfo projectInfo; - private JList featureSelList; - - //class-specific variables - private String defaultOptions=""; - private String defaultClassifierName=""; - private boolean hasChanged=false; - - //UI variables -// private final JFrame frame = new JFrame("Learning"); - private final ActionEvent LOAD_BUTTON_PRESSED = new ActionEvent(this, 1, "Load"); - private final ActionEvent SAVE_BUTTON_PRESSED = new ActionEvent(this, 2, "Save"); - private ClassifierManager learningManager; - - //Weka-specific variables - private AbstractClassifier aclass=null; - private GenericObjectEditor wekaClassifierEditor ; - - /** - * - * @param projectManager - * @param learningManager - */ - public LearningPanel(ProjectManager projectManager, ClassifierManager learningManager ) { - this.projectManager = projectManager; - this.learningManager=learningManager; - this.projectInfo = projectManager.getMetaInfo(); - try { - this.wekaClassifierEditor = new GenericObjectEditor(); - }catch (Exception ex ) { - ex.printStackTrace(); - } - DefaultListModel model = new DefaultListModel<>(); - featureSelectionUI(model); - featureSelList = new JList<>(model); - } - - /** - * - * @param event - */ - public void doAction(ActionEvent event) { - if (event == SAVE_BUTTON_PRESSED) { - updateClassifier(); - if (aclass!=null ) { - IClassifier classifier = new WekaClassifier(aclass); - - learningManager.setClassifier(classifier); - learningManager.saveLearningMetaData(); - projectManager.updateMetaInfo(projectInfo); - - // to avoid data creep because we are changing the learning method. - IDataSet data = projectManager.getDataSet(); - if (data!=null) - data.delete(); - } - - } // end SAVE - if (event == LOAD_BUTTON_PRESSED) { - LearningInfo li=learningManager.getLearningMetaData(); - String[] options= li.getOptionsArray(); - - String cname=li.getClassifierName(); - System.out.println("loading "+cname); - //GuiUtil.printStringArray(options); - try { - if (cname!="") { - // TODO change into loadClass from TestLoadClass - aclass = (AbstractClassifier) Class.forName(cname).newInstance(); - //cls.setOptions(options); - IClassifier classifier = new WekaClassifier(aclass); - learningManager.setClassifier(classifier); - wekaClassifierEditor.setClassType(Classifier.class); - Object obj =learningManager.getClassifier(); - System.out.println(obj); - - aclass.setOptions(options); - wekaClassifierEditor.setValue(aclass); - - defaultOptions = Utils.joinOptions(options); - System.out.println(defaultOptions); - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } // end LOAD - } - - - - @Override - public void run() { - showPanel(); - } - - /** - * - */ - private void showPanel() { - setTitle("Learning"); - setIconImage(Toolkit.getDefaultToolkit().getImage(LearningPanel.class.getResource("logo.png"))); - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - getContentPane().setBackground(Color.GRAY); - setLocationRelativeTo(null); - setSize(600, 300); - - - final int xOffsetCol1=10; - - JPanel aPanel = new JPanel(); - aPanel.setLayout(null); - aPanel.setBackground(Color.GRAY); - - JPanel learningJPanel = new JPanel(); - learningJPanel.setBorder(BorderFactory.createTitledBorder("Learning")); - - PropertyPanel wekaCEPanel = new PropertyPanel(wekaClassifierEditor); - wekaClassifierEditor.setClassType(Classifier.class); - wekaClassifierEditor.setValue(learningManager.getClassifier()); - Object c = wekaClassifierEditor.getValue(); - defaultOptions = ""; - defaultClassifierName = c.getClass().getName(); - - if ((c instanceof OptionHandler)) { - defaultOptions = Utils.joinOptions(((OptionHandler)c).getOptions()); - } - - wekaCEPanel.setBounds(30, 30, 250, 30); - learningJPanel.add(wekaCEPanel); - learningJPanel.setBounds(xOffsetCol1, 20, 300, 150); - - ////////////////////////////////Will be enabled in the future - - JPanel options = new JPanel(); - - options.setBorder(BorderFactory.createTitledBorder("Learning Options")); - options.setBounds(xOffsetCol1, 120, 300, 120); - - - JRadioButton pasiveLearning = new JRadioButton ("Passive Learning" ); - JRadioButton activeLearning = new JRadioButton ("Active Learning" ); - ButtonGroup bg=new ButtonGroup(); - bg.add(pasiveLearning); - bg.add(activeLearning); - options.add(pasiveLearning); - options.add(activeLearning); - pasiveLearning.setSelected(true); - - pasiveLearning.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - projectInfo.getLearning().setFeatureSelection(PASSIVELEARNING); - hasChanged=true; - updateClassifier(); - } - }); - - activeLearning.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - projectInfo.getLearning().setFeatureSelection(ACTIVELEARNING); - hasChanged=true; - updateClassifier(); - } - }); - - final int xOffsetCol2=370; - - ///////////////////////////// - JPanel featurePanel = new JPanel(); - featurePanel.setBorder(BorderFactory.createTitledBorder("Feature Selection")); - featurePanel.setBounds(xOffsetCol2, 20, 200, 150); - - - JScrollPane classScrolPanel = new JScrollPane(featurePanel); - classScrolPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); - - featureSelList.setBackground(Color.WHITE); - featureSelList.setSelectedIndex(0); - featureSelList.addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent evt) { - - String fv=""; - //System.out.println("Learning: Feature selection: " + fv); - TreeMap hm=learningManager.getFeatureSelMap(); - - int ind=featureSelList.getSelectedIndex(); - //System.out.println(ind); - - Iterator> iter=hm.entrySet().iterator(); - List> result = new ArrayList<>(); - while (iter.hasNext()){ - result.add(iter.next()); - } - Entry ee=result.get(ind); - fv=ee.getKey(); - System.out.println("Learning: Feature selection: " + fv); - projectInfo.getLearning().setLearningOption(fv); - - hasChanged=true; - updateClassifier(); - - } - }); - - //classScrolPanel.add(featureSelList); - featurePanel.add(featureSelList); - learningJPanel.add(classScrolPanel); - - //////////////////////////// - JPanel IOpanel = new JPanel(); - IOpanel.setBackground(Color.GRAY); - IOpanel.setBounds(xOffsetCol2, 200, 200, 80); - - IOpanel.add(addButton("Save", null, xOffsetCol2, 120, 200, 50, SAVE_BUTTON_PRESSED)); - - IOpanel.add(addButton("Load", null, xOffsetCol2+200+100, 200, 450, 50, LOAD_BUTTON_PRESSED)); - - aPanel.add(learningJPanel); - aPanel.add(featurePanel); - aPanel.add(IOpanel); - // aPanel.add(options); - - add(aPanel); - setVisible(true); - setResizable(false); - } - - /** - * @param model - */ - private void featureSelectionUI(DefaultListModel model) { - //model.addElement("NONE"); - TreeMap compset=learningManager.getFeatureSelMap(); - Set set=compset.keySet(); - for (String s:set) { - final String s2=compset.get(s).getName(); - model.addElement(s2); - } - } - - /** - * - * @return - */ - private void updateClassifier() { - System.out.println("Learning panel: in updateClassifier"); - Object c = wekaClassifierEditor.getValue(); - //String options = "updateClassifier: default options "; - String[] optionsArray = ((OptionHandler)c).getOptions(); - System.out.println(""+defaultOptions); - - try { - // TODO change into loadClass from TestLoadClass - final AbstractClassifier cls = (AbstractClassifier)c.getClass().newInstance(); - cls.setOptions(optionsArray); - - final LearningInfo li=projectInfo.getLearning(); - li.setClassifier(cls); - li.updateOptionList(); - hasChanged=true; - aclass= cls; - } catch (Exception ex) { - ex.printStackTrace(); - } - - } - - private JButton addButton(String label, ImageIcon icon, int x, int y, int width, int height, final ActionEvent action) { - JButton button = new JButton(label, icon); - button.setFont(labelFONT); - button.setBorderPainted(false); - button.setFocusPainted(false); - button.setBackground(buttonBGColor); - button.setForeground(buttonColor); - button.setBounds(x, y, width, height); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doAction(action); - } - }); - return button; - } -} +package activeSegmentation.gui; + + +import java.awt.Color; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.ImageIcon; + +import activeSegmentation.ASCommon; +import activeSegmentation.learning.ClassifierManager; +import activeSegmentation.learning.weka.WekaClassifier; +import weka.classifiers.AbstractClassifier; +import weka.classifiers.Classifier; +import weka.core.OptionHandler; +import weka.core.Utils; +import weka.gui.GenericObjectEditor; +import weka.gui.PropertyPanel; + +import activeSegmentation.IClassifier; +import activeSegmentation.IDataSet; +import activeSegmentation.IFeatureSelection; +import activeSegmentation.prj.LearningInfo; +import activeSegmentation.prj.ProjectInfo; +import activeSegmentation.prj.ProjectManager; + +/** + * Panel for configuring the machine learning classifier used during + * the Active Segmentation training pipeline. + * + *

This is a Weka-specific panel; it is intentional that Weka classes + * are exposed at this level.

+ * + * @author Sumit Vohra, Dimiter Prodanov + */ +public class LearningPanel extends JFrame implements Runnable, ASCommon { + + // Platform-level variables + private ProjectManager projectManager; + private ProjectInfo projectInfo; + private JList featureSelList; + + // Class-specific variables + private String defaultOptions = ""; + private String defaultClassifierName = ""; + private boolean hasChanged = false; + + private final ActionEvent LOAD_BUTTON_PRESSED = new ActionEvent(this, 1, "Load"); + private final ActionEvent SAVE_BUTTON_PRESSED = new ActionEvent(this, 2, "Save"); + private ClassifierManager learningManager; + + // Weka-specific variables + private AbstractClassifier aclass = null; + private GenericObjectEditor wekaClassifierEditor; + + /** + * Constructs a {@code LearningPanel} and initialises the Weka classifier + * editor and feature selection list. + * + * @param projectManager the active {@link ProjectManager} instance + * @param learningManager the {@link ClassifierManager} managing classifiers + */ + public LearningPanel(ProjectManager projectManager, ClassifierManager learningManager) { + this.projectManager = projectManager; + this.learningManager = learningManager; + this.projectInfo = projectManager.getMetaInfo(); + try { + this.wekaClassifierEditor = new GenericObjectEditor(); + } catch (Exception ex) { + ex.printStackTrace(); + } + DefaultListModel model = new DefaultListModel<>(); + featureSelectionUI(model); + featureSelList = new JList<>(model); + } + + /** + * Handles button-press actions for Save and Load operations. + * + *

Note: {@link ActionEvent#equals(Object)} is used for comparison + * instead of reference equality ({@code ==}) to ensure correct dispatch + * regardless of how the event object was constructed.

+ * + * @param event the {@link ActionEvent} to dispatch + */ + public void doAction(ActionEvent event) { + if (event.equals(SAVE_BUTTON_PRESSED)) { + updateClassifier(); + if (aclass != null) { + IClassifier classifier = new WekaClassifier(aclass); + learningManager.setClassifier(classifier); + learningManager.saveLearningMetaData(); + projectManager.updateMetaInfo(projectInfo); + + // Avoid data creep when changing the learning method + IDataSet data = projectManager.getDataSet(); + if (data != null) + data.delete(); + } + } // end SAVE + + if (event.equals(LOAD_BUTTON_PRESSED)) { + LearningInfo li = learningManager.getLearningMetaData(); + String[] options = li.getOptionsArray(); + String cname = li.getClassifierName(); + + try { + if (!cname.isEmpty()) { + aclass = (AbstractClassifier) Class.forName(cname).newInstance(); + IClassifier classifier = new WekaClassifier(aclass); + learningManager.setClassifier(classifier); + wekaClassifierEditor.setClassType(Classifier.class); + wekaClassifierEditor.setValue(aclass); + aclass.setOptions(options); + defaultOptions = Utils.joinOptions(options); + } + } catch (Exception e) { + e.printStackTrace(); + } + } // end LOAD + } + + @Override + public void run() { + showPanel(); + } + + /** + * Builds and displays the learning configuration panel. + */ + private void showPanel() { + setTitle("Learning"); + setIconImage(Toolkit.getDefaultToolkit().getImage( + LearningPanel.class.getResource("logo.png"))); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + getContentPane().setBackground(Color.GRAY); + setLocationRelativeTo(null); + setSize(600, 300); + + final int xOffsetCol1 = 10; + + JPanel aPanel = new JPanel(); + aPanel.setLayout(null); + aPanel.setBackground(Color.GRAY); + + JPanel learningJPanel = new JPanel(); + learningJPanel.setBorder(BorderFactory.createTitledBorder("Learning")); + + PropertyPanel wekaCEPanel = new PropertyPanel(wekaClassifierEditor); + wekaClassifierEditor.setClassType(Classifier.class); + wekaClassifierEditor.setValue(learningManager.getClassifier()); + Object c = wekaClassifierEditor.getValue(); + defaultOptions = ""; + defaultClassifierName = c.getClass().getName(); + + if (c instanceof OptionHandler) { + defaultOptions = Utils.joinOptions(((OptionHandler) c).getOptions()); + } + + wekaCEPanel.setBounds(30, 30, 250, 30); + learningJPanel.add(wekaCEPanel); + learningJPanel.setBounds(xOffsetCol1, 20, 300, 150); + + JPanel options = new JPanel(); + options.setBorder(BorderFactory.createTitledBorder("Learning Options")); + options.setBounds(xOffsetCol1, 120, 300, 120); + + JRadioButton pasiveLearning = new JRadioButton("Passive Learning"); + JRadioButton activeLearning = new JRadioButton("Active Learning"); + ButtonGroup bg = new ButtonGroup(); + bg.add(pasiveLearning); + bg.add(activeLearning); + options.add(pasiveLearning); + options.add(activeLearning); + pasiveLearning.setSelected(true); + + pasiveLearning.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + projectInfo.getLearning().setFeatureSelection(PASSIVELEARNING); + hasChanged = true; + updateClassifier(); + } + }); + + activeLearning.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + projectInfo.getLearning().setFeatureSelection(ACTIVELEARNING); + hasChanged = true; + updateClassifier(); + } + }); + + final int xOffsetCol2 = 370; + + JPanel featurePanel = new JPanel(); + featurePanel.setBorder(BorderFactory.createTitledBorder("Feature Selection")); + featurePanel.setBounds(xOffsetCol2, 20, 200, 150); + + JScrollPane classScrolPanel = new JScrollPane(featurePanel); + classScrolPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + + featureSelList.setBackground(Color.WHITE); + featureSelList.setSelectedIndex(0); + featureSelList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent evt) { + TreeMap hm = learningManager.getFeatureSelMap(); + int ind = featureSelList.getSelectedIndex(); + + Iterator> iter = hm.entrySet().iterator(); + List> result = new ArrayList<>(); + while (iter.hasNext()) { + result.add(iter.next()); + } + Entry ee = result.get(ind); + String fv = ee.getKey(); + projectInfo.getLearning().setLearningOption(fv); + hasChanged = true; + updateClassifier(); + } + }); + + featurePanel.add(featureSelList); + learningJPanel.add(classScrolPanel); + + JPanel IOpanel = new JPanel(); + IOpanel.setBackground(Color.GRAY); + IOpanel.setBounds(xOffsetCol2, 200, 200, 80); + + IOpanel.add(addButton("Save", null, xOffsetCol2, 120, 200, 50, SAVE_BUTTON_PRESSED)); + IOpanel.add(addButton("Load", null, xOffsetCol2 + 200 + 100, 200, 450, 50, LOAD_BUTTON_PRESSED)); + + aPanel.add(learningJPanel); + aPanel.add(featurePanel); + aPanel.add(IOpanel); + + add(aPanel); + setVisible(true); + setResizable(false); + } + + /** + * Populates the feature selection list from the classifier manager. + * + * @param model the {@link DefaultListModel} to populate + */ + private void featureSelectionUI(DefaultListModel model) { + TreeMap compset = learningManager.getFeatureSelMap(); + Set set = compset.keySet(); + for (String s : set) { + final String s2 = compset.get(s).getName(); + model.addElement(s2); + } + } + + /** + * Reads the current classifier configuration from the Weka editor and + * updates the project's {@link LearningInfo} accordingly. + */ + private void updateClassifier() { + Object c = wekaClassifierEditor.getValue(); + String[] optionsArray = ((OptionHandler) c).getOptions(); + + try { + final AbstractClassifier cls = (AbstractClassifier) c.getClass().newInstance(); + cls.setOptions(optionsArray); + + final LearningInfo li = projectInfo.getLearning(); + li.setClassifier(cls); + li.updateOptionList(); + hasChanged = true; + aclass = cls; + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Creates and returns a styled {@link JButton} with the given properties. + * + * @param label button text + * @param icon optional button icon + * @param x x position (absolute layout) + * @param y y position (absolute layout) + * @param width button width + * @param height button height + * @param action the {@link ActionEvent} to dispatch on click + * @return a configured {@link JButton} + */ + private JButton addButton(String label, ImageIcon icon, int x, int y, + int width, int height, final ActionEvent action) { + JButton button = new JButton(label, icon); + button.setFont(labelFONT); + button.setBorderPainted(false); + button.setFocusPainted(false); + button.setBackground(buttonBGColor); + button.setForeground(buttonColor); + button.setBounds(x, y, width, height); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doAction(action); + } + }); + return button; + } +}