Irek Słonina

Irek Słonina programowanie, bazy
danych i linuksy

Temat: JFrame, setVisible, nie do końca załadowane okno.

Witam,

robię setVisible(true) tuż przed czasochłonną operacją. Niestety okno nie zdąży się jeszcze "załadować".
JButtony pojawią się dopiero po wykonaniu czasochłonnej operacji.
Jak kazać JFrame'owi żeby się jednak łaskawie załadował do końca?


JFrame frame = new JFrame();

JPanel pan = new JPanel();
pan.setLayout(new javax.swing.BoxLayout(pan, javax.swing.BoxLayout.Y_AXIS));
frame.add(pan);
pan.add(new JButton("bat0"));
pan.add(new JButton("bat1"));

frame.setSize(100, 100)
frame.validate();
frame.setVisible(true);

try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Irek Słonina edytował(a) ten post dnia 27.07.09 o godzinie 14:11

konto usunięte

Temat: JFrame, setVisible, nie do końca załadowane okno.

SOI#1, u mnie działa:)

Z tego co pamiętam odświeżanie okien wykonywane jest z innego wątku, więc "czasochłonna operacja" nie powinna mieć na niego wpływu.

Możesz spróbować przed operacją wywołać frame.repaint().

polecam
http://java.sun.com/products/jfc/tsc/articles/painting...
Irek Słonina

Irek Słonina programowanie, bazy
danych i linuksy

Temat: JFrame, setVisible, nie do końca załadowane okno.

Dariusz Wawer:
SOI#1, u mnie działa:)

Z tego co pamiętam odświeżanie okien wykonywane jest z innego wątku, więc "czasochłonna operacja" nie powinna mieć na niego wpływu.

Możesz spróbować przed operacją wywołać frame.repaint().

polecam
http://java.sun.com/products/jfc/tsc/articles/painting...

Dzięki za odpowiedź.
repaint(), update(), revalidate() nie pomagają.

Się trochę naumiałem wczoraj na ten temat. To ma prawo działać, zależnie od tego skąd jest to wywoływane. Jeśli np. z main() - będzie działać, jeśli wywołane z GUI, np. w ActionListenerze jButtona, jak w moim przypadku - nie będzie.
Mój błąd, że nie podałem całego kodu tylko wyrywek.

Nie będzie działać dlatego, że jFrame nie zostanie przerysowany jeśli "przytrzymam" czasochłonną operacją funkcję obsługującą ActionListenera.
Dopiero jak skończę przetwarzanie jFrame się przerysowuje.
A moim problemem jest właśnie to zatrzymanie przetwarzania - bo nie mogę puścić dalej programu przed ukończeniem tej czasochłonnej operacji.

Prawdopodobnie będę musiał przerobić całą aplikację, abym jednak nie musiał jej zatrzymywać na tym ActionListenerze i nad tym w tej chwili pracuje.

konto usunięte

Temat: JFrame, setVisible, nie do końca załadowane okno.

Irek Słonina:
Nie będzie działać dlatego, że jFrame nie zostanie przerysowany jeśli "przytrzymam" czasochłonną operacją funkcję obsługującą ActionListenera.
Dopiero jak skończę przetwarzanie jFrame się przerysowuje.
A moim problemem jest właśnie to zatrzymanie przetwarzania - bo nie mogę puścić dalej programu przed ukończeniem tej czasochłonnej operacji.
Prawdopodobnie będę musiał przerobić całą aplikację, abym jednak nie musiał jej zatrzymywać na tym ActionListenerze i nad tym w tej chwili pracuje.

IMO nieco źle do tego problemu podchodzisz. Powinieneś tą czasochłonną operację wykonać w innym wątku. Przygotuj sobie wątek, któremu możesz podrzucać obiekty w których wykonywałbyś te operacje. To raz.
Dwa, zamiast przerabiać program, po prostu przed rozpoczęciem obliczeń schowaj główną ramkę i pokaż nową ramkę z napisem "proszę czekać". Po zakończeniu schowaj tą drugą i wróć do programu.
Ew. metody setEnabled(b) będą Twoimi przyjaciółmi:)
Irek Słonina

Irek Słonina programowanie, bazy
danych i linuksy

Temat: JFrame, setVisible, nie do końca załadowane okno.

Dariusz Wawer:
IMO nieco źle do tego problemu podchodzisz. Powinieneś tą czasochłonną operację wykonać w innym wątku. Przygotuj sobie wątek, któremu możesz podrzucać obiekty w których wykonywałbyś te operacje. To raz.
Dwa, zamiast przerabiać program, po prostu przed rozpoczęciem obliczeń schowaj główną ramkę i pokaż nową ramkę z napisem "proszę czekać". Po zakończeniu schowaj tą drugą i wróć do programu.
Ew. metody setEnabled(b) będą Twoimi przyjaciółmi:)

Moją czasochłonną operacją jest komunikacja aplikacji z serwerem.
Czasami (w większości przypadków) wykonania sekwencyjne, jedno po drugim. Czasami kolejne zapytanie polega na wyniku poprzednich i nie może zostać wysłane zanim poprzednie wywołanie się nie zakończy.

Nie mogę dopuścić do sytuacji, że nie kontroluję kolejności wysyłania komunikatów i czasu ich wysłania. Dlatego też chciałem pójść na łatwiznę - program pakuje sobie ileś zapytań, każde pakuję do osobnego miniwątku i każde kolejne zapytanie czeka na ukończenie poprzedniego.

Miałem nadzieję, że takie podejście nie zablokuje mi GUI - ale się myliłem bo tak czy inaczej czekanie na ukończenie poprzedniego blokuje główny wątek.

Brałem wcześniej pod uwagę setEnabled ale niestety ta opcja to jedynie uzupełnienie funkcjonalności (zablokuje GUI), nie ma natomiast wpływu na zablokowanie działania funkcji, a tego tak naprawdę potrzebuję.

Jedynym rozwiązaniem jest zrezygnowanie z miniwątków dla każdej pojedynczej komunikacji z serwerem i obudowanie każdej sekwencji komunikatów w jeden osobny wątek jak bozia nakazała.

konto usunięte

Temat: JFrame, setVisible, nie do końca załadowane okno.

Irek Słonina:
Nie mogę dopuścić do sytuacji, że nie kontroluję kolejności wysyłania komunikatów i czasu ich wysłania. Dlatego też chciałem pójść na łatwiznę - program pakuje sobie ileś zapytań, każde pakuję do osobnego miniwątku i każde kolejne zapytanie czeka na ukończenie poprzedniego.

Miałem nadzieję, że takie podejście nie zablokuje mi GUI - ale się myliłem bo tak czy inaczej czekanie na ukończenie poprzedniego blokuje główny wątek.

Nie rozumiem. Skoro każde z zapytań wykonywane jest w oddzielnym wątku, to dlaczego wątek gui jest blokowany?
Czy na pewno wołasz new Thread(aRunnable).start()?
Irek Słonina

Irek Słonina programowanie, bazy
danych i linuksy

Temat: JFrame, setVisible, nie do końca załadowane okno.

Dariusz Wawer:
Nie rozumiem. Skoro każde z zapytań wykonywane jest w oddzielnym wątku, to dlaczego wątek gui jest blokowany?
Czy na pewno wołasz new Thread(aRunnable).start()?

Tak wywołuje, próbowałem też np. w SwingWorkerze Runnable upakować. Nic to nie zmienia.
Same wątki nie blokują GUI. Blokuje czekanie na ich koniec, a to robię z wątku głównego, czyli tego samego z którego korzysta GUI.

konto usunięte

Temat: JFrame, setVisible, nie do końca załadowane okno.

Irek Słonina:
Tak wywołuje, próbowałem też np. w SwingWorkerze Runnable upakować. Nic to nie zmienia.
Same wątki nie blokują GUI. Blokuje czekanie na ich koniec, a to robię z wątku głównego, czyli tego samego z którego korzysta GUI.

Rozumiem. W takim razie nie czekaj na koniec, tylko zrób jakąś metodę, która wywoła setEnabled(false) na wszystkich elementach gui do wyłączenia, a gdy wątek się skończy, niech wywoła setEnabled(true).

Bo, jak rozumiem, wszystko sprowadza się do tego, by aplikacja nie pozwalała użytkownikowi na interakcję, gdy czeka na dane?
Irek Słonina

Irek Słonina programowanie, bazy
danych i linuksy

Temat: JFrame, setVisible, nie do końca załadowane okno.

Dariusz Wawer:
Irek Słonina:
Tak wywołuje, próbowałem też np. w SwingWorkerze Runnable upakować. Nic to nie zmienia.
Same wątki nie blokują GUI. Blokuje czekanie na ich koniec, a to robię z wątku głównego, czyli tego samego z którego korzysta GUI.

Rozumiem. W takim razie nie czekaj na koniec, tylko zrób jakąś metodę, która wywoła setEnabled(false) na wszystkich elementach gui do wyłączenia, a gdy wątek się skończy, niech wywoła setEnabled(true).

Bo, jak rozumiem, wszystko sprowadza się do tego, by aplikacja nie pozwalała użytkownikowi na interakcję, gdy czeka na dane?

Wtedy problem by nie istniał. Problemem jest to, aby nie dopuścić
do działania drugiego pobrania rezultatu przed zakończeniem komunikacji
(czyli funkcja getRezultat() powinna mnie przytrzymać aż będę mógł z niej wyciągnąć coś sensownego).

Tak to chciałem zrobić żeby się nie urobić (wywołań Komunikat.wyslij() sa grube setki):


jButton_Akcja.addActionListener(new ActionListener() {

Komunikat k1 = new Komunikat();
k1.wyslij(obiekt);
int rezultatPierwszegoZapytania = k1.getRezultat();

InnyObiekt obiekt2 = new InnyObiekt();
obiekt2.setJakasWartosc(rezultatPierwszegoZapytania);
Komunikat k2 = new Komunikat();
k2.wyslij(obiekt2);
jLabel_Wynik.setText(k2.getRezultat());
});


public class Komunikat {
private boolean komunikatGotowy;
private int rezultat;

public void wyslij(Object zapytanie) {
komunikatGotowy = false;
pokazCosWGUISwiadczaceOPracyWTle(true);
new Thread(new Runnable() {
jakasMetodaRealizujacaKomunikacjeZSerwerem();
komunikatGotwy = true;
}).start();
}

public int getRezultat() {
while (!komunikatGotowy) {
System.out.println("czekam chwile");
try {
Thread.sleep(300);
} catch (ExceptionInterruptedCostam ex) {
ex.printStackTrace();
}
}

return rezultat;
}

pokazCosWGUISwiadczaceOPracyWTle(boolean trufals) {
// tutaj cuda jak setEnabled, new JFrame albo start/stop
// progressbara.
}
}


Kod pisany z palca, jedynie pokazujący metodologię.
Zamiast Thread.sleep(300) powinien być oczywiście np. join().
Kod w ActionListenerze bezpośrednio też jest brzydkim rozwiązaniem. Nie jest to jednak przedmiotem dyskusji.

Wnioski:
Generalnie doszedłem do tego, że jest to niewykonalne ponieważ Thread.sleep() / join() blokuje jedyny wątek, w którym może działać GUI.

Innej metody niż poniższe rozwiązanie nie widzę, choć bardzo nad tym boleję:


jButton_Akcja.addActionListener(new ActionListener() {
new Thread(new Runnable() {
Komunikat.pokazCosWGUISwiadczaceOPracyWTle(true);

Komunikat k1 = new Komunikat();
k1.wyslij(obiekt);
int rezultatPierwszegoZapytania = k1.getRezultat();

InnyObiekt obiekt2 = new InnyObiekt();
obiekt2.setJakasWartosc(rezultatPierwszegoZapytania);
Komunikat k2 = new Komunikat();
k2.wyslij(obiekt2);
jLabel_Wynik.setText(k2.getRezultat());

Komunikat.pokazCosWGUISwiadczaceOPracyWTle(false);
}).start();
});


public class Komunikat {
private int rezultat;

public void wyslij(Object zapytanie) {
jakasMetodaRealizujacaKomunikacjeZSerwerem();
}

public int getRezultat() {
return rezultat;
}

public static void pokazCosWGUISwiadczaceOPracyWTle(boolean trufals) {
// tutaj cuda jak setEnabled, new JFrame albo start/stop
// progressbara.
}
}
Irek Słonina edytował(a) ten post dnia 29.07.09 o godzinie 13:01

konto usunięte

Temat: JFrame, setVisible, nie do końca załadowane okno.

Irek Słonina:
Generalnie doszedłem do tego, że jest to niewykonalne ponieważ Thread.sleep() / join() blokuje jedyny wątek, w którym może działać GUI.

Innej metody niż poniższe rozwiązanie nie widzę, choć bardzo nad tym boleję:


jButton_Akcja.addActionListener(new ActionListener() {
new Thread(new Runnable() {
Komunikat.pokazCosWGUISwiadczaceOPracyWTle(true);

Komunikat k1 = new Komunikat();
k1.wyslij(obiekt);
int rezultatPierwszegoZapytania = k1.getRezultat();

InnyObiekt obiekt2 = new InnyObiekt();
obiekt2.setJakasWartosc(rezultatPierwszegoZapytania);
Komunikat k2 = new Komunikat();
k2.wyslij(obiekt2);
jLabel_Wynik.setText(k2.getRezultat());

Komunikat.pokazCosWGUISwiadczaceOPracyWTle(false);
}).start();
});


public class Komunikat {
private int rezultat;

public void wyslij(Object zapytanie) {
jakasMetodaRealizujacaKomunikacjeZSerwerem();
}

public int getRezultat() {
return rezultat;
}

public static void pokazCosWGUISwiadczaceOPracyWTle(boolean trufals) {
// tutaj cuda jak setEnabled, new JFrame albo start/stop
// progressbara.
}
}
Irek Słonina edytował(a) ten post dnia 29.07.09 o godzinie 13:01

Nie widzę przyczyny nad którą masz nad tym boleć, to, do czego właśnie dochodzisz, to dość standardowa konstrukcja.

Pamiętaj jednak, że utworzenie nowego obiektu Thread jest dość kosztowne. Zamiast dla każdego polecenia tworzyć nowy wątek napisz klasę, która będzie wątkiem przyjmującym zadania. Możesz w niej użyć jakiejś synchronizowanej kolejki, dzięki której ładnie unikniesz problemów z wątkami.
Jeśli chcesz pójść na łatwiznę, daj mi maila na priva, podeślę Ci podobną klasę z jednej z moich aplikacji.
Irek Słonina

Irek Słonina programowanie, bazy
danych i linuksy

Temat: JFrame, setVisible, nie do końca załadowane okno.

Dariusz Wawer:
Nie widzę przyczyny nad którą masz nad tym boleć, to, do czego właśnie dochodzisz, to dość standardowa konstrukcja.


Boleję dlatego, że zamiast poprawić w jednym miejscu to muszę
znaleźć paręset miejsc w projekcie i w każdym z nich to poprawić.
Im więcej poprawek tym większe prawdopodobieństwo pomyłki.

To, że to jest standardowa konstrukcja i tak się powinno robić
od początku - nie mam wątpliwości.
Pamiętaj jednak, że utworzenie nowego obiektu Thread jest dość kosztowne. Zamiast dla każdego polecenia tworzyć nowy wątek napisz klasę, która będzie wątkiem przyjmującym zadania. Możesz w niej użyć jakiejś synchronizowanej kolejki, dzięki której ładnie unikniesz problemów z wątkami.
Jeśli chcesz pójść na łatwiznę, daj mi maila na priva, podeślę Ci podobną klasę z jednej z moich aplikacji.

Chętnie przyjmę przykłady sprawdzonych rozwiązań, z pewnością się przydadzą.

Pozdrawiam i dzięki za czas, który mi poświęciłeś.
Marcin K.

Marcin K. Centrium CRM

Temat: JFrame, setVisible, nie do końca załadowane okno.

Moim zdaniem Twój problem wynika z tego, że nie upewniasz się, że ramka buduje się w EDT.



private void createGUI() {
// tu ramkę tworzysz
}

private void startComm() {
// tu startujesz wątek, który długo trwa
}

public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGui();
}
});
startComm();
}


Cytując "Alexander Potochkin's Blog":
Actually the main rule is
"If you work with Swing not from EDT all sorts of strange things can happen".

Więcej:
http://weblogs.java.net/blog/alexfromsun/archive/2005/...



Wyślij zaproszenie do