org.optaplanner.examples.common.swingui.SolverAndPersistenceFrame Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of optaplanner-examples Show documentation
Show all versions of optaplanner-examples Show documentation
OptaPlanner solves planning problems.
This lightweight, embeddable planning engine implements powerful and scalable algorithms
to optimize business resource scheduling and planning.
This module contains the examples which demonstrate how to use it in a normal Java application.
/*
* Copyright 2010 JBoss Inc
*
* 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 org.optaplanner.examples.common.swingui;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileFilter;
import org.apache.commons.io.FilenameUtils;
import org.optaplanner.core.api.score.FeasibilityScore;
import org.optaplanner.core.api.score.Score;
import org.optaplanner.core.impl.solution.Solution;
import org.optaplanner.examples.common.business.SolutionBusiness;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SolverAndPersistenceFrame extends JFrame {
protected final transient Logger logger = LoggerFactory.getLogger(getClass());
public static final ImageIcon OPTA_PLANNER_ICON = new ImageIcon(
SolverAndPersistenceFrame.class.getResource("optaPlannerIcon.png"));
private final String titlePrefix;
private final SolutionBusiness solutionBusiness;
private SolutionPanel solutionPanel;
private ConstraintMatchesDialog constraintMatchesDialog;
private JPanel quickOpenUnsolvedPanel;
private List quickOpenUnsolvedActionList;
private JPanel quickOpenSolvedPanel;
private List quickOpenSolvedActionList;
private Action openAction;
private Action saveAction;
private Action importAction;
private Action exportAction;
private JCheckBox refreshScreenDuringSolvingCheckBox;
private Action solveAction;
private JButton solveButton;
private Action terminateSolvingEarlyAction;
private JButton terminateSolvingEarlyButton;
private JPanel middlePanel;
private JProgressBar progressBar;
private JTextField scoreField;
private ShowConstraintMatchesDialogAction showConstraintMatchesDialogAction;
public SolverAndPersistenceFrame(SolutionBusiness solutionBusiness, SolutionPanel solutionPanel,
String titlePrefix) {
super(titlePrefix);
this.titlePrefix = titlePrefix;
this.solutionBusiness = solutionBusiness;
this.solutionPanel = solutionPanel;
setIconImage(OPTA_PLANNER_ICON.getImage());
solutionPanel.setSolutionBusiness(solutionBusiness);
solutionPanel.setSolverAndPersistenceFrame(this);
registerListeners();
constraintMatchesDialog = new ConstraintMatchesDialog(this, solutionBusiness);
}
private void registerListeners() {
solutionBusiness.registerForBestSolutionChanges(this);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
// This async, so it doesn't stop the solving immediately
solutionBusiness.terminateSolvingEarly();
}
});
}
public void bestSolutionChanged() {
Solution solution = solutionBusiness.getSolution();
if (refreshScreenDuringSolvingCheckBox.isSelected()) {
solutionPanel.updatePanel(solution);
validate(); // TODO remove me?
}
scoreField.setForeground(determineScoreFieldForeground(solutionBusiness.getScore()));
scoreField.setText("Latest best score: " + solution.getScore());
}
public void init(Component centerForComponent) {
setContentPane(createContentPane());
pack();
setLocationRelativeTo(centerForComponent);
}
private JComponent createContentPane() {
JComponent quickOpenPanel = createQuickOpenPanel();
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(createToolBar(), BorderLayout.NORTH);
mainPanel.add(createMiddlePanel(), BorderLayout.CENTER);
mainPanel.add(createScorePanel(), BorderLayout.SOUTH);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, quickOpenPanel, mainPanel);
splitPane.setOneTouchExpandable(true);
splitPane.setResizeWeight(0.2);
return splitPane;
}
private JComponent createQuickOpenPanel() {
JSplitPane quickOpenSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
createQuickOpenUnsolvedPanel(), createQuickOpenSolvedPanel());
quickOpenSplitPane.setResizeWeight(0.8);
return quickOpenSplitPane;
}
private JComponent createQuickOpenUnsolvedPanel() {
quickOpenUnsolvedPanel = new JPanel();
quickOpenUnsolvedActionList = new ArrayList();
List unsolvedFileList = solutionBusiness.getUnsolvedFileList();
return createQuickOpenPanel(quickOpenUnsolvedPanel, "Quick open (unsolved)", quickOpenUnsolvedActionList,
unsolvedFileList);
}
private JComponent createQuickOpenSolvedPanel() {
quickOpenSolvedPanel = new JPanel();
quickOpenSolvedActionList = new ArrayList();
List solvedFileList = solutionBusiness.getSolvedFileList();
return createQuickOpenPanel(quickOpenSolvedPanel, "Quick open (solved)", quickOpenSolvedActionList,
solvedFileList);
}
private JComponent createQuickOpenPanel(JPanel panel, String title, List quickOpenActionList, List fileList) {
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
refreshQuickOpenPanel(panel, quickOpenActionList, fileList);
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.getVerticalScrollBar().setUnitIncrement(25);
scrollPane.setMinimumSize(new Dimension(100, 80));
// Size fits into screen resolution 1024*768
scrollPane.setPreferredSize(new Dimension(180, 200));
JPanel titlePanel = new JPanel(new BorderLayout());
titlePanel.add(scrollPane, BorderLayout.CENTER);
titlePanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(2, 2, 2, 2), BorderFactory.createTitledBorder(title)));
return titlePanel;
}
private void refreshQuickOpenPanel(JPanel panel, List quickOpenActionList, List fileList) {
panel.removeAll();
quickOpenActionList.clear();
if (fileList.isEmpty()) {
JLabel noneLabel = new JLabel("None");
noneLabel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
panel.add(noneLabel);
} else {
for (File file : fileList) {
Action quickOpenAction = new QuickOpenAction(file);
quickOpenActionList.add(quickOpenAction);
JButton quickOpenButton = new JButton(quickOpenAction);
quickOpenButton.setHorizontalAlignment(SwingConstants.LEFT);
quickOpenButton.setMargin(new Insets(0, 0, 0, 0));
panel.add(quickOpenButton);
}
}
}
private class QuickOpenAction extends AbstractAction {
private File file;
public QuickOpenAction(File file) {
super(file.getName());
this.file = file;
}
public void actionPerformed(ActionEvent e) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
solutionBusiness.openSolution(file);
setSolutionLoaded();
} finally {
setCursor(Cursor.getDefaultCursor());
}
}
}
private JComponent createToolBar() {
JToolBar toolBar = new JToolBar("File operations");
toolBar.setFloatable(false);
importAction = new ImportAction();
importAction.setEnabled(solutionBusiness.hasImporter());
toolBar.add(new JButton(importAction));
openAction = new OpenAction();
openAction.setEnabled(true);
toolBar.add(new JButton(openAction));
saveAction = new SaveAction();
saveAction.setEnabled(false);
toolBar.add(new JButton(saveAction));
exportAction = new ExportAction();
exportAction.setEnabled(false);
toolBar.add(new JButton(exportAction));
toolBar.addSeparator();
progressBar = new JProgressBar(0, 100);
progressBar.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
toolBar.add(progressBar);
toolBar.addSeparator();
solveAction = new SolveAction();
solveAction.setEnabled(false);
solveButton = new JButton(solveAction);
terminateSolvingEarlyAction = new TerminateSolvingEarlyAction();
terminateSolvingEarlyAction.setEnabled(false);
terminateSolvingEarlyButton = new JButton(terminateSolvingEarlyAction);
terminateSolvingEarlyButton.setVisible(false);
toolBar.add(solveButton, "solveAction");
toolBar.add(terminateSolvingEarlyButton, "terminateSolvingEarlyAction");
solveButton.setMinimumSize(terminateSolvingEarlyButton.getMinimumSize());
solveButton.setPreferredSize(terminateSolvingEarlyButton.getPreferredSize());
return toolBar;
}
private class SolveAction extends AbstractAction {
public SolveAction() {
super("Solve", new ImageIcon(SolverAndPersistenceFrame.class.getResource("solveAction.png")));
}
public void actionPerformed(ActionEvent e) {
setSolvingState(true);
Solution planningProblem = solutionBusiness.getSolution();
new SolveWorker(planningProblem).execute();
}
}
protected class SolveWorker extends SwingWorker {
protected final Solution planningProblem;
public SolveWorker(Solution planningProblem) {
this.planningProblem = planningProblem;
}
@Override
protected Solution doInBackground() throws Exception {
return solutionBusiness.solve(planningProblem);
}
@Override
protected void done() {
try {
Solution bestSolution = get();
solutionBusiness.setSolution(bestSolution);
} catch (InterruptedException e) {
throw new IllegalStateException("Solving interrupted.", e);
} catch (ExecutionException e) {
throw new IllegalStateException("Solving failed.", e.getCause());
} finally {
setSolvingState(false);
resetScreen();
}
}
}
private class TerminateSolvingEarlyAction extends AbstractAction {
public TerminateSolvingEarlyAction() {
super("Terminate solving early",
new ImageIcon(SolverAndPersistenceFrame.class.getResource("terminateSolvingEarlyAction.png")));
}
public void actionPerformed(ActionEvent e) {
terminateSolvingEarlyAction.setEnabled(false);
progressBar.setString("Terminating...");
// This async, so it doesn't stop the solving immediately
solutionBusiness.terminateSolvingEarly();
}
}
private class OpenAction extends AbstractAction {
private static final String NAME = "Open...";
public OpenAction() {
super(NAME, new ImageIcon(SolverAndPersistenceFrame.class.getResource("openAction.png")));
}
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser(solutionBusiness.getSolvedDataDir());
fileChooser.setFileFilter(new FileFilter() {
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith(".xml");
}
public String getDescription() {
return "Solver xml files";
}
});
fileChooser.setDialogTitle(NAME);
int approved = fileChooser.showOpenDialog(SolverAndPersistenceFrame.this);
if (approved == JFileChooser.APPROVE_OPTION) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
solutionBusiness.openSolution(fileChooser.getSelectedFile());
setSolutionLoaded();
} finally {
setCursor(Cursor.getDefaultCursor());
}
}
}
}
private class SaveAction extends AbstractAction {
private static final String NAME = "Save as...";
public SaveAction() {
super(NAME, new ImageIcon(SolverAndPersistenceFrame.class.getResource("saveAction.png")));
}
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser(solutionBusiness.getSolvedDataDir());
fileChooser.setFileFilter(new FileFilter() {
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith(".xml");
}
public String getDescription() {
return "Solver xml files";
}
});
fileChooser.setDialogTitle(NAME);
fileChooser.setSelectedFile(new File(solutionBusiness.getSolvedDataDir(),
FilenameUtils.getBaseName(solutionBusiness.getSolutionFileName()) + ".xml"));
int approved = fileChooser.showSaveDialog(SolverAndPersistenceFrame.this);
if (approved == JFileChooser.APPROVE_OPTION) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
solutionBusiness.saveSolution(fileChooser.getSelectedFile());
} finally {
setCursor(Cursor.getDefaultCursor());
}
refreshQuickOpenPanel(quickOpenUnsolvedPanel, quickOpenUnsolvedActionList,
solutionBusiness.getUnsolvedFileList());
refreshQuickOpenPanel(quickOpenSolvedPanel, quickOpenSolvedActionList,
solutionBusiness.getSolvedFileList());
SolverAndPersistenceFrame.this.validate();
}
}
}
private class ImportAction extends AbstractAction {
private static final String NAME = "Import...";
public ImportAction() {
super(NAME, new ImageIcon(SolverAndPersistenceFrame.class.getResource("importAction.png")));
}
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser(solutionBusiness.getImportDataDir());
fileChooser.setFileFilter(new FileFilter() {
public boolean accept(File file) {
return file.isDirectory() || solutionBusiness.acceptImportFile(file);
}
public String getDescription() {
return "Import files (*." + solutionBusiness.getImportFileSuffix() + ")";
}
});
fileChooser.setDialogTitle(NAME);
int approved = fileChooser.showOpenDialog(SolverAndPersistenceFrame.this);
if (approved == JFileChooser.APPROVE_OPTION) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
solutionBusiness.importSolution(fileChooser.getSelectedFile());
setSolutionLoaded();
} finally {
setCursor(Cursor.getDefaultCursor());
}
}
}
}
private class ExportAction extends AbstractAction {
private static final String NAME = "Export as...";
public ExportAction() {
super(NAME, new ImageIcon(SolverAndPersistenceFrame.class.getResource("exportAction.png")));
}
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser(solutionBusiness.getExportDataDir());
fileChooser.setFileFilter(new FileFilter() {
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith("." + solutionBusiness.getExportFileSuffix());
}
public String getDescription() {
return "Export files (*." + solutionBusiness.getExportFileSuffix() + ")";
}
});
fileChooser.setDialogTitle(NAME);
fileChooser.setSelectedFile(new File(solutionBusiness.getExportDataDir(),
FilenameUtils.getBaseName(solutionBusiness.getSolutionFileName())
+ "." + solutionBusiness.getExportFileSuffix()));
int approved = fileChooser.showSaveDialog(SolverAndPersistenceFrame.this);
if (approved == JFileChooser.APPROVE_OPTION) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
solutionBusiness.exportSolution(fileChooser.getSelectedFile());
} finally {
setCursor(Cursor.getDefaultCursor());
}
}
}
}
private JPanel createMiddlePanel() {
middlePanel = new JPanel(new CardLayout());
ImageIcon usageExplanationIcon = new ImageIcon(getClass().getResource(solutionPanel.getUsageExplanationPath()));
JLabel usageExplanationLabel = new JLabel(usageExplanationIcon);
// Allow splitPane divider to be moved to the right
usageExplanationLabel.setMinimumSize(new Dimension(100, 100));
middlePanel.add(usageExplanationLabel, "usageExplanationPanel");
JComponent wrappedSolutionPanel;
if (solutionPanel.isWrapInScrollPane()) {
wrappedSolutionPanel = new JScrollPane(solutionPanel);
} else {
wrappedSolutionPanel = solutionPanel;
}
middlePanel.add(wrappedSolutionPanel, "solutionPanel");
return middlePanel;
}
private JPanel createScorePanel() {
JPanel scorePanel = new JPanel(new BorderLayout());
scorePanel.setBorder(BorderFactory.createEtchedBorder());
showConstraintMatchesDialogAction = new ShowConstraintMatchesDialogAction();
showConstraintMatchesDialogAction.setEnabled(false);
scorePanel.add(new JButton(showConstraintMatchesDialogAction), BorderLayout.WEST);
scoreField = new JTextField("Score:");
scoreField.setEditable(false);
scoreField.setForeground(Color.BLACK);
scoreField.setBorder(BorderFactory.createLoweredBevelBorder());
scorePanel.add(scoreField, BorderLayout.CENTER);
refreshScreenDuringSolvingCheckBox = new JCheckBox("Refresh screen during solving",
solutionPanel.isRefreshScreenDuringSolving());
scorePanel.add(refreshScreenDuringSolvingCheckBox, BorderLayout.EAST);
return scorePanel;
}
private class ShowConstraintMatchesDialogAction extends AbstractAction {
public ShowConstraintMatchesDialogAction() {
super("Constraint matches", new ImageIcon(SolverAndPersistenceFrame.class.getResource("showConstraintMatchesDialogAction.png")));
}
public void actionPerformed(ActionEvent e) {
constraintMatchesDialog.resetContentPanel();
constraintMatchesDialog.setVisible(true);
}
}
private void setSolutionLoaded() {
setTitle(titlePrefix + " - " + solutionBusiness.getSolutionFileName());
((CardLayout) middlePanel.getLayout()).show(middlePanel, "solutionPanel");
solveAction.setEnabled(true);
saveAction.setEnabled(true);
exportAction.setEnabled(solutionBusiness.hasExporter());
showConstraintMatchesDialogAction.setEnabled(true);
resetScreen();
}
private void setSolvingState(boolean solving) {
for (Action action : quickOpenUnsolvedActionList) {
action.setEnabled(!solving);
}
for (Action action : quickOpenSolvedActionList) {
action.setEnabled(!solving);
}
importAction.setEnabled(!solving && solutionBusiness.hasImporter());
openAction.setEnabled(!solving);
saveAction.setEnabled(!solving);
exportAction.setEnabled(!solving && solutionBusiness.hasExporter());
solveAction.setEnabled(!solving);
solveButton.setVisible(!solving);
terminateSolvingEarlyAction.setEnabled(solving);
terminateSolvingEarlyButton.setVisible(solving);
solutionPanel.setEnabled(!solving);
progressBar.setIndeterminate(solving);
progressBar.setStringPainted(solving);
progressBar.setString(solving ? "Solving..." : null);
showConstraintMatchesDialogAction.setEnabled(!solving);
solutionPanel.setSolvingState(solving);
}
public void resetScreen() {
solutionPanel.resetPanel(solutionBusiness.getSolution());
validate();
scoreField.setForeground(determineScoreFieldForeground(solutionBusiness.getScore()));
scoreField.setText("Score: " + solutionBusiness.getScore());
}
private Color determineScoreFieldForeground(Score> score) {
if (!(score instanceof FeasibilityScore)) {
return Color.BLACK;
} else {
FeasibilityScore> feasibilityScore = (FeasibilityScore>) score;
return feasibilityScore.isFeasible() ? TangoColorFactory.CHAMELEON_3 : TangoColorFactory.SCARLET_3;
}
}
}