/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.tools; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JSeparator; import javax.swing.SwingUtilities; import javax.swing.Timer; import com.rapidminer.BreakpointListener; import com.rapidminer.Process; import com.rapidminer.ProcessListener; import com.rapidminer.gui.look.RapidLookTools; import com.rapidminer.gui.processeditor.ProcessEditor; import com.rapidminer.operator.IOContainer; import com.rapidminer.operator.Operator; import com.rapidminer.tools.Tools; /** * The status bar shows the currently applied operator and the time it needed so far. In addition, * the number of times the operator was already applied is also displayed. * * @author Ingo Mierswa, Simon Fischer */ public class StatusBar extends JPanel implements ProcessEditor { private final ProcessListener processListener = new ProcessListener() { @Override public void processStarts(com.rapidminer.Process process) { rootOperator = null; clearSpecialText(); } @Override public void processStartedOperator(final com.rapidminer.Process process, final Operator op) { if (rootOperator == null) { rootOperator = new OperatorEntry(op); } else { rootOperator.addOperator(op); } setText(); } @Override public void processFinishedOperator(com.rapidminer.Process process, final Operator op) { if (rootOperator != null) { rootOperator.removeOperator(op); } } @Override public void processEnded(Process process) { rootOperator = null; clearSpecialText(); } }; private final BreakpointListener breakpointListener = new BreakpointListener() { @Override public void resume() { breakpoint = -1; if (rootOperator != null) { setText(); } else { operatorLabel.setText(" "); } } @Override public void breakpointReached(Process process, Operator op, IOContainer io, int location) { breakpoint = location; operatorLabel.setText("[" + op.getApplyCount() + "] " + op.getName() + ": breakpoint reached " + BreakpointListener.BREAKPOINT_POS_NAME[breakpoint] + " operator, press resume..."); } }; private static class OperatorEntry { private final Collection<OperatorEntry> children = new LinkedList<>(); private final Operator operator; public OperatorEntry(Operator operator) { this.operator = operator; } public void addOperator(Operator operator) { synchronized (children) { if (this.operator == operator.getParent()) { children.add(new OperatorEntry(operator)); } else { for (OperatorEntry childEntry : children) { childEntry.addOperator(operator); } } } } public void removeOperator(Operator operator) { synchronized (children) { Iterator<OperatorEntry> iterator = children.iterator(); while (iterator.hasNext()) { OperatorEntry childEntry = iterator.next(); if (childEntry.getOperator() == operator) { iterator.remove(); } else { childEntry.removeOperator(operator); } } } } public String toString(OperatorEntry entry, long time) { synchronized (children) { StringBuffer buffer = new StringBuffer(); Operator currentOperator = entry.getOperator(); buffer.append("[" + currentOperator.getApplyCount() + "] " + currentOperator.getName() + " " + Tools.formatDuration(time - currentOperator.getStartTime())); Iterator<OperatorEntry> iterator = children.iterator(); if (iterator.hasNext()) { buffer.append(" \u21B3 "); } while (iterator.hasNext()) { OperatorEntry childEntry = iterator.next(); if (children.size() > 1) { buffer.append(" ( "); } buffer.append(childEntry.toString(childEntry, time)); if (children.size() > 1) { buffer.append(" ) "); } if (iterator.hasNext()) { buffer.append(" | "); } } return buffer.toString(); } } public Operator getOperator() { return operator; } } private static final long serialVersionUID = 1L; private final JLabel operatorLabel = createLabel(" "); private OperatorEntry rootOperator = null; private final JProgressBar progressBar; private int breakpoint = -1; private String specialText = null; /** Only needed to keep track where we added ourselves as listeners. */ private Process process; public StatusBar() { this(true); } public StatusBar(boolean showProgressBar) { GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 0; constraints.gridwidth = 1; constraints.insets = new Insets(0, 5, 0, 0); BetaFeaturesIndicator indicator = new BetaFeaturesIndicator(); JLabel modeLabel = indicator.getModeLabel(); layout.setConstraints(modeLabel, constraints); add(modeLabel); constraints.weighty = 1; constraints.fill = GridBagConstraints.VERTICAL; JSeparator separator = indicator.getModeSeparator(); layout.setConstraints(separator, constraints); add(separator); constraints.weightx = 1; constraints.weighty = 0; constraints.fill = GridBagConstraints.HORIZONTAL; layout.setConstraints(operatorLabel, constraints); add(operatorLabel); constraints.weightx = 0; constraints.fill = GridBagConstraints.NONE; constraints.gridwidth = GridBagConstraints.RELATIVE; constraints.insets = new Insets(0, 0, 1, 3); progressBar = new JProgressBar() { private static final long serialVersionUID = 1L; @Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); return new Dimension(500, size.height); } @Override public boolean isStringPainted() { // the status bar progress bar needs to be of the same height regardless so we // always pretend we paint the label because that affects its height return true; } }; progressBar.putClientProperty(RapidLookTools.PROPERTY_PROGRESSBAR_COMPRESSED, true); if (showProgressBar) { progressBar.setStringPainted(false); progressBar.setEnabled(false); progressBar.setOpaque(false); progressBar.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { if (!ProgressThreadDialog.getInstance().isVisible()) { ProgressThreadDialog.getInstance().setVisible(true, true); } } } }); layout.setConstraints(progressBar, constraints); add(progressBar); } } public void setSpecialText(String specialText) { this.specialText = specialText; setText(this.specialText); } public void clearSpecialText() { this.specialText = null; setText(); } public void startClockThread() { new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (specialText != null && !specialText.isEmpty()) { setText(specialText); } else { setText(); } } }).start(); } /** Sets the progress in the status bar. Executed on EDT. */ public void setProgress(final String label, final int completed, final int total) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (label == null || label.isEmpty()) { progressBar.setStringPainted(false); } else { progressBar.setStringPainted(true); } progressBar.setIndeterminate(false); if (completed < total) { if (!progressBar.isEnabled()) { progressBar.setEnabled(true); } progressBar.setIndeterminate(false); progressBar.setString(label); progressBar.setMaximum(total); progressBar.setValue(completed); } else { progressBar.setString(""); progressBar.setValue(0); progressBar.setEnabled(false); } progressBar.repaint(); } }); } /** * Sets the progress in the status bar using intermediate mode. Executed on the EDT. * * @param label * @param completed * @param total */ public void setIndeterminateProgress(final String label, final int completed, final int total) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (completed < total) { if (!progressBar.isEnabled()) { progressBar.setEnabled(true); } progressBar.setIndeterminate(true); progressBar.setString(label); progressBar.setMaximum(total); progressBar.setValue(completed); } else { progressBar.setString(""); progressBar.setValue(0); progressBar.setEnabled(false); progressBar.setIndeterminate(false); } progressBar.repaint(); } }); } @Override public void processChanged(Process process) { if (this.process != process) { if (this.process != null) { this.process.removeBreakpointListener(breakpointListener); this.process.getRootOperator().removeProcessListener(processListener); } this.process = process; if (this.process != null) { this.process.addBreakpointListener(breakpointListener); this.process.getRootOperator().addProcessListener(processListener); } } } @Override public void processUpdated(Process process) {} @Override public void setSelection(List<Operator> selection) {} private synchronized void setText(final String text) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { operatorLabel.setText(text); } }); } private void setText() { if (rootOperator != null) { setText(rootOperator.toString(rootOperator, System.currentTimeMillis())); } else { setText(""); } } private static JLabel createLabel(String text) { JLabel label = new JLabel(text); label.setFont(label.getFont().deriveFont(Font.PLAIN)); return label; } }