Gradle+Java, immagini non trovate

Stato
Discussione chiusa ad ulteriori risposte.

Ibernato

Utente Èlite
4,328
2,047
OS
Windows 10 Pro / Ubuntu 22.04
Le cose stanno in questo modo:
avevo bisogno di una classe che estendesse la classe standard dei pulsanti in modo che, oltre al normale pulsante:
  • venisse associato automaticamente il testo del pulsante stesso (per esempio "Search")
  • venisse associato automaticamente un tooltip (la "nuvoletta" che descrive la funzione di un bottone quando il mouse lo sorvola)
  • un'immagine;
Allo scopo ho definito una classe chiamata BottoneGUI;
dentro tale classe c'è una enumerazione che descrive il tipo di bottone. Il codice dell'enumerazione è il seguente:
Java:
// tipo di bottone
    enum TipoBottone {
        RICERCA("Search","Search (cerca)","search.png"),
        AGGIUNGI("Add","Add component (aggiungi componente)","add.png"),
        DESELEZIONA("Deselect","Deselect row (deseleziona riga)","remove-selection.png");

        private final String tipo; // Search, add ecc.
        private final String trad; // tooltip
        private final String icon; // icona PNG

        // directory delle immagini usate nella GUI
        private static final String imgDir = "/img/bottoni/";

        /**
         * Costruttore di TipoBottone.
         *
         * @param t il tipo
         * @param u la traduzione in italiano di <tt>t</tt>
         * @param i il nome dell'icona
         */
        private TipoBottone(String t, String u, String i){
            tipo = t;
            trad = u;
            icon = i;
        }

        /**
         * @return il tipo (RICERCA ecc.)
         */
        public String getTipo(){
            return tipo;
        }

        /**
         * @return un tooltip da associare ad un bottone
         */
        public String getToolTip(){ return trad; }

        /** Ricava il percorso di directory dell'icona del bottone.
         *
         * @return una stringa con il percorso di directory dell'icona di questo bottone
         */
        public String getIconPath(){
            StringBuilder sb = new StringBuilder(imgDir);
            sb.append(icon); // icona
            return sb.toString();
        } // FINE metodo getIconPath

    } // FINE enum TipoBottone

Come vedete dentro l'enumerazione c'è una stringa imgDir dove è memorizzato il percorso assoluto dell'icona;
riferendomi a questa parte: RICERCA("Search","Search (cerca)","search.png")
in parole povere, si crea questo nuovo tipo di bottone con
Java:
BottoneGUI bg = new BottoneGUI(BottoneGUI.TipoBottone.RICERCA);
viene
  • creato un bottone "Search" (è la prima stringa della costante che vedete nell'enumerazione) --> funziona
  • associato il tooltip Search (cerca)" (è la seconda stringa nella definizione dell'enumerazione) --> funziona
  • idealmente messa l'immagine search.png (la terza stringa nell'enumerazione), che dovrebbe essere prelevata dal percorso /img/bottoni/search.png --> è questo che non funziona, e la causa ora capisco che è il metodo che ho usato
Ragionando a posteriori mi rendo conto che ho fatto una pessima gestione delle immagini mettendo i percorsi in stringhe, quindi dovrò riscrivere parecchio codice, ma per controllarlo dovrò prima capire come recuperare le immagini. Il codice completo della classe BottoneGUI è il seguente:
Java:
package blam;

import javafx.scene.control.Button;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.util.Duration;

public class BottoneGUI extends Button {
    // tipo di bottone
    enum TipoBottone {
        RICERCA("Search","Search (cerca)","search.png"),
        AGGIUNGI("Add","Add component (aggiungi componente)","add.png"),
        DESELEZIONA("Deselect","Deselect row (deseleziona riga)","remove-selection.png");

        private final String tipo; // Cerca, aggiungi ecc
        private final String trad; // tooltip
        private final String icon; // icona PNG

        // directory delle immagini usate nella GUI
        private static final String imgDir = "/img/bottoni/";

        /**
         * Costruttore di TipoBottone.
         *
         * @param t il tipo
         * @param u la traduzione in italiano di <tt>t</tt>
         * @param i il nome dell'icona
         */
        private TipoBottone(String t, String u, String i){
            tipo = t;
            trad = u;
            icon = i;
        }

        /**
         * @return il tipo (RICERCA ecc.)
         */
        public String getTipo(){
            return tipo;
        }

        /**
         * @return un tooltip da associare ad un bottone
         */
        public String getToolTip(){ return trad; }

        /** Ricava il percorso di directory dell'icona del bottone.
         *
         * @return una stringa con il percorso di directory dell'icona di questo bottone
         */
        public String getIconPath(){
            StringBuilder sb = new StringBuilder(imgDir);
            sb.append(icon); // icona
            return sb.toString();
        } // FINE metodo getIconPath

    } // FINE enum TipoBottone

    private static final double buttonH = 24.0;
    // caratteristiche pulsante
    private static final String family = "Verdana";
    private static final double size = 12.0;
    private static final FontWeight fw = FontWeight.SEMI_BOLD;
    private static final FontPosture fp = FontPosture.REGULAR;
    private static final Font f = Font.font(family, fw, fp, size);
    // font per tooltip
    private static final Font fDesc = Font.font("Verdana", FontWeight.MEDIUM, 12);
    private Image img;
    private ImageView imgv;

    public BottoneGUI(TipoBottone tb){
        super();
        setFont(f);
        try {
            img = new Image(getClass().getResourceAsStream(tb.getIconPath()));
            imgv = new ImageView(img);
            imgv.setFitWidth(buttonH); // ridimensiona secondo la base prefissata
            imgv.setPreserveRatio(true);
            imgv.setSmooth(true);
            imgv.setCache(true);
            setGraphic(imgv);
        } catch (NullPointerException ex) { // immagine non trovata
            //setText(testo);
        }
        setText(tb.getTipo());
        Tooltip ttip = new Tooltip(tb.getToolTip());
        ttip.setFont(fDesc);
        ttip.setShowDelay(Duration.seconds(0.2)); // dopo quanto tempo in secondi appare il tooltip
        setTooltip(ttip);
    } // FINE costruttore
} // FINE BottoneGUI
Prova a leggere anche questo thread https://stackoverflow.com/questions/26328040/intellij-idea-getclass-getresource-return-null
 

Skills07

Head of Development
Staff Forum
Utente Èlite
35,404
11,483
CPU
Ryzen 7 5800x
Dissipatore
Deepcool gammax l240 v2
Scheda Madre
Aourus Gigabyte X-470 ultra gaming
HDD
Samsung Evo 850 250gb ,2tb x 2 toshiba P300, 256 Ssd silicon power, Samsung evo plus 1tb nvme
RAM
32 gb G.Skill Tridentz 3200 mhz RGB
GPU
Zotac - RTX 3070 Ti Trinity OC
Monitor
ASUS TUF QHD 165hz IPS, ITEK GCF 165hz IPS QHD, Samsung Curvo 144hz QHD
PSU
Seasonic Focus 650w 80 platinum
Case
Coolermaster h500p
Periferiche
Asus Tuf Wireless
Net
Fibra Tim 100mb
OS
Windows 10 Pro 64 bit
@BAT hai già provato cosi:

Java:
Image image = new Image("/path/to/your/resource/in/your/project");
ImageView imageView = new ImageView(image);

e poi

URL url = getClass().getResource("/drawIcon.png");
Image image = ImageIO.read(url);

senno' prova a dare un'occhiata qui

 

BAT

Moderatore
Staff Forum
Utente Èlite
22,888
11,546
CPU
1-Neurone
Dissipatore
Ventaglio
RAM
Scarsa
Net
Segnali di fumo
OS
Windows 10000 BUG
Image image = new Image("/path/to/your/resource/in/your/project");
questa istruzione causa direttamente un runtime error, l'applicazione neanche parte
senno' prova a dare un'occhiata qui
come già avevo detto nel mio primo post di risposta, le mie ricerche in internet mi avevano già portato a quella pagina prima ancora che postassi questa discussione. Difatti penso che la soluzione stia lì, ossia definire un metodo come quello che mi avevi detto
public static Image createImage(Object context, String resourceName) { URL _url = context.getClass().getResource(resourceName); return new Image(_url.toExternalForm()); }
ma non so cosa passare come parametro context: come si ricava il context da passare? risolto quello penso e spero che la questione si risolva.
Il context, a quanto ricordo, è un oggetto da ricavare in base all'ambiente che si sta usando, per esempio nello sviluppo web lato server con Java Servlet il context è contenuto nei file di configurazione dell'application server (che va "interrogato"). Qui il concetto dovrebbe essere lo stesso: come ricavo il context dall'ambiente di building (Gradle) per poter invocare quel metodo costruttore di immagini. E' il context che mi serve almeno per provare.
[EDIT]
Ad ogni modo ho provato semplicemente a costruire un oggetto immagine, perfino senza aggiungerlo alla finestra da visualizzare;
l'istruzione è
Image img = new Image(percorsoFileImmagine);
e come percorso ho provato TUTTI questi in modo da non saltare nessuna possibilità:
Codice:
/src/main/resources/img/title.png
src/main/resources/img/title.png
/main/resources/img/title.png
main/resources/img/title.png
/resources/img/title.png
resources/img/title.png
/img/title.png
img/title.png
non ne funziona nessuno, questa semplice istruzione causa una runtime exception:
java.lang.RuntimeException: Exception in Application start method
se commento l'istruzione il programma di prova parte (una semplice finestra vuota 600x400px
sospetto ci sia altro, o nella definizione del file module-info.java oppure qualcosa da impostare nel file build.gradle
 

Skills07

Head of Development
Staff Forum
Utente Èlite
35,404
11,483
CPU
Ryzen 7 5800x
Dissipatore
Deepcool gammax l240 v2
Scheda Madre
Aourus Gigabyte X-470 ultra gaming
HDD
Samsung Evo 850 250gb ,2tb x 2 toshiba P300, 256 Ssd silicon power, Samsung evo plus 1tb nvme
RAM
32 gb G.Skill Tridentz 3200 mhz RGB
GPU
Zotac - RTX 3070 Ti Trinity OC
Monitor
ASUS TUF QHD 165hz IPS, ITEK GCF 165hz IPS QHD, Samsung Curvo 144hz QHD
PSU
Seasonic Focus 650w 80 platinum
Case
Coolermaster h500p
Periferiche
Asus Tuf Wireless
Net
Fibra Tim 100mb
OS
Windows 10 Pro 64 bit
forse non ti serve il context ma puo bastare semplicemente un'istruzione come questa:

Java:
this.getClass().getClassLoader().getResource("nome della risorsa da usare");
oppure

this.getClass().getResource("/risorsa")

Se si passa un percorso di risorsa che non inizia con un"/" a "Class.getResource()", il caricatore di classi cerca la risorsa nel pacchetto della classe. Non alla radice.
 

Ibernato

Utente Èlite
4,328
2,047
OS
Windows 10 Pro / Ubuntu 22.04
Io ho letto anche che bisogna assegnare la cartella delle risorse come root e poi fare l'istruzione di @Skills07
this.getClass().getClassLoader().getResource("nome della risorsa da usare");
 

BAT

Moderatore
Staff Forum
Utente Èlite
22,888
11,546
CPU
1-Neurone
Dissipatore
Ventaglio
RAM
Scarsa
Net
Segnali di fumo
OS
Windows 10000 BUG
Il seguente codice dà Runtime error:
Codice:
URL s = this.getClass().getClassLoader().getResource("/img/title.png");
        BufferedImage img = null;
        try {
            img = ImageIO.read(s);
        } catch (IOException e) {
            e.printStackTrace();
        }
stessa cosa se cambio il path con
"/resources/img/title.png"
"/main/resources/img/title.png"
"/src/main/resources/img/title.png"
Inoltre ho dovuto usare BufferedImage (invece di image) altrimenti neanche lo compila
ugualmente se cambio metodo e uso
URL s = this.getClass().getResource("/img/title.png");
anche cambiando i path con quelli pecedentemente detti
 

Ibernato

Utente Èlite
4,328
2,047
OS
Windows 10 Pro / Ubuntu 22.04
Il seguente codice dà Runtime error:
Codice:
URL s = this.getClass().getClassLoader().getResource("/img/title.png");
        BufferedImage img = null;
        try {
            img = ImageIO.read(s);
        } catch (IOException e) {
            e.printStackTrace();
        }
stessa cosa se cambio il path con
"/resources/img/title.png"
"/main/resources/img/title.png"
"/src/main/resources/img/title.png"
Inoltre ho dovuto usare BufferedImage (invece di image) altrimenti neanche lo compila
ugualmente se cambio metodo e uso
URL s = this.getClass().getResource("/img/title.png");
anche cambiando i path con quelli pecedentemente detti
Prova a marcare la cartella res come root
 

BAT

Moderatore
Staff Forum
Utente Èlite
22,888
11,546
CPU
1-Neurone
Dissipatore
Ventaglio
RAM
Scarsa
Net
Segnali di fumo
OS
Windows 10000 BUG
Prova a marcare la cartella res come root
già lo è, lo fa il progetto in modo automatico, a meno che non bisogna editare la configurazione standard;
marcatura.png
tra l'altro lì dentro c'è anche un .jar (driver di database) che riconosce senza problemi
 

Andretti60

Utente Èlite
6,440
5,091
@pabloski scommetto che hai la soluzione...? o magari @Andretti60 ? @Ibernato ?
Scusa, non sono stato notificato e solo adesso ho letto questa discussione e solo per curiosità, la avevo saltata in tronco in quanto non conosco Gradle. Mi spiace, non posso aiutarti. Vedo però che in Java come in C# usare le risorse non è così banale, cosa che mi manda in bestia in quanto è una cosa essenziale e quindi “dovrebbe” esserlo.
[rant mode OFF]
 

BAT

Moderatore
Staff Forum
Utente Èlite
22,888
11,546
CPU
1-Neurone
Dissipatore
Ventaglio
RAM
Scarsa
Net
Segnali di fumo
OS
Windows 10000 BUG
usare le risorse non è così banale, cosa che mi manda in bestia in quanto è una cosa essenziale e quindi “dovrebbe” esserlo
infatti: esiste una cartella predefinita che dovrebbe funzionare punto e basta ma per qualche motivo non è così.
La cosa curiosa è che in quella cartella c'è un driver di database che viene usato correttamente e senza neanche passare percorsi di risorsa. con le immagini invece no, probabilmente c'è qualcosa che che non so, in fondo è la prima volta che lo uso (e poi la colpa è SEMPRE del programmatore ?);
ho riscritto parte del codice in modo da rendere impossibile la presenza di nullpointer in caso i file immagini non vengano trovati e l'applicazione funziona ugualmente (con una GUI che come potrai immaginare fa abbastanza schifo, però funziona... incredibile come l'assenza di immagini renda il tutto più "faticoso" per l'utente).
Ma la cosa che mi fa più imbestialire è un'altra:
mi ero sbattuto a rifare il progetto sotto Gradle e ora, a parte le immagini, mi ritrovo lo stesso problema che mi aveva spinto a farlo: l'artifact (cioè il prodotto finale della compilazione) non viene creato! il messaggio di errore dice che manca una componente non distribuita nel JDK per cui in assenza di essa va usato uno tra Gradle, Maven o altro! BUUUUAAAAAH ????
 

BAT

Moderatore
Staff Forum
Utente Èlite
22,888
11,546
CPU
1-Neurone
Dissipatore
Ventaglio
RAM
Scarsa
Net
Segnali di fumo
OS
Windows 10000 BUG
@Andretti60 @Ibernato @Skills07
non ci posso credere adesso mi riconsoce i file di immagine; non era un errore di programmazione come credevo, vengono caricate anche dal metodo getClass().getResourceAsStream(pathCompletoImmagine), ma prima era necessario eseguire in Gradle un task chiamato jlink... 8 giorni a cercare un errore che non c'era!
Mi rimane da risolvere il problema della generazione dell'artifact (il benedetto file che sulla carta è l'eseguibile) su cui fare doppio click;
a quel punto dovrò anche verificare che le immagini vengano davvero caricate dall'interno di un file .jar (se non accade userò uno dei metodi che mi avete linkato).
[EDIT]
sono stato troppo ottimista, ho provato a rifare i passi e misteriosamente le immagini sono sparite di nuovo! ?
 
Ultima modifica:

BAT

Moderatore
Staff Forum
Utente Èlite
22,888
11,546
CPU
1-Neurone
Dissipatore
Ventaglio
RAM
Scarsa
Net
Segnali di fumo
OS
Windows 10000 BUG
Ho risolto il problema del caricamente delle immagini, causato da una sfortunata combinazione di:
  • IDE (Intellij IDEA): gli aggiornamenti dell'ambiente causavano strani errori casuali nel suo funzionamento, risolto reinstallandolo da zero;
  • un file chiamato module-info.java che richiedeva una modifica: i progetti modulari devono averlo per forza ma la versione standard creata dall'IDE va modificata manualmente a seconda del progetto.
  • file di configurazione di Gradle: la versione standard va modificata manualmente per permettere la distribuzione del file .zip contenente il tool che si vuole rilasciare al pubblico.
Il codice Java di caricamento delle immagini è sempre stato corretto sin dall'inizio (le modifiche al volo erano inutili), lo riporto per completezza:
Java:
/** imgDir = percorso immagine sottoforma di stringa, esempio
 * imgDir = "/img/bottoni/search.png";
 * un percorso assoluto che inizia con "/" in modo che le immagini vengano ricercate auomaticamente a partire
 * dalla directory resources considerata root (radice) dalla configurazione dell'ambiente.
*/
Image img = new Image(getClass().getResourceAsStream(iconDir));
ImageView imgv = new ImageView(img);
/** oppure, se preferite le versioni monoriga
* ImageView imgv = new ImageView(new Image(getClass().getResourceAsStream(iconDir)));
*/

al file module-info.java vanno aggiunte 2 righe che specificano il progetto:

Java:
module bblam.bblam {
    requires javafx.controls; // necessario per le JavaFX
    requires javafx.fxml; // necessario per le JavaFX per l'uso di fxml
 
    opens bblam.bblam; // come il nome del modulo: era questo che mancava per le immagini
    exports bblam.bblam; // come il nome del modulo: era questo che mancava per le immagini
  /** invece se si usa fxml
    *  opens bblam.bblam to javafx.fxml;
    */
}

Infine va modificato il file di configurazione per Gradle per permettere la costruzione e distribuzione del tool, le istruzioni per farlo sono le seguenti https://openjfx.io/openjfx-docs/#IDE-Intellij nella sezione Runtime images

Il problema delle immagini è risolto, sfortunatamente se n'è aperto un altro più grosso che richiederà di nuovo chissà quale modifica banale (ma che non conosco) nei file di configurazione. Se continuo di questo passo il tool verrà concluso per le olimpiadi di Brisbane 2032.

Chiudo la discussione.
 
  • Mi piace
Reazioni: Moffetta88
Stato
Discussione chiusa ad ulteriori risposte.

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!