View Full Version : [JAVA] Aggiungere interfaccia grafica a un programma...
Andrea16v
19-10-2006, 13:25
Ciao a tutti, in Java non ho mai lavorato con la "grafica", quindi magari quello che chiedo è assurdo.
Ho un classico programma che chiede dei dati all'utente tramite System.out.println, riceve in input i dati inseriti dall'utente, li usa per eseguire dei calcoli e stampa i risultati sempre con println sulla consolle.
Se volessi modificare il programma in modo che l'utente possa inserire i dati in una finestra (che vorrei personalizzare con dei pulsanti e grafica varia) devo riscrivere tutto da capo o mi basta solo compilare un'interfaccia o estendere qualche classe predefinita che si occupi solo della grafica della finestra e effettui in automatico il reindirizzamento dell'input-output?
franksisca
19-10-2006, 14:05
Ciao a tutti, in Java non ho mai lavorato con la "grafica", quindi magari quello che chiedo è assurdo.
Ho un classico programma che chiede dei dati all'utente tramite System.out.println, riceve in input i dati inseriti dall'utente, li usa per eseguire dei calcoli e stampa i risultati sempre con println sulla consolle.
Se volessi modificare il programma in modo che l'utente possa inserire i dati in una finestra (che vorrei personalizzare con dei pulsanti e grafica varia) devo riscrivere tutto da capo o mi basta solo compilare un'interfaccia o estendere qualche classe predefinita che si occupi solo della grafica della finestra e effettui in automatico il reindirizzamento dell'input-output?
la riutilizzazione del software prevede questo.
Esiste un modello di pattern MVC(Model View Controller) che non fà altro che esprimere ciò.
Ovvero separare l'interfaccia grafica dalla parte implementativa.
Se il tuo programa è stato pensato bene puoi farti un controllore che gestisce la comunicazione tra grafica e software pre-esistente.
tutto ciò comporta la realizzazione della parte grafica e delle piccolissime modifiche di quello preesistente;)
Sì ed è più facile di quanto possa sembrare. Le interfacce a linea di comando sono casi di comunicazione elementare (sincrona, di solito con una produzione). L'omologo "visuale" è la procedura guidata: una serie di componenti che si attivano in via esclusiva secondo una sequenza prestabilita.
Semplicemente, quando catturi dall'output la stringa "inserire il nome", attivi il componente X e quando l'utente preme il pulsante "successivo" sul pannello che contiene X spari il risultato attraverso System.in. Metti in pausa l'interfaccia aspetti che arrivi la risposta. Puoi usare un Pipe per connettere un flusso di output ad uno di input e viceversa.
[PS]
Il sì è "sì ti basta scrivere l'interfaccia e aggiungere qualche classe", non "sì MVC".
franksisca
19-10-2006, 14:33
Il sì è "sì ti basta scrivere l'interfaccia e aggiungere qualche classe", non "sì MVC".
non ho capito questa sottilineatura..... :mbe: :mbe: :mbe:
Ho iniziato la risposta con "Sì" quando c'era solo la richiesta di Andrea16v. Con un'altra risposta in mezzo il "sì" poteva sembrare un assenso alla risposta di franksiska. Per non cambiare il testo sotto il naso di chi eventualmente lo stesse leggendo, ho aggiunto una postilla per chiarire che il "sì" non era riferito alla risposta che citava MVC.
Non perchè non vada bene. Non c'era quando ho scritto la risposta, dunque non potevo averla letta, dunque non potevo assentire o dissentire.
Che siamo entrati nella stagione degli amori? Facciamo come gli Alci? Ci attacchiamo anche alle virgole pur di prenderci a cornate? :boxe: :D
franksisca
19-10-2006, 14:54
Che siamo entrati nella stagione degli amori? Facciamo come gli Alci? Ci attacchiamo anche alle virgole pur di prenderci a cornate? :boxe: :D
:D :D :D :D Tranquillo, sono un pacifista convinto, almeno nei forum:D:D:D
Andrea16v
19-10-2006, 15:59
Sì ed è più facile di quanto possa sembrare. Le interfacce a linea di comando sono casi di comunicazione elementare (sincrona, di solito con una produzione). L'omologo "visuale" è la procedura guidata: una serie di componenti che si attivano in via esclusiva secondo una sequenza prestabilita.
(..)
Attivare una serie di componenti in via esclusiva però non mi piace molto, vorrei ottenere una finestra con una serie di caselle etichettate in cui l'utente possa scrivere a piacere, e successivamente confermare tutti i dati inseriti in un colpo solo, con un pulsante etichettato "invia" o "inserisci" e un secondo pulsante per annullare i dati inseriti e uscire dal programma.
Per intenderci un classico form tipo quelli che si usano per registrarsi ad un servizio online, solo molto semplice, es:
una classe Persona con una serie di variabili d'istanza che verranno inizializzate tramite questa finestra lasciando inserire all'utente le sue generalità, per poi premere il pulsante "invia".
A questo punto, una volta assegnati i valori alle varie var. di istanza, il compito della finestra finisce. Il programma crea un'istanza di oggetto Persona chiamando l'opportuno costruttore e inserisce l'utente in un database, ecc ecc
Ahhh, pretenzioso! :D :D. Penso che sia altrettanto semplice. Tu crei la tua interfaccia con tutti i pulsanti e le caselle necessarie, poi, quanto l'utente premi invio, inizi il "dialogo" con l'applicazione a linea di comando.
In pratica la prendi per il naso.
Supponi che la sequenza di interazione dell'interfaccia a linea di comando sia:
1. Inserire nome (attende l'inserimento)
2. Inserire cognome (attende l'inserimento)
La finestra prima raccoglie tutti i dati in un colpo solo, poi lancia il programma a linea di comando. Aspetta che questo dica: "Inserire nome" e poi risponde con il valore del campo di testo che contiene il nome. A quel punto il programma chiederà "Inserire cognome" e tu gli rifili il cognome.
In pratica fai un bot: gli rifili tutti i dati in un colpo e quello risponde automaticamente alle richieste del precedente programma.
"lancia il programma a linea di comando" in senso vago. Diciamo che potresti ottenere una cosa del genere:
public void quandoLutentePremeAccetta() {
ConsoleBOT bot = new ConsoleBOT(ilPacchettoDiDati);
bot.scofanatiIFlussiIO();
try {
ApplicazioneALineaDiComando.main(eventualiArgomentiRichiesti);
} finally {
bot.rilasciaIFlussiIO();
}
}
Internamente ConsoleBOT si mette in ascolte per i messaggi che arrivano nel "fu" System.out (che con un Pipe ConsoleBOT farà diventare "il suo in") e risponde.
Ma è solo un'idea.
franksisca
19-10-2006, 16:54
Ma è solo un'idea.
per'altro buona :D :D :D :D
comunque è molto semplice....inizia a lavorarci e ti renderai conto da solo di quanto lo sia;)
Andrea16v
19-10-2006, 19:20
Penso di aver capito il concetto che c'è dietro, ma la struttura che hai abozzato per costruire un'implementazione mi risulta totalmente oscura..
Un oggetto ConsoleBOT è una sorta di "buffer intelligente per i flussi IO" ?
Penso mi convenga prima fare un po' di pratica mastruzzando qualche programma già fatto, hai mai incocciato strada facendo qualcosa di simile a quello che vorrei fare? Magari provo a piegarlo al mio scopo, almeno non devo costruire anche la grafica da zero.. :)
Bene. Siore e siori, non c'è trucco non c'è inganno, vado a dimostrare l'arcano. Donne incinte, bambini, vecchi, deboli di cuore o di stomaco son pregati di lasciare il tendone. Praticamente restiamo in due :D.
Allora, facciamo finta che il programma da linea di comando sia questo pezzo d'antologia:
import java.util.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
/** Chiede un nome, chiede un cognome e li salva su un file xml*/
public class OProgrammone {
public static void main(String[] args) {
java.io.PrintStream out = System.out;
Scanner in = new Scanner(System.in);
out.println("O' Programmone, v. 1.0");
out.println("Inserire un nome");
String nome = in.nextLine();
out.println("Inserire un cognome");
String cognome = in.nextLine();
out.println("O' Programmone genera il file XML...");
try {
salva(nome, cognome);
out.println("Tutto è andato bene");
} catch(Exception ex) {
out.println("Errore nel salvataggio");
}
}
private static void salva(String nome, String cognome) throws Exception {
Document doc = DocumentBuilderFactory.newInstance().
newDocumentBuilder().newDocument();
Node root = doc.createElement("persona");
Node name = doc.createElement("nome");
Node nameValue = doc.createTextNode(nome);
Node surname = doc.createElement("cognome");
Node surnameValue = doc.createTextNode(cognome);
root.appendChild(name);
name.appendChild(nameValue);
root.appendChild(surname);
surname.appendChild(surnameValue);
String fileName = nome + "_" + cognome + ".xml";
java.io.File file = new java.io.File(fileName);
DOMSource source = new DOMSource(root);
StreamResult result = new StreamResult(file);
TransformerFactory.newInstance().newTransformer().
transform(source, result);
}
}
Fa poco ma lo fa male. Stampa una stringa di benvenuto, chiede un nome, chiede un cognome e sputa un file xml. Un programma reale sarà più complicato ma abbiate pietà :cry:
Bene! Le informazioni collezionate dal programma sono due stringhe, una per il nome e una per il cognome. Ora fingiamo di avere questi dati e di darli a un BOT che li rifili al programma secondo le sue esigenze. Questo è il pacchetto di dati che diamo al BOT:
public class BOTData {
private final String name;
private final String surname;
public BOTData(String name, String surname) {
this.name = name;
this.surname = surname;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
}
Dopo profonde meditazioni ho dedotto che la bozza aveva il bozzo. La faccio breve, il BOT deve essere asincrono. Per poter comunicare con l'interfaccia utente, il BOT ha bisogno di tutto un lavoro che noi ci giriamo dall'altra parte e facciamo così:
public interface BOTListener {
void botComm(String message);
}
Ottimo! Ora tocca al BOT. Che deve fare due cose. La prima è far diventare System.in un "out" e System.out un "in". Questo è facile: il programma scrive su System.out e il BOT deve leggere da System.out, per sapere cosa voglia il programma. Per il magheggio basta un Pipe. Di magheggi dobbiamo farne due, una per System.in e una per System.out.
import java.io.*;
import java.nio.channels.*;
import java.util.*;
public class BOTProgrammone {
private Scanner botIn;
private PrintStream botOut;
private InputStream systemIn;
private PrintStream systemOut;
private BOTData data;
private BOTListener listener;
public BOTProgrammone(BOTData data, BOTListener listener) {
this.data = new BOTData(data.getName(), data.getSurname());
this.listener = listener;
}
public void catturaFlussiStandard() throws IOException {
systemIn = System.in;
systemOut = System.out;
/* Crea il flusso in uscita per scrivere sul flusso
in entrata del programma */
Pipe pipeIn = Pipe.open();
InputStream in = Channels.newInputStream(pipeIn.source());
PrintStream out =
new PrintStream(Channels.newOutputStream(pipeIn.sink()));
System.setOut(out);
botIn = new Scanner(in);
/* Crea il flusso in entrata per leggere dal flusso
in uscita del programma */
Pipe pipeOut = Pipe.open();
in = Channels.newInputStream(pipeOut.source());
out = new PrintStream(Channels.newOutputStream(pipeOut.sink()));
System.setIn(in);
botOut = out;
}
public void liberaFlussiStandard() {
System.setOut(systemOut);
System.setIn(systemIn);
}
/* Legge una linea dal flusso in entrata (che è stato collegato al
flusso in uscita del sistema) */
private String leggiLinea() {
return botIn.nextLine();
}
/* Scrive una linea sul flusso in uscita (che è stato collegato al
flusso in entrata del sistema) */
private void inviaLinea(String v) {
botOut.println(v);
}
/* Da qui in poi il BOT legge e scrive */
public void attiva() {
new Thread(runner).start();
}
private Runnable runner = new Runnable() {
public void run() {
String line = leggiLinea();//O' programmone v. 1.0
line = leggiLinea();//Inserire il nome
inviaLinea(data.getName());//invia il nome...
line = leggiLinea();//Inserire il cognome
inviaLinea(data.getSurname());//invia il cognome
line = leggiLinea();//legge l'esito...e se ne frega :D
listener.botComm("Fine");
}
};
}
E' un po' sbrigativo ma è un esempio.
Non resta che l'interfaccia. Se hai dimestichezza con swing i commenti sono superflui. Altrimenti ci vuole tutto il forum per spiegarlo. Io molto pigro, spero nella prima condizione :D
import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
public class InterfacciaSwing implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new InterfacciaSwing());
}
private JTextComponent nameField, surnameField;
private Window frame;
private BOTProgrammone bot;
public void run() {
JFrame frame = new JFrame("InterfacciaSwing");
frame.setContentPane(creaContenuto());
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
this.frame = frame;
}
private Container creaContenuto() {
JLabel nameLabel = new JLabel("Nome", JLabel.RIGHT);
JLabel surnameLabel = new JLabel("Cognome", JLabel.RIGHT);
JTextField nameField = new JTextField(10);
JTextField surnameField = new JTextField(10);
JButton accept = new JButton("ok");
JButton cancel = new JButton("cancel");
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
buttonPanel.add(cancel);
buttonPanel.add(accept);
accept.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
accept();
}
});
cancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cancel();
}
});
GridBagLayout layout = new GridBagLayout();
GridBagConstraints lim = new GridBagConstraints();
lim.insets = new Insets(4, 4, 4, 4);
lim.fill = GridBagConstraints.HORIZONTAL;
lim.gridx = 0;
lim.gridy = 0;
layout.setConstraints(nameLabel, lim);
lim.gridx = 1;
lim.gridy = 0;
layout.setConstraints(nameField, lim);
lim.gridx = 0;
lim.gridy = 1;
layout.setConstraints(surnameLabel, lim);
lim.gridy = 1;
lim.gridx = 1;
layout.setConstraints(surnameField, lim);
lim.gridx = 0;
lim.gridy = 2;
lim.gridwidth = 2;
layout.setConstraints(buttonPanel, lim);
this.nameField = nameField;
this.surnameField = surnameField;
JPanel container = new JPanel(layout);
container.add(nameLabel);
container.add(surnameLabel);
container.add(nameField);
container.add(surnameField);
container.add(buttonPanel);
return container;
}
private void accept() {
BOTData data = new BOTData(nameField.getText(), surnameField.getText());
bot = new BOTProgrammone(data, botListener);
try {
bot.catturaFlussiStandard();
bot.attiva();
OProgrammone.main(null);
} catch(Exception ex) {
ex.printStackTrace();
}
}
private void cancel() {
frame.dispose();
}
private void notifyEnd() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(frame, "Fine programma");
}
});
}
private BOTListener botListener = new BOTListener() {
public void botComm(String message) {
if(message.equals("Fine")) {
bot.liberaFlussiStandard();
notifyEnd();
}
}
};
}
Ecco, l'idea era questa. Poi vedi se ti sconfiffera.
Andrea16v
19-10-2006, 23:19
Da paura!!! :eek:
Ho compilato tutto e funziona perfettamente, era quello che mi ci voleva!
Per capirlo un po' bene mi ci vorrà del tempo, in quanto non ho dimestichezza con le Swing e con molte altre cose, che voglio imparare in questi giorni, quindi colgo l'occasione.. :)
Inoltre vorrei provare a cambiare il formato di output da xml a CSV su file .txt, che è quello che usa il mio programma. Ci sono dei vantaggi a usare l'xml?
Da paura!!! :eek:
:D Ok, non è bellissimo ma che addirittura faccia spavento... :D
Se per CSV intendi valori separati da virgola e il tuo programma usa questo formato direi che è meglio fare qualche esperimento con CSV, giusto per acquisire dimestichezza nel manipolare quel formato. Sai, una cosa tira l'altra, magari oggi fai l'interfaccia e domani ti vien voglia di aggiungere qualcosa che manipoli il prodotto del programma...
Andrea16v
25-10-2006, 20:56
Sto andando avanti a fare qualche esperimento, sono ancora lontano da capire tutto, comunque grazie al tuo programma posso già usarlo.. :)
Una domanda: per memorizzare in una variabile in automatico la data, senza dover farla inserire dall'utente, esiste qualche metodo?
Es. per ogni persona inserita voglio una var. di istanza con il timedate di quando è stata aggiunta alla lista, posso inizializzarla sulla data corrente direttamente dal costruttore predefinito?
Sì, puoi inserire nel costruttore l'inizializzazione del "tempo" di creazione. Basta un:
class Qualcosa {
private java.util.Date tempoDiCreazione;
public Qualcosa() {
tempoDiCreazione = new java.util.Date();
}
}
Andrea16v
25-10-2006, 22:18
Sì, puoi inserire nel costruttore l'inizializzazione del "tempo" di creazione. Basta un:
class Qualcosa {
private java.util.Date tempoDiCreazione;
public Qualcosa() {
tempoDiCreazione = new java.util.Date();
}
}
Grazie, ora mi guardo questa classe Date, sperando che abbia un metodo per scegliere in che formato restituire la data, io per esempio sono interessato a stampare solo giorno, mese ed eventualmente anno, senza orario. :)
Butta l'occhio a SimpleDateFormat.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.