/* * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package boofcv.gui; import boofcv.gui.image.ImagePanel; import boofcv.io.MediaManager; import boofcv.io.PathLabel; import boofcv.io.wrapper.DefaultMediaManager; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; /** * Provides pull a menubar for selecting the input source and which algorithm to use * * @author Peter Abeles */ public abstract class SelectAlgorithmAndInputPanel extends JPanel implements ActionListener, VisualizeApp { JToolBar toolbar; // each combo box is used to select different algorithms JComboBox algBoxes[]; // used to select the input image JComboBox imageBox; // when selected it shows the original image protected JCheckBox originalCheck; List<Object> algCookies[]; // list of input names and where to get the inputs protected List<PathLabel> inputRefs; protected String baseDirectory=""; // components which had been externally added List<JComponent> addedComponents = new ArrayList<>(); // what the original image was before any processing protected BufferedImage inputImage; // panel used for displaying the original image ImagePanel origPanel = new ImagePanel(); // the main GUI being displayed Component gui; // should it post algorithm change events yet? boolean postAlgorithmEvents = false; // abstract way of reading in media protected MediaManager media = DefaultMediaManager.INSTANCE; public SelectAlgorithmAndInputPanel(int numAlgFamilies) { super(new BorderLayout()); toolbar = new JToolBar(); imageBox = new JComboBox(); toolbar.add(imageBox); imageBox.addActionListener(this); imageBox.setMaximumSize(imageBox.getPreferredSize()); algBoxes = new JComboBox[numAlgFamilies]; algCookies = new List[numAlgFamilies]; for( int i = 0; i < numAlgFamilies; i++ ) { JComboBox b = algBoxes[i] = new JComboBox(); toolbar.add( b); b.addActionListener(this); b.setMaximumSize(b.getPreferredSize()); algCookies[i] = new ArrayList<>(); } toolbar.add(Box.createHorizontalGlue()); originalCheck = new JCheckBox("Show Input"); toolbar.add(originalCheck); originalCheck.addActionListener(this); originalCheck.setEnabled(false); add(toolbar, BorderLayout.PAGE_START); } /** * Loads a standardized file for input references * * @param fileName path to config file */ @Override public void loadInputData(String fileName) { Reader r = media.openFile(fileName); List<PathLabel> refs = new ArrayList<>(); try { BufferedReader reader = new BufferedReader(r); String line; while( (line = reader.readLine()) != null ) { String[]z = line.split(":"); String[] names = new String[z.length-1]; for( int i = 1; i < z.length; i++ ) { names[i-1] = baseDirectory+z[i]; } refs.add(new PathLabel(z[0],names)); } setInputList(refs); } catch (IOException e) { throw new RuntimeException(e); } } /** * Sets the directory that relative references are relative too */ public void setBaseDirectory(String baseDirectory) { this.baseDirectory = baseDirectory; } /** * Adds a new component into the toolbar. * * @param comp The component being added */ public void addToToolbar( JComponent comp ) { toolbar.add(comp,1+algBoxes.length); toolbar.revalidate(); addedComponents.add(comp); } public void removeFromToolbar( JComponent comp ) { toolbar.remove(comp); toolbar.revalidate(); addedComponents.remove(comp); } /** * Used to add the main GUI to this panel. Must use this function. * Algorithm change events will not be posted until this function has been set. * * @param gui The main GUI being displayed. */ public void setMainGUI( final Component gui ) { postAlgorithmEvents = true; this.gui = gui; SwingUtilities.invokeLater(new Runnable() { public void run() { add(gui,BorderLayout.CENTER); }}); } /** * Specifies an image which contains the original input image. After this has been called the * view input image widget is activated and when selected this image will be displayed instead * of the main GUI. This functionality is optional. * * @param image Original input image. */ public void setInputImage( BufferedImage image ) { inputImage = image; SwingUtilities.invokeLater(new Runnable() { public void run() { if( inputImage == null ) { originalCheck.setEnabled(false); } else { originalCheck.setEnabled(true); origPanel.setBufferedImage(inputImage); origPanel.setPreferredSize(new Dimension(inputImage.getWidth(),inputImage.getHeight())); origPanel.repaint(); } }}); } /** * Specifies a list of images to use as input and loads them * * @param inputRefs Name of input and where to get it */ public void setInputList(final List<PathLabel> inputRefs) { this.inputRefs = inputRefs; SwingUtilities.invokeLater(new Runnable() { public void run() { for( int i = 0; i < inputRefs.size(); i++ ) { imageBox.addItem(inputRefs.get(i).getLabel()); } }}); } public void addAlgorithm(final int indexFamily, final String name, Object cookie) { algCookies[indexFamily].add(cookie); SwingUtilities.invokeLater(new Runnable() { public void run() { algBoxes[indexFamily].addItem(name); }}); } /** * Grabs the currently selected algorithm, passes information to GUI for updating, toggles GUI * being active/not. refreshAll() is called in a new thread. * */ public void doRefreshAll() { SwingUtilities.invokeLater(new Runnable() { public void run() { // collect the current state inside the GUI thread final Object state[] = new Object[ algCookies.length ]; for( int i = 0; i < state.length; i++ ) { state[i] = algCookies[i].get(algBoxes[i].getSelectedIndex()); } // create a new thread to process this change new Thread() { public void run() { setActiveGUI(false); refreshAll(state); setActiveGUI(true); } }.start(); }}); } /** * Enables/disables the ability to interact with the algorithms GUI. */ private void setActiveGUI( final boolean isEnabled ) { SwingUtilities.invokeLater(new Runnable() { public void run() { toolbar.setEnabled(isEnabled); for( JComboBox b : algBoxes ) { b.setEnabled(isEnabled); } for( JComponent b : addedComponents ) { b.setEnabled(isEnabled); } imageBox.setEnabled(isEnabled); } }); } /** * Returns the cookie associated with the specified algorithm family. */ protected <T> T getAlgorithmCookie( int indexFamily ) { return (T)algCookies[indexFamily].get( algBoxes[indexFamily].getSelectedIndex() ); } @Override public void actionPerformed(ActionEvent e) { for( int i = 0; i < algBoxes.length; i++ ) { if( algBoxes[i] == e.getSource() ) { // see if its ready to start posting these events if( !postAlgorithmEvents ) return; // notify the main GUI to change the input algorithm final Object cookie = algCookies[i].get(algBoxes[i].getSelectedIndex()); final String name = (String)algBoxes[i].getSelectedItem(); final int indexFamily = i; new Thread() { public void run() { performSetAlgorithm(indexFamily,name, cookie); } }.start(); return; } } if( e.getSource() == imageBox ) { // notify the main GUI to change the input image final String name = (String)imageBox.getSelectedItem(); new Thread() { public void run() { performChangeInput(name, imageBox.getSelectedIndex()); } }.start(); } else if( e.getSource() == originalCheck ) { origPanel.setSize(gui.getWidth(),gui.getHeight()); // swap the main GUI with a picture of the original input image if( originalCheck.isSelected() ) { remove(gui); add(origPanel); } else { remove(origPanel); add(gui); } validate(); repaint(); } } private void performSetAlgorithm( int indexFamily , String name, Object cookie) { setActiveGUI(false); setActiveAlgorithm(indexFamily, name , cookie ); setActiveGUI(true); } private void performChangeInput(String name, int index) { setActiveGUI(false); changeInput(name, index); setActiveGUI(true); } @Override public void setMediaManager( MediaManager manager) { this.media = manager; } /** * Provides the current state of all selected algorithms. * * @param cookies state of each selected algorithm. */ public abstract void refreshAll( Object[] cookies ); /** * A request has been made to change the processing algorithm. NOT called from a GUI thread. * * @param indexFamily * @param name Display name of the algorithm. * @param cookie Reference to user defined data. */ public abstract void setActiveAlgorithm(int indexFamily, String name, Object cookie); /** * A request to change the input image has been made. The input image's label and its index in the * manager are returned. * * @param name Display name of the image. * @param index Which image in the list. */ public abstract void changeInput(String name, int index); }