Komunikacja między komponentami

0

Witam.
Mam "maleńki" problem.
Na zaliczenie mam do napisania komponent w javie który będzie umożliwiał filtrowanie wczytanego obrazka.
Samo wczytanie obrazka, filtracja i zapis nie jest problemem.
Problemy stwarza prowadzący :P
Zażyczył sobie żeby komponent główny (nazwijmy go main) miał możliwość wczytania tylko obrazka oraz wyświetlenia listy filtrów oraz guzik do filtrowania i zapisania obrazka.
Nie ma on mieć w sobie kodu żadnego filtru!
Filtry które będą dostępne w tymże komponencie mają być w osobnych jar'ach (fasolkach).

Także przeciągam na formatkę komponent główny, obok niego dorzucam komponenty rozmycie, krawędzie i mediana. Kiedy uruchomię aplikacje w combobox'ie w głównym komponencie pojawią się pozycje: rozmycie, krawędzie, mediana.
Wybieram interesującą mnie opcje i klikam filtruj. Oczywiście dorzucając do formatki nowe komponenty lista z filtrami się powiększa.

Każdy filtr powinien mieć także własne opcje, tzn panel z opcjami filtru, a co za tym idzie kiedy wybiorę z combo filtr, poniżej powinny pokazać się dostępne opcje dla filtru. One także powinny znajdować się w jarach, tzn każdy filtr ma swój panel z opcjami.

Dodając jar'y z filtrami oczywiście na scenie nie ma się nic pokazywać.

Mniej więcej zarys komponentu:

user image

Mam problem z pomysłem na taką komunikacje i nie wiem jak się zabrać za to.
Chciałem w plikach txt wpisać zawartość macierzy do filtru macierzowego, zczytywać zawartość folderu i dodawać je do listy, Później przekazać jako parametr funkcji zawartość tego pliku (jako macierzy) i gotowe. Niestety zgody nie było.

Wiem, założenia projektu może nie są zbyt fajne, wręcz głupie, ale wykładowcy się nie wybiera :P

0

Robisz tak:

  • wszystkie filtry umieszczasz w katalogu lib.
  • następnie po uruchomieniu programu wczytujesz wszystkie jary z tego katalogu. Jak dokładnie jest w googlu (jar loader)
  • Każdy filtr implementuje sobie interfejs Filtr:
public interface Filtr{
   public void setContext(Main main);
}

Metoda setContext() przekazuje do filtru komponent main. Reszta to już tylko sprawa filtru i tego jakie będzie wyciągał informacje z tego komponentu..

0

Ja bym to zrobił tak, że dla każdego filtru wyklikałbym gołe panele pasujące do tego miejsca na filtry. Wtedy uaktywnienie pozycji na liście filtru powodowałoby wrzucenie panelka w to miejsce z jednoczesnym wyrzuceniem panelka poprzedniego (o ile jakiś by wcześniej był). Oczywiście panele te musiałyby byc powiązane z danymi samych filtrów, ale jak sądzę z tym sobie bez problemu poradzisz.
W tym samym czasie komponent zawierający przetwarzany obrazek stale testowałby aktualność filtra (w tym sensie, że jego dane nie zostały zmienione od ostatniego wyświetlania), a w momencie gdy filtr stałby się nieaktualny (bo user zmienił cokolwiek na panelu bieżącego filtra) dawał mu obraz do przetworzenia i odbierałby przetworzoną zwrotkę oraz po chwili (zależy jak długo trwałoby to filtrowanie) wyświetlał na swoim polu. I tak aż do kolejnej zmiany danych.

Kluczem jest więc komunikacja między komponentem wyświetlającym przefiltrowany obrazek, a bieżącym filtrem. Między kontrolkami filtrów, a komponentem obrazka nie powinno być żadnego połączenia.
Można to przeprowadzić też tak, żeby dezaktualizacja filtra powodowała utworzenie wątku roboczego (może być Swingworker), który przeczyta wczytany obrazek i przefiltruje go po czym wynik przekaże w jakiejś metodzie set do komponentu, który go wyświetli i osobno ustawi stan filtra jako aktualny.

Czyli komunikacja może być na zasadzie pobierania danych filtra przez komponent obrazka lub przez wpychanie zaktualizowanych danych przez wątek roboczy zainicjowany z bieżącego filtra. Można też wykombinować coś pośredniego - komponent obrazka zawsze pobiera do wyświetlenia z czystego obrazka lub wybranego filtra (jeżeli jest ustawiony)), a bieżący filtr wysyła tylko komunikaty do komponentu o swojej dezaktualizacji, czyli kolejnym pobraniu. Każdy filtr musiałby mieć funkcje komunikacyjne z komponentem wrzucone do jakiegoś szablonu - czy to przez implementację interfejsu czy przez dziedziczenie (bo kod komunikacyjny zawsze będzie ten sam).

ps. Nie doczytałem, że miał być guzik do filtrowania. Bo ja opisałem filtrowanie w locie. Za pomocą guzika cała dezaktualizacja filtra to byłby skutek wywołania ActionPerformed(). Reszta taka sama. Tylko efekt mniej interaktywny.

0

Wszystko ok, nie wiem tylko jak zrobić tą komunikacje.
Chciałem zrobić to tak:
aby w każdym filtrze dać metodę zgłaszającą filtr do okna głównego (w sensie mojego głównego komponentu).
W nim listę filtrów które się zgłosiły. Przy starcie aplikacji po prostu miałbym listę wszystkich filtrów dodanych (przeciągniętych na formatkę).
Później po prostu wybierać z listy filtr i wywoływać metodę filtruj z niego która by zwróciła obrazek, a ten już bym sobie wyświetlił w moim głównym komponencie.

Nie wiem czy tak to by mogło wyglądać, bo javą zajmuje się stosunkowo krótko i nie pracowałem z interfejsami.

Może jakiś przykład zastosowania interfejsu??
Na necie niestety nic ciekawego nie ma, a na pewno koledzy programiści nie mają problemu z taką drobnostką.

Wielkie dzięki za wszelką pomoc :)

0

Już naczytałem się o introspekcji, BeanInfo i podobnych.
Niestety nie wiem jak wykryć przeciągnięcie komponentu na formatkę (mówię cały czas o NetBeans'ie)
Jak wykryć wszystkie komponenty na scenie i zgłosić się tylko do określonego rodzaju komponentu?
Kiedy chce pobrać info o jakimś komponencie przez Introspekcje robię tak:

    BeanInfo info = Introspector.getBeanInfo( MyBean.class );

Gdzie muszę podać nazwę konkretnej klasy, tzn komponentu. A chciałbym aby przeszukał wszystkie komponenty na scenie w poszukiwaniu określonej metody i potem wywołał ta metodę i jako argument podał swoją nazwę- tzn nazwę którą faktycznie ma w projekcie, czyli nie np filtr_blur, ale filtr_blur1, bo NetBeans dodaje indeksy dla poszczególnych instancji.

Nie mogę sobie z tym poradzić, a nie znam na tyle javy aby zrobić to w jakiś zaawansowany sposób.
Może jakiś przykładzik?
Sen mi to z powiek spędza, a termin oddania projektu coraz to bliżej :(

Do szczęścia brakuje mi tylko wiedzy jak wywołać metodę z innej klasy, tzn za pomocą invoke.
http://java.sun.com/docs/books/tutorial/reflect/member/methodInvocation.html
Tu coś takiego jest, ale u mnie nie działa.

Moja klasa główna ma metodę dodajFiltr która przyjmuje jako argument nazwe filtru, czyli klasy filtru.
Chciałbym wywołać tą metodę z poziomu mojego filtru. Niestety nie wiem jak przekazać do niej argumenty :/

0
little_MASTER napisał(a)

Wszystko ok, nie wiem tylko jak zrobić tą komunikacje.

Najprostsza komunikacja między jedną klasą, a inną, to po prostu wywołanie metody drugiej klasy i przekazanie jej jakichś argumentów.
Za dużo próbujesz kombinować.
Lista (zapewne JComboBox), z której będziesz wybierać filtr musi zostać jakoś wypełniona czymś, co identyfikuje obiekty poszczególnych paneli. W najprostszej wersji albo indeks do tablicy z wszystkimi wczytanymi panelami, albo referencje do tych obiektów wczytanych paneli.
Wszystkie panele muszą mieć jakieś wspólne właściwości, dzięki którymi należą do tej samej rodziny. Czyli mają właśnie taki sam interfejs (w sensie potocznym). W Javie interfejs, to po prostu kilka metod zgrupowanych w jedną rzecz, która jest jakby szablonem klasy. Metody mają konkretne nazwy i swoje typy argumentów i nie mają definicji, albo mówiąc inaczej są pustymi wirtualnymi metodami. Jeżeli jakaś klasa implementuje interfejs, to musi te metody zdefiniować. Jeżeli każdy panel filtra będzie implementował ten sam interfejs, to z każdym z nich będzie się można w ten sam sposób komunikować - wywołując te same metody (o tych samych nazwach i argumentach). W ten sposób wszystkie filtry będą miały ustandaryzowaną komunikację. Właśnie w tym celu stosuje się interfejsy. Alternatywnym sposobem jest stworzenie klasy abstrakcyjnej z pustymi metodami i sposobowanie aby każdy panel filtra dziedziczył po niej. Problem w tym, że jeżeli panel już dziedziczy po komponencie Swinga, to nie można tego zrobić bo w Javie nie ma wielodziedziczenia.
Każdy obiekt, który będzie chciał się skomunikować z panelem filtra (nieważne jakim) będzie po prostu wywoływał metody interfejsu dla tego filtra. Nie ważne z jakim konkretnym filtrem będzie mieć do czynienia i czy dostęp do niego będzie z jakiejś tablicy filtrów po indeksie, czy bezpośrednio przez referencję uzyskaną z listy dla wybranego filtra. Wywołanie będzie takie same. Kwestia komunikacji odfajkowana. Od razu masz też przykład jak wykorzystywać interfejsy.

Co do problemu jak umieścić poszczególne filtry, to zależy to od tego czy filtry robisz sam, czy już gdzieś są zrobione i czekają sobie na użycie. Jeżeli czekają, to wystarczy sprawdzić jakie metody posiadają i czy mają te same metody do komunikacji. Jeżeli mają te same metody, to pewnie spełniają jakiś interfejs, którego wystarczy użyć (sprawdzić w dokumentacji tych filtrów). Jeżeli każdy filtr ma inne metody i inaczej się go używa, to musiałbyś do każdego filtra dopisać klasę, która będzie spełniać jakiś jeden wspólny interfejs komunikacyjny i będzie "używać" tego konkretnego filtra, którego opakuje. Od tego momentu to te klasy będą się komunikować z resztą Twojego programu jako ustandaryzowane filtry
Jeżeli każdy filtr będziesz robić samodzielnie, to od razu każdy z nich powinien implementować jakiś interfejs, który sam sobie obmyślisz. Sprawa filtrów odfajkowana.

Co do problemu jak pokleić w NB poszczególne panele stworzone na formatkach. Robi się to to prościej tak: Tworzysz x klas paneli dla każdego filtra (te same wymiary i ogólny wygląd). Następnie w klasie swojego programu robisz albo tablicę, której elementami będą skonstruowane za pomocą new obiekty tych klas, a na listę filtrów wrzucasz indeksy do tej tablicy, albo przy wypełnianiu listy filtrów będziesz tworzyć (też przez new) po jednym obiekcie filtra i referencję do niego umieścisz jako kolejny element listy. Wtedy po kliknięciu konkretnego elementu dostaniesz od razu referencję do obiektu filtra (co napisałem wyżej). Oba sposoby są równoważne. W przypadku wczytywania obcych klas filtrów na listę lub do tablicy pakujesz utworzony przez new obiekt opakowujący konkretny filtr.
Tu przykład szablonu kodu. Każda klasa filtra, to może być zrobioną w osobnym pliku formatką Netbeans. Jedynym Twoim dopiskiem powinno być implementowanie jakiegoś interfejsu (tutaj Filtr):

interface Filtr
{
//synchroniczna, w przypadku błędu filtrowania może zwracać null
//albo generować wyjątek
  Image filtruj(/*...*/);
//...
}

//1
public class Filtr_blur extends JPanel implements Filtr { /*...*/ }
//2
public class Filtr_smooth extends JPanel implements Filtr { /*...*/ }
//3
public class Filtr_sharpen extends JPanel implements Filtr { /*...*/ }
//...
//Kod Twojej aplikacji:
Filtr[] filtry_tab = { new Filtr_blur(), new Filtr_smooth(), new Filtr_sharpen(), /*...*/ };
//...
JComboBox filtry = new JComboBox(filtry_tab);

W ten sposób akcja na konkretnym elemencie da Ci od razu referencję do obiektu z interfejsem Filtr, którego metodami możesz się posłużyć do przesłania danych do przefiltrowania i odebrania wyników
Komunikacja odfajkowana.

Co do wywoływania obiektów, które opisałeś w drugim poście, to refleksja i jej mechanizmy "dobierania się do klas od kuchni" byłyby potrzebne wyłącznie gdyby klasy filtrów były pisane przez kogoś innego i nie miałbyś ich źródeł (tylko pliki class lub opakowujące je "jary"). Wtedy takimi metodami posługiwałyby się klasy opakowujące. Gdyby klasy te wykorzystywały wspólny interfejs, to nawet to nie byłoby potrzebne bo wystarczyłoby po prostu wywołać metody tego wspólnego interfejsu dla klas tych filtrów.
Jeżeli jednak to ty masz napisać te filtry, to jest to zupełnie zbędny balast.

Później po prostu wybierać z listy filtr i wywoływać metodę filtruj z niego która by zwróciła obrazek, a ten już bym sobie wyświetlił w moim głównym komponencie.

taka metoda "filtruj", to właśnie przykład metody wspólnego interfejsu komunikacyjnego. Może być pojedyncza - wtedy wywołanie jest synchroniczne - czyli nie wróci ze sterowaniem dopóki filtr nie odda wyniku (i wtedy trzeba uważać bo Swing nie toleruje długich czasów wykonania - zawiesza wtedy obsługę okienkową i program staje dęba), A można to też rozwiązać inaczej - tak aby wywołanie było asynchroniczne - czyli wywołanie metody przekaże tylko zlecenie oraz referencję do zlecającego. Filtr, kiedy skończy mógłby sam wywołać konkretną metodę zlecającego i w ten sposób "oddać" wynik w postaci przefiltrowanego obrazka. Można w tym celu wykorzystać klasę SwingWorker, która tworzy własny wątek roboczy i komunikuje się samodzielnie ze zlecającym lub obiektem podanym przez zlecającego.

Mam nadzieję, że nieco rozjaśniłem.

0

dzięki za odpowiedz i za pomoc. staram się to wszystko ogarnąć i jakoś napisać.
Java nie jest moją mocną stroną, a niestety prowadzący ćwiczenia wyskoczył z poziomem :/
Wszystko jeszcze musi być komponentami.
Ale teraz już łatwiej będzie, o wiele :)
Jak coś stworze to wrzucę kod do przejrzenia :)

Wszystko pisze od zera, ale z filtrami nie będzie problemu, bo wszystko i tak na macierzowym mogę zrobić, czyli tylko współczynniki do zmiany.

Tylko że z powyższym rozwiązaniu muszę wpisać listę filtrów które będę używał.
W założeniach projektu, które dostałem od prowadzącego, mam napisane że wystarczy przeciągnąć komponent filtru na formatkę i bez edycji kodu po odpaleniu programu w moim komponencie głównym pojawi sie dodatkowy filtr.
Chciałem to zrobić właśnie przez invoke. W konstruktorze filtru wywołuję metodę która dodaje do listy filtry.
Właśnie chodzi o tego typu dynamiczne dodawanie filtrów :/

0

Ok więc mam coś takiego:



class BlurFilter {
  public BufferedImage processImage(BufferedImage image) {
    float[] blurMatrix = { 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f,
        1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f };
    BufferedImageOp blurFilter = new ConvolveOp(new Kernel(3, 3, blurMatrix),
        ConvolveOp.EDGE_NO_OP, null);
    return blurFilter.filter(image, null);
  }
}

Jest to filtr Blur. Wziąłem go z gotowca, ale jak najbardziej działa. Jako parametr podaje BufferedImage i to samo powinien zwracać.

Teraz za pomocą:

       String curDir = System.getProperty("user.dir");
       URL[]  urlsToLoadFrom = new URL []  { new File(curDir).toURI().toURL() } ; 
       URLClassLoader loader1 = new URLClassLoader(urlsToLoadFrom) ;
       Class klasa = Class.forName("BlurFilter", true, loader1);
       Method m = klasa.getMethod("processImage", new Class[] { BufferedImage.class });
       wynikowy_bufor = (BufferedImage)(m.invoke(null, new Object[] { do_filtrowania}));

Wyszukuje czy istnieje w folderze aplikacji klasa BlurFilter i wywołuje metodę processImage która powinna mi zwrócić mój nowy przefiltrowany bufor.

Niestety wywala mi same błędy:

init:
deps-jar:
compile:
run:
C:\Documents and Settings\Misiu\Pulpit\java\JavaApplication30
Glow_OK.class
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at little_MASTER.Photoshop$Filtruj.mouseClicked(Photoshop.java:182)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
at java.awt.Component.processMouseEvent(Component.java:6044)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
at java.awt.Component.processEvent(Component.java:5806)
at java.awt.Container.processEvent(Container.java:2058)
at java.awt.Component.dispatchEventImpl(Component.java:4413)
at java.awt.Container.dispatchEventImpl(Container.java:2116)
at java.awt.Component.dispatchEvent(Component.java:4243)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4322)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3995)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3916)
at java.awt.Container.dispatchEventImpl(Container.java:2102)
at java.awt.Window.dispatchEventImpl(Window.java:2440)
at java.awt.Component.dispatchEvent(Component.java:4243)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)
BUILD SUCCESSFUL (total time: 5 minutes 43 seconds)

Nie wiem czy poprawnie skonstruowałem klasę wewnętrzną, inaczej nie pozwolił mi tego zrobić netbeans :/

 public class Filtruj extends MouseAdapter
{

        @Override
        @SuppressWarnings("static-access")
	public void mouseClicked(MouseEvent e)
	{
       String curDir = System.getProperty("user.dir");
       URL[]  urlsToLoadFrom = null ;
                   try {
                urlsToLoadFrom = new URL[]{new File(curDir).toURI().toURL()};
            } catch (MalformedURLException ex) {
                Logger.getLogger(Photoshop.class.getName()).log(Level.SEVERE, null, ex);
            }
       URLClassLoader loader1 = new URLClassLoader(urlsToLoadFrom) ;
       Class klasa = null;
       
                   try {
                klasa = Class.forName("BlurFilter", true, loader1);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(Photoshop.class.getName()).log(Level.SEVERE, null, ex);
            }
       
       Method m = null;
       
                   try {
                m = klasa.getMethod("processImage", new Class[]{BufferedImage.class});
            } catch (NoSuchMethodException ex) {
                Logger.getLogger(Photoshop.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SecurityException ex) {
                Logger.getLogger(Photoshop.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                wynikowy_bufor = (BufferedImage) (m.invoke(null, new Object[] {do_filtrowania }));
            } catch (IllegalAccessException ex) {
                Logger.getLogger(Photoshop.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(Photoshop.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                Logger.getLogger(Photoshop.class.getName()).log(Level.SEVERE, null, ex);
            }
   
            
	repaint();
	}
}  

mój paint wygląda tak:

public void paint(Graphics g)
		{ 
		 super.paint(g);                  
                 g.drawImage(wynikowy_bufor, 20, 55, 200, 200, this);
		}

Nie mam pomysłu co jest z tym nie tak :/
Wg mnie wszystko powinno być ok.
Opierałęm sięna tym poście: http://4programmers.net/Forum/388044?h=invoke#id388044

Kiedy "na żywca" skopiuje przykład ze strony to ładnie mi zwraca liczby, ale nie wiem co zrobić żeby mi zwróciło ten bufor.

Proszę pomóżcie...

0

Zrób najpierw program, który działa - nie przejmując się jakimś dynamicznym ładowaniem klas. Użyj zwykłego statycznego ładowania, które jest niczym innym tylko utworzeniem obiektów klas filtrów zawartych w projekcie aplikacji. Użycie klasy powoduje załadowanie pliku .class (domyślnie z bieżącego miejsca ładowania klasy aplikacji i jej metody startowej main()) przez JVM i jest to dla Twojego programu zupełnie niewidoczne. Jedynym wymaganiem takiego użycia jest dostępne źródło filtra w momencie kompilacji (a jest na pewno gdy filtr robisz sam).

Z tego co widzę zabierasz się do dynamicznego ładowania klas nie wiedząc w ogóle czy działa Ci program z chociaż jednym pracującym filtrem i jego panelem. To nie tędy droga. To tak jakby budować dom od dachu.
Należy zacząć od fundamentu czyli:

  1. działania programu z jednym umieszczonym na sztywno filtrem
  2. potem zrobić kilka filtrów i uruchomić ich dynamiczną wymianę (zarówno inny panel w oknie jak i wywoływanie innych filtrów).
  3. A dopiero na końcu mając już sprawnie wymieniane filtry można pokusić się o ładowanie nieznanych filtrów bez dostępu do ich źródeł. Przy czym musiałbyś ustalić czy do każdego z tych filtrów już jest zrobiony jakiś panel sterujący i czy można go użyć - ewentualnie czy da się taki panel dla każdego z dostępnych filtrów stworzyć - czy to statycznie w netbeans czy dynamicznie w kodzie Twojej aplikacji (raczej bardzo trudne bo trzeba by tworzyć panel na podstawie nieznanej funkcjonalności nieznanego filtra).

U Ciebie wygląda na to, że chcesz zacząć od punktu 3. gdy nie masz działającego punktu 1.

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