Jak wyświetlić grafikę z dysku za pomocą JFileChooser w obrębie GUI?

0

Witajcie :)

Wybaczcie proszę, jeśli jest to bardzo proste pytanie. Jestem świeżo po książce "Java, ćwiczenia praktyczne" Marcina Lisa, wydanie II. Jak to zwykle z takimi książkami bywa - wprowadzą w podstawy podstaw, jednak informacje z tego typu książek nie wprowadzają w nic głębszego i po ukończeniu publikacji nie potrafi się w zasadzie nic konkretnego ;-)

Chciałbym napisać prostą przeglądarkę obrazów - klika się w menu Otwórz, wybiera za pomocą JFileChoosera grafikę i ta ładuje się w obrębie GUI. GUI już jest - niestety nie wiem jak sprawić, aby po wybraniu grafiki i kliknięciu OK, ta się załadowała. Oczywiście przydałoby się jeszcze jakieś rozróżnienie jakie formaty program obsługuje, a jakie nie (aby do programu nie szło załadować plików xml czy doc, a tylko graficzne np. o konkretnym rozszerzeniu) - niemniej to może na później.

Kod:

import javax.swing.*;
import java.awt.event.*;


public class KImage extends JFrame implements ActionListener {
	public KImage() {
		super();
		setTitle("KImage browser");
		setSize(640, 480);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JMenuBar menuBar = new JMenuBar();
		JMenu menu1 = new JMenu("Plik");
		JMenu menu2 = new JMenu("Pomoc");
		
		JMenuItem menu1Item1 = new JMenuItem("Otwórz");
		JMenuItem menu1Item2 = new JMenuItem("Zamknij");
		
		JMenuItem menu2Item1 = new JMenuItem("O programie");
		
		menu1Item1.addActionListener(this);
		menu1Item2.addActionListener(this);
		menu2Item1.addActionListener(this);
		
		menu1Item1.setActionCommand("Open");
		menu1Item2.setActionCommand("Close");
		menu2Item1.setActionCommand("About");
		
		menu1.add(menu1Item1);
		menu1.add(menu1Item2);
		menu2.add(menu2Item1);
		
		setJMenuBar(menuBar);
		menuBar.add(menu1);
		menuBar.add(menu2);
		
	}
	
	public void actionPerformed(ActionEvent e) {
		String cmd = e.getActionCommand();
		if("About".equals(cmd)){
			JOptionPane.showMessageDialog(this, "KImage - simple image viewer.", "Informacja!", JOptionPane.INFORMATION_MESSAGE);
		}
		else if("Close".equals(cmd)){
			dispose();
		}
		else if("Open".equals(cmd)){
			JFileChooser fc = new JFileChooser();
			if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				fc.getSelectedFile();
			}
		}
	}
	public static void main (String args[]) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new KImage();
			}
		});
	}
}

Prosiłbym tylko, jeśli to nie kłopot, o jakąś prostą odpowiedź - przy skomplikowanym kodzie bez objaśnienia, mógłbym się zagubić :( Aczkolwiek być może jest gdzieś jakieś konkretne how-to poświęcone temu zagadnieniu?

Dziękuję, jeśli ktoś zechciałby nakierować mnie na rozwiązanie.

1

Jeśli chodzi o wyświetlenie grafiki, to masz dwie proste opcje:

  1. Ustawić JLabel z pustym tekstem oraz ikoną w postaci obrazu.
  2. Wczytać obraz jako BufferedImage (ImageIO), zachować w pamięci i nadpisać metodę "paintComponent" w JPanel tak, aby wyświetlała obraz.

Obie metody są poprawne, kwestia preferencji. Dodatkowo ImageIO powinno rzucić wyjątkiem, jeśli wczytywany plik nie jest obrazem.

1

Proste rozwiązanie, wszystkie grafiki maja taki sam rozmiar.

  1. Do klasy KImage dodaj pole img klasy BufferedImage i usuń instrukcję setSize(...).
  2. Do głównego okna dodaj JPanel (a dokładniej to obiekt klasy dziedziczącej po JPanel - niech ta klasa nazywa się Panelik i niech będzie klasą wewnętrzną w klasie KImage, unikniesz przekazywania parametrów między klasami).
  3. Kod klasy Panelik:
    class Panelik extends JPanel
    {
        public PanelRysunku()
        {
            setPreferredSize(new Dimension(640,480));
        }
        //--------------------
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);  
            if(img != null)
            {
                g.drawImage(img,0,0,null);
            }
        }
    }
  1. Niech fc będzie polem w klasie KIMage tworzonym jednokrotnie. Użytkownik, zmuszony do wielokrotnego wędrowania po drzewie katalogów przeklnie Cie. Wybrany ostatnio katalog musi być zapamiętany.
  2. Uzupełnij tworzenie obiektu fc o filtr:
FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & GIF & PNG Images", "jpg", "gif", "png");
fc.setFileFilter(filter);
if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
{
    img = null;
    try
    {
        img = ImageIO.read(fc.getSelectedFile());
    } 
    catch (IOException ex)
    {
        JOptionPane.showMessageDialog(this,ex.toString(),"",JOptionPane.ERROR_MESSAGE);
    }
    repaint();
}
0

Dziękuję za odpowiedzi :)

Postaram się dowiedzieć o tych niuansach, aby móc ich używać. Przyznam, że niewiele mnie jednak ta książka nauczyła, w tym nie miałem pojęcia ile rzeczy trzeba po drodze zaimportować - dobrze, że Eclipse jest na tyle mądre, aby podpowiadać import. Będę musiał chyba zaopatrzyć się w szerszą literaturę, gdyż inaczej chyba nie ujadę zbyt daleko, na to wygląda :-(

Próbowałem coś namodzić, jednak chyba nie bardzo coś z tego wyszło:

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;

import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;


public class KImage extends JFrame implements ActionListener {
	BufferedImage img;
	public KImage() {
		
		super();
		setTitle("KImage browser");
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JPanel Panelik = new JPanel(new BorderLayout());
		
		JMenuBar menuBar = new JMenuBar();
		JMenu menu1 = new JMenu("Plik");
		JMenu menu2 = new JMenu("Pomoc");
		
		JMenuItem menu1Item1 = new JMenuItem("Otwórz");
		JMenuItem menu1Item2 = new JMenuItem("Zamknij");
		
		JMenuItem menu2Item1 = new JMenuItem("O programie");
		
		menu1Item1.addActionListener(this);
		menu1Item2.addActionListener(this);
		menu2Item1.addActionListener(this);
		
		menu1Item1.setActionCommand("Open");
		menu1Item2.setActionCommand("Close");
		menu2Item1.setActionCommand("About");
		
		menu1.add(menu1Item1);
		menu1.add(menu1Item2);
		menu2.add(menu2Item1);
		
		setJMenuBar(menuBar);
		menuBar.add(menu1);
		menuBar.add(menu2);
		menuBar.add(Panelik);
		
		 class Panelik extends JPanel
		    {
		        public Panelik(BufferedImage img)
		        {
		            setPreferredSize(new Dimension(640,480));
		        }
		        //--------------------
		        public void paintComponent(Graphics g)
		        {
		            super.paintComponent(g);  
		            if(img != null)
		            {
		                g.drawImage(img,0,0,null);
		            }
		        }
		    }
		
	}
	
	
	public void actionPerformed(ActionEvent e) {
		String cmd = e.getActionCommand();
		if("About".equals(cmd)){
			JOptionPane.showMessageDialog(this, "KImage - simple image viewer.", "Informacja!", JOptionPane.INFORMATION_MESSAGE);
		}
		else if("Close".equals(cmd)){
			dispose();
		}
		else if("Open".equals(cmd)){
			JFileChooser fc = new JFileChooser();
			FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & GIF & PNG Images", "jpg", "gif", "png");
			fc.setFileFilter(filter);
			if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
			{
			    img = null;
			    try
			    {
			        img = ImageIO.read(fc.getSelectedFile());
			    } 
			    catch (IOException ex)
			    {
			        JOptionPane.showMessageDialog(this,ex.toString(),"",JOptionPane.ERROR_MESSAGE);
			    }
			    repaint();
			}
			}
		}
	public static void main (String args[]) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new KImage();
			}
		});
	}
}

Na ten moment pochrzaniłem na tyle, że GUI odpala się w formie samego titlebara i trzeba go zmaksymalizować, FileChooser podpowiada format plików, ale nie są wczytywane.

Cóż - pozostaje mi przewertować internet, sprawdzić portfel i poszukać bardziej wyczerpującej książki o Javie gdyż widzę, że kuleją u mnie podstawy, choćby głupie wczytanie obrazka do okna. A z kolei bez podstaw daleko nie zajadę, bo nie mogę przecież co 5 minut pisać postów na forum :)

1

W poprzednim poście dałem złą deklarację konstruktora klasy Panelik.

  1. Panelik trzeba dodać do okna, nie do MenuBara.
  2. Po dodaniu komponentów do okna należy wywołać metodę pack();
  3. setVisible(true) powinno być ostatnia instrukcją konstruktora - po dodaniu wszystkich komponentów.
  4. Warto wywołać setLocationRelativeTo(null); - okno pojawi się w środku ekranu.
  5. Definicja klasy Panelik była zbyt głęboko.
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
 
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
 
 
public class KImage extends JFrame implements ActionListener {
    BufferedImage img;
    JFileChooser fc = new JFileChooser();
    public KImage() {
 
        super();
        setTitle("KImage browser");
        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(new Panelik());
        pack();
        setLocationRelativeTo(null);
 
        JMenuBar menuBar = new JMenuBar();
        JMenu menu1 = new JMenu("Plik");
        JMenu menu2 = new JMenu("Pomoc");
 
        JMenuItem menu1Item1 = new JMenuItem("Otwórz");
        JMenuItem menu1Item2 = new JMenuItem("Zamknij");
 
        JMenuItem menu2Item1 = new JMenuItem("O programie");
 
        menu1Item1.addActionListener(this);
        menu1Item2.addActionListener(this);
        menu2Item1.addActionListener(this);
 
        menu1Item1.setActionCommand("Open");
        menu1Item2.setActionCommand("Close");
        menu2Item1.setActionCommand("About");
 
        menu1.add(menu1Item1);
        menu1.add(menu1Item2);
        menu2.add(menu2Item1);
 
        setJMenuBar(menuBar);
        menuBar.add(menu1);
        menuBar.add(menu2);
        setVisible(true);
  
    }
 
 
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        if("About".equals(cmd)){
            JOptionPane.showMessageDialog(this, "KImage - simple image viewer.", "Informacja!", JOptionPane.INFORMATION_MESSAGE);
        }
        else if("Close".equals(cmd)){
            dispose();
        }
        else if("Open".equals(cmd)){
            FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & GIF & PNG Images", "jpg", "gif", "png");
            fc.setFileFilter(filter);
            if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
            {
                img = null;
                try
                {
                    img = ImageIO.read(fc.getSelectedFile());
                } 
                catch (IOException ex)
                {
                    JOptionPane.showMessageDialog(this,ex.toString(),"",JOptionPane.ERROR_MESSAGE);
                }
                repaint();
            }
            }
        }
    public static void main (String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new KImage();
            }
        });
    }
    class Panelik extends JPanel
    {
        public Panelik()
        {
            setPreferredSize(new Dimension(640,480));
        }
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);  
            if(img != null)
            {
                g.drawImage(img,0,0,null);
            }
        }
    }    
}

Btw, książka M.Lisa nie podobała mi się.

0

Dziękuję za odpowiedzi :-) Przeanalizuję sobie teraz kod na spokojnie i douczę czego trzeba, aby zrozumieć jak wykorzystywać go w przyszłości.

Mam tylko pytanie co do metody pack(). Doczytałem, iż teorią metody pack() jest "ustalanie rozmiaru okna tak, aby mieściły się w nim wszystkie komponenty. Aktualnie okno nie dostosowuje się do rozmiaru zdjęcia (a może to zdjęcia muszą być skalowane do rozmiaru okna?), przez co zdjęcia się maksymalnie rozciągnięte i najczęściej w oknie programu figuruje mocno rozciągnięty lewy, górny róg zdjęcia. Czy na takie coś stosuje się właśnie dynamiczne skalowanie okna programu do zdjęcia, czy odwrotnie? Chciałbym po prostu wiedzieć, gdzie zacząć szukać dalej.

PS: Forum znam dopiero od dziś, więc dopiero je przewertuję, nie omieszkam tego uczynić, jednakże skoro książka M. Lisa sprawdziła się tylko na krótką metę do pierwszego realnego programu, to w czyją literaturę zainwestować, bądź może jest w sieci jakiś tutorial pisany przez człowieka dla człowieka? Oczywiście jest dokumentacja Oracle, jednak wydaje mi się...zbyt sucha, abym mógł coś z niej wynieść na realnych przykładach.

1

Metoda pack dostosowuje rozmiar okna do rozmiaru komponentów. Twój program zawiera jeden komponent, obiekt klasy Panelik, który ma rozmiar 640x480.

setPreferredSize(new Dimension(640,480));

Masz co najmniej dwa wyjścia:

  • dostosowywać obrazki do wielkości panelu (okna), metoda getScaledInstance(...)
  • dostosowywać panel do obrazków, będzie to trochę skomplikowane dla bardzo dużych obrazków, niemieszczących się na ekranie.
    ... 
    Panelik panelik;
    public KImage() {
 
        super();
        setTitle("KImage browser");
        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        panelik = new Panelik(640,480);
        add(panelik);
        ...
            if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
            {
                img = null;
                try
                {
                    img = ImageIO.read(fc.getSelectedFile());
                    remove(panelik);
                    panelik = new Panelik(img.getWidth(),img.getHeight());
                    add(panelik);
                    pack();
                } 
                catch (IOException ex)
                {
                    JOptionPane.showMessageDialog(this,ex.toString(),"",JOptionPane.ERROR_MESSAGE);
                }
                repaint();
            }
...
    class Panelik extends JPanel
    {
        public Panelik(int width,int height)
        {
            setPreferredSize(new Dimension(width,height));
        }
...

Zajrzyj tu http://zetcode.com/tutorials/javaswingtutorial/ i tu http://docs.oracle.com/javase/tutorial/uiswing/components/index.html

0

Dziękuje za tutoriale - szczególnie zetcode wydaje interesujący (tj, życiowe przykłady na kodzie, a nie sucha przejażdżka po specyfikacji). Co najwyżej muszę przestawić swoją składnię - ich mechanika składni nieco różni się od tego, jaki styl przyjął M.Lis w książce :) Ale nie uczę się co do joty rysowania GUI od nich, lecz filtruję potrzebne dla siebie informacje. Nie znałem tego tutoriala wcześniej :) Już wplotłem do GUI rzeczy, które podłapałem z tego kursu (np. ikony obok napisów w menu) :)

Poszperałem po internecie i rozwiązałem to nieco inaczej. Wprawdzie nie jest to rozwiązanie bardzo elastyczne - zmieniłem linijkę g.drawImage na:

g.drawImage(img, 0, 0, getWidth(), getHeight(), null);

W tym momencie zdjęcia skalowane są do rozmiaru GUI. Oczywiście dopiero uświadomiłem sobie, ile funkcji taka przeglądarka obrazów jeszcze mieć musi (np. wygodne przewijanie zdjęć z klawiatury), ale program na wersję 0.1 zasługuje - ma GUI, Look and Feel i wyświetla grafiki :)

Kłaniam się i serdecznie dziękuję,
Krystian

1 użytkowników online, w tym zalogowanych: 0, gości: 1