GUI refactoring

- used only nodefactory for creation
- merged MyDecisionPanel with LW
This commit is contained in:
2024-01-11 19:24:47 +01:00
parent 732d9eed20
commit 86f14f2d5a
10 changed files with 111 additions and 235 deletions

View File

@@ -21,7 +21,7 @@ public class PrototypeGUI extends MyDecisionPanel {
};
public PrototypeGUI() {
super(SmileLib.getNetworkFrom(PROTOTIPO), NODES);
this.buildPanel(SmileLib.getNetworkFrom(PROTOTIPO), NODES);
}
@Override

View File

@@ -23,7 +23,7 @@ public class VehicleGUI extends MyDecisionPanel {
};
public VehicleGUI() {
super(SmileLib.getNetworkFrom(VEICOLO), NODES);
this.buildPanel(SmileLib.getNetworkFrom(VEICOLO), NODES);
}
@Override

View File

@@ -1,11 +1,14 @@
package net.berack.upo.ai.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
@@ -32,6 +35,7 @@ public class MainGUI extends JFrame {
public final PrototypeGUI PrototypeGUI = new PrototypeGUI();
public final VehicleGUI VehicleGUI = new VehicleGUI();
private final Dimension size = new Dimension(500, 400);
private final JMenuBar menuBar = new JMenuBar();
/**
@@ -44,7 +48,7 @@ public class MainGUI extends JFrame {
this.setJMenuBar(menuBar);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(500, 400);
this.setSize(this.size);
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setVisible(true);
@@ -88,11 +92,15 @@ public class MainGUI extends JFrame {
*/
private void setPanel(MyPanel panel) {
if(panel instanceof MyDecisionPanel) {
var scroll = new JScrollPane(panel);
var temp = new JPanel(new BorderLayout());
temp.add(panel, BorderLayout.NORTH);
temp.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
var scroll = new JScrollPane(temp);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.getVerticalScrollBar().setUnitIncrement(20);
scroll.setPreferredSize(new Dimension(500, 400));
scroll.setPreferredSize(this.size);
this.setContentPane(scroll);
}
else this.setContentPane(panel);

View File

@@ -1,7 +1,6 @@
package net.berack.upo.ai.gui;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;
@@ -24,21 +23,47 @@ import smile.Network;
*/
public abstract class MyDecisionPanel extends MyPanel {
public final Network net;
private final List<NodeGUI> update = new ArrayList<>();
protected Network net;
protected final List<NodeGUI> update = new ArrayList<>();
private Component[] components = new Component[0];
/**
* Aggiunge dei componenti nel caso siano necessari che verranno mostrati nella prima parte del pannello
* @param components i componenti da mostrare (devono essere pari)
*/
protected void setExtraComponents(Component[] components) {
if(components.length % 2 != 0) throw new IllegalArgumentException("Must be a an even length array");
this.components = components;
}
/**
* Costruisce il dinamicamente da una rete e da una lista di nodi da mostrare.
* Il risultato sarà già inserito nel pannello e per vedere i valori bisognerà utilizzare il metodo updateAll()
* @param net la rete da mostrare
* @param nodes il sottoinsieme di nodi da mostrare
* @param nodes il sottoinsieme di nodi da mostrare o (empty / null) per tutti i nodi
*/
protected MyDecisionPanel(Network net, String...nodes) {
public void buildPanel(Network net, String...nodes) {
this.net = net;
var layout = new GroupLayout(this);
if(this.net == null) return;
var size = new Dimension(500, 400);
this.setSize(size);
if(nodes == null || nodes.length == 0)
this.buildNodes(this.net.getAllNodes());
else {
var handles = new int[nodes.length];
for(var i = 0; i < nodes.length; i++) handles[i] = this.net.getNode(nodes[i]);
this.buildNodes(handles);
}
}
/**
* Costruisce i nodi passati in input nel pannello
* @param handles i nodi da costruire
*/
private void buildNodes(int[] handles) {
this.update.clear();
this.removeAll();
var layout = new GroupLayout(this);
this.setLayout(layout);
layout.setAutoCreateGaps(true);
@@ -47,8 +72,18 @@ public abstract class MyDecisionPanel extends MyPanel {
var gBarch = layout.createParallelGroup();
var vGroup = layout.createSequentialGroup();
for(var node: nodes) {
var handle = net.getNode(node);
for(var i = 0; i < this.components.length; i += 2) {
var comp1 = this.components[i];
var comp2 = this.components[i+1];
gLabel.addComponent(comp1);
gBarch.addComponent(comp2);
vGroup.addGroup(layout.createParallelGroup()
.addComponent(comp1)
.addComponent(comp2));
}
for(var handle : handles) {
var panel = factory.create(handle);
update.add(panel);
@@ -78,7 +113,7 @@ public abstract class MyDecisionPanel extends MyPanel {
* Utile per cambiare il font in BOLD e aumentarne la grandezza
* @param component il componente da cambiare il font
*/
private void setFont(Component component) {
protected void setFont(Component component) {
var font = component.getFont();
font = new Font(font.getName(), Font.BOLD, font.getSize() + 2);
component.setFont(font);

View File

@@ -60,7 +60,7 @@ public class DecisionNodeGUI extends NodeGUI {
@Override
public void updateNode() {
var selected = this.net.isEvidence(this.node)? this.net.getEvidence(this.node) : -1;
var values = this.net.getNodeValue(this.node);
var values = this.getValues();
var max = -1;
if(values.length == this.buttons.length) {
max = 0;

View File

@@ -12,6 +12,7 @@ public abstract class NodeGUI extends JPanel {
public final Network net;
public final int node;
private double[] values = null;
/**
* Salva informazioni essenziali del nodo
@@ -27,6 +28,28 @@ public abstract class NodeGUI extends JPanel {
this.node = node;
}
/**
* Permette di impostare dei valori custom da mostrare.
* Dopo la chiamata non verranno modificati i valori a schermo se non
* si ha chiamato il metodo updateNode.
*
* @param values i valori da mostrare (dovranno essere compresi tra 0 e 1)
*/
public void setValues(double[] values) {
for(var val : values) if(val < 0 || val > 1) throw new IllegalArgumentException();
this.values = values;
}
/**
* Mostra i valori del nodo. Se sono stati passati dei valori custom allora userà quelli,
* altrimenti prenderà i valori contenuti all'interno della rete.
* @return i valori del nodo
*/
public double[] getValues() {
if(this.values != null) return this.values;
return this.net.getNodeValue(this.node);
}
/**
* In questo metodo si deve fare un refresh dei valori mostrati nel pannello.
* I valori potranno essere presi direttamente dalla rete utilizzando le proprietà pubbliche net e node.

View File

@@ -2,6 +2,7 @@ package net.berack.upo.ai.gui.nodes;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Label;
import java.util.function.Consumer;
@@ -68,10 +69,7 @@ public class ProbabilityNodeGUI extends NodeGUI {
if(action == null) lName.setBorder(null);
else lName.addActionListener(a -> action.accept(index));
var size = barchart.getPreferredSize();
size.width = 100;
size.height = 10;
barchart.setPreferredSize(size);
barchart.setPreferredSize(new Dimension(100, 10));
barchart.setBackground(COLORS[i % COLORS.length]);
@@ -91,8 +89,8 @@ public class ProbabilityNodeGUI extends NodeGUI {
@Override
public void updateNode() {
var values = this.net.getNodeValue(this.node);
var evidence = this.net.isEvidence(this.node)? this.net.getEvidence(node) : -1;
var values = this.getValues();
var evidence = this.net.isEvidence(this.node)? this.net.getEvidence(this.node) : -1;
var enable = (values.length == this.outcomes.length);
for(var i = 0; i < this.outcomes.length; i++) {

View File

@@ -39,7 +39,7 @@ public class UtilityNodeGUI extends NodeGUI {
@Override
public void updateNode() {
var values = this.net.getNodeValue(this.node);
var values = this.getValues();
var val = (values.length == 1) ? String.format("% 5.2f", values[0]) : " ";
this.utility.setText(val);
}

View File

@@ -1,33 +1,24 @@
package net.berack.upo.ai.problem3;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.FileDialog;
import java.awt.Font;
import java.util.Collection;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import net.berack.upo.ai.gui.MyPanel;
import net.berack.upo.ai.gui.MyDecisionPanel;
/**
* Classe usata per far vedere il risultato di una run di lw su un network
* @author Berack
*/
public class LikelihoodWeightingGUI extends MyPanel {
public class LikelihoodWeightingGUI extends MyDecisionPanel {
private LikelihoodWeighting lw = null;
private OutcomeChartGUI[] chartGUI = null;
private final JPanel scroll = new JPanel(); // tried using JScrollPane but nothing shows up
private int totalRuns = 1000;
/**
@@ -35,8 +26,13 @@ public class LikelihoodWeightingGUI extends MyPanel {
* Siccome non c'è nessun network, il pannello sarà vuoto finchè non si apriranno dei network
*/
public LikelihoodWeightingGUI() {
this.scroll.setPreferredSize(new Dimension(500, 400));
this.add(this.scroll);
var totLabel = new JLabel("Total Runs");
var totValue = new JComboBox<>(new Integer[] {this.totalRuns, 5 * this.totalRuns, 10 * this.totalRuns, 20 * this.totalRuns});
totValue.addItemListener(a -> this.totalRuns = (Integer) totValue.getSelectedItem());
this.setFont(totLabel);
this.setExtraComponents(new Component[] { totLabel, totValue });
this.buildPanel(null);
}
/**
@@ -66,7 +62,15 @@ public class LikelihoodWeightingGUI extends MyPanel {
try {
var net = SmileLib.getNetworkFrom(fileName);
this.lw = new LikelihoodWeighting(net);
this.chartGUI = null;
this.buildPanel(net);
var totLabel = new JLabel("Total Runs");
var totValue = new JComboBox<>(new Integer[] {this.totalRuns, 5 * this.totalRuns, 10 * this.totalRuns, 20 * this.totalRuns});
totValue.addItemListener(a -> this.totalRuns = (Integer) totValue.getSelectedItem());
this.setFont(totLabel);
this.add(totLabel);
this.add(totValue);
this.updateAll();
} catch (Exception e) {
e.printStackTrace();
@@ -81,93 +85,11 @@ public class LikelihoodWeightingGUI extends MyPanel {
if(this.lw == null) return;
this.lw.updateNetwork(totalRuns);
var nodes = this.lw.getAllNodes();
if(chartGUI == null) buildPanel(nodes);
var i = 0;
for(var node : nodes) chartGUI[i++].updateValues(node.values, node.evidence);
this.invalidate();
this.validate();
this.repaint();
}
/**
* Crea il pannello da zero usando i nodi passati in input come valori
* @param nodes i nodi da mostrare
*/
private void buildPanel(Collection<NetworkNode> nodes) {
var panel = new JPanel();
var layout = new GroupLayout(panel);
panel.setLayout(layout);
layout.setAutoCreateGaps(true);
var gLabel = layout.createParallelGroup();
var gBarch = layout.createParallelGroup();
var vGroup = layout.createSequentialGroup();
var totLabel = new JLabel("Total Runs");
var totValue = new JComboBox<>(new Integer[] {this.totalRuns, 5 * this.totalRuns, 10 * this.totalRuns, 20 * this.totalRuns});
totValue.addItemListener(a -> this.totalRuns = (Integer) totValue.getSelectedItem());
var font = totLabel.getFont();
font = new Font(font.getName(), Font.BOLD, font.getSize() + 2);
totLabel.setFont(font);
gLabel.addComponent(totLabel);
gBarch.addComponent(totValue);
vGroup.addGroup(layout.createParallelGroup()
.addComponent(totLabel)
.addComponent(totValue));
this.chartGUI = new OutcomeChartGUI[nodes.size()];
var i = 0;
for(var node : nodes) {
var net = node.net;
var handle = node.handle;
var label = new JTextArea(node.name);
label.setEditable(false);
label.setLineWrap(true);
label.setWrapStyleWord(true);
label.setOpaque(false);
label.setBorder(BorderFactory.createEmptyBorder());
var barch = new OutcomeChartGUI(node.outcomes, e -> {
if(net.isEvidence(handle) && net.getEvidence(handle) == e)
net.clearEvidence(handle);
else net.setEvidence(handle, e);
this.updateAll();
});
this.chartGUI[i++] = barch;
font = label.getFont();
font = new Font(font.getName(), Font.BOLD, font.getSize() + 2);
label.setFont(font);
gLabel.addComponent(label);
gBarch.addComponent(barch);
vGroup.addGroup(layout.createParallelGroup()
.addComponent(label)
.addComponent(barch));
for(var node : this.update) {
var values = this.lw.getNodeValue(node.node);
node.setValues(values);
node.updateNode();
}
var hGroup = layout.createSequentialGroup();
hGroup.addGroup(gLabel).addGroup(gBarch);
layout.setVerticalGroup(vGroup);
layout.setHorizontalGroup(hGroup);
var sizes = this.scroll.getPreferredSize();
sizes.height = panel.getMinimumSize().height;
sizes.width -= 10;
panel.setPreferredSize(sizes);
this.scroll.removeAll();
this.scroll.add(panel);
}
@Override

View File

@@ -1,110 +0,0 @@
package net.berack.upo.ai.problem3;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Label;
import java.util.function.Consumer;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
/**
* Classe che rappresenta gli outcome di un nodo di un network.
* I valori deli outcome vengono visualizzati con un grafico.
* @author Berack
*/
public class OutcomeChartGUI extends JPanel {
public static final Color[] COLORS = {
new Color(255,127,0),
new Color(0,127,255),
new Color(0,255,127),
new Color(255,0,127),
new Color(127,0,255),
new Color(127,255,0),
};
private JButton[] outcomes;
private Label[] valuesChart;
private JLabel[] valuesPercent;
/**
* Crea il JPanel da visualizzare a partire da un NetworkNode appropriamente inizializzato.
* Quando verrà visualizzato, il nodo avrà il nome degli output e i suoi valori in %
* con una barra colorata per indicare la grandezza visivamente.
*
* @param labels i label degli output del nodo
* @param action una azione da fare nel caso in cui venga premuto su un outcome
*/
public OutcomeChartGUI(String[] labels, Consumer<Integer> action) {
var layout = new GridLayout(labels.length, 2);
this.setLayout(layout);
this.setBorder(BorderFactory.createCompoundBorder(new EmptyBorder(1, 0, 0, 0), BorderFactory.createLineBorder(Color.GRAY)));
this.outcomes = new JButton[labels.length];
this.valuesChart = new Label[labels.length];
this.valuesPercent = new JLabel[labels.length];
for(var i = 0; i < labels.length; i++) {
var lName = new JButton(labels[i]);
var lValue = new JLabel();
var barchart = new Label();
this.outcomes[i] = lName;
this.valuesChart[i] = barchart;
this.valuesPercent[i] = lValue;
final var index = i;
lName.setContentAreaFilled(false);
lName.setFocusable(false);
if(action == null) lName.setBorder(null);
else lName.addActionListener(a -> action.accept(index));
var size = barchart.getPreferredSize();
size.width = 100;
size.height = 100;
barchart.setPreferredSize(size);
barchart.setBackground(COLORS[i % COLORS.length]);
var panel1 = new JPanel();
panel1.setLayout(new GridLayout(1, 2));
panel1.add(lName);
panel1.add(lValue);
var panel2 = new JPanel();
panel2.setLayout(new BorderLayout());
panel2.add(barchart, BorderLayout.LINE_START);
this.add(panel1);
this.add(panel2);
}
}
/**
* Modifica i valori mostrati a schermo cambiando anche la grandezza
* della barra indicante il valore
* @param values i nuovi valori da mostrare
* @param evidence indica l'evidenza del nodo
*/
public void updateValues(double[] values, int evidence) {
if(this.outcomes.length != values.length) throw new IllegalArgumentException("Arrays length myst be equals!");
for(var i = 0; i < this.outcomes.length; i++) {
var value = values[i] * 100;
var barchart = this.valuesChart[i];
var size = barchart.getSize();
size.width = (int) (value * 1.5);
barchart.setSize(size);
barchart.setPreferredSize(size);
this.valuesPercent[i].setText(String.format("% 4.2f%%", value));
if(evidence == i) this.outcomes[i].setForeground(Color.RED);
else this.outcomes[i].setForeground(Color.BLACK);
}
}
}