NullPointer losowo w GUI

NullPointer losowo w GUI
Lukasz_
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:140
0

Witam. Próbuję znaleźć powód dla którego dostaje losowo NullPointerExcetion w prostym programie. Program ma symulować dwa różne kolory płytek ganiające się za sobą. Jak na razie jest tylko jeden kolor i nie robi nic poza pojawieniem się w losowym miejscu na planszy. Wszystko fajnie, działa ale z jakiegoś powodu wyskakuje ten chory nullpointer. Nie ma zasady, raz wyrzuci raz nie. Zawsze natomiast po utworzeniu wszystkich 6 wątków. Jedyne podejrzenie jakie mam to to, że może problem polega na tym, że szybciej wątek próbuje pokolorować JPanel , niż go stworzyć. Ale jako że dzieje się to w EventQueue to raczej niemożliwe. Ktoś coś ?

MainFrame.java

Kopiuj
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class MainFrame extends JFrame{
	JButton Close;
	JButton New;
	JButton Initialize;
	JButton Stop;
	JButton Start;
	
	JPanel ButtonPanel;
	JPanel ViewPanel;
	JPanel RightMargin;
	JPanel LeftMargin;
	JPanel BottomMargin;
	
	JPanel[][] PanelsTab;
	
	public MainFrame(int MapSizeX, int MapSizeY) {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setResizable(false);
		setLocation(500, 200);
		setSize(800, 800);
		setLayout(new BorderLayout());
		
		Close = new JButton("Zamknij");
		New = new JButton("Czysc");
		Initialize = new JButton("Inicjuj");
		Start = new JButton("Start");
		Stop = new JButton("Stop");
		
		ButtonPanel = new JPanel(new FlowLayout());
		ViewPanel = new JPanel(new GridLayout(MapSizeX, MapSizeY, 2, 2));
		
		ButtonPanel.add(Initialize);
		ButtonPanel.add(Start);
		ButtonPanel.add(Stop);
		ButtonPanel.add(New);
		ButtonPanel.add(Close);
		
		PanelsTab = new JPanel[MapSizeX][MapSizeY];
		RightMargin = new JPanel();
		LeftMargin = new JPanel();
		BottomMargin = new JPanel();
		
		for(int i = 0; i < MapSizeX; i++ ) {
			for(int j = 0; j < MapSizeY; j++) {
				PanelsTab[i][j] = new JPanel();
				PanelsTab[i][j].setBackground(Color.WHITE);
				ViewPanel.add(PanelsTab[i][j]);
			}
		}
		
		add(RightMargin, BorderLayout.LINE_END);
		add(LeftMargin, BorderLayout.LINE_START);
		add(BottomMargin, BorderLayout.PAGE_END);
		add(ViewPanel, BorderLayout.CENTER);
		add(ButtonPanel, BorderLayout.NORTH);
		
		Close.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				
			}
		});
		
		setVisible(true);
	}
}

GUI.java

Kopiuj
import java.awt.Color;
import java.awt.EventQueue;


public class GUI implements Runnable{
	private int MapSizeX;
	private int MapSizeY;
	private static MainFrame MainFrame1;
	
	public GUI(int x, int y) {
		this.MapSizeX = x;
		this.MapSizeY = y;
	}
	public void run() {
		EventQueue.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				MainFrame1 = new MainFrame(MapSizeX, MapSizeY);
				
			}
		});
	}
	
	public static void InsertRabbit(int x, int y) {
		MainFrame1.PanelsTab[x][y].setBackground(Color.BLUE);
	}
	
	public static void InsertWolf(int x, int y) {
		MainFrame1.PanelsTab[x][y].setBackground(Color.RED);
	}
	
	public static void Clear(int x, int y) {
		MainFrame1.PanelsTab[x][y].setBackground(Color.WHITE);
	}
}

Rabbit.java

Kopiuj
import java.awt.EventQueue;


public class Rabbit implements Runnable{
	private int posX;
	private int posY;
	private boolean isAlive;
	private int RabbitNumber;
	
	public Rabbit(int number) {
		this.isAlive = true;
		this.RabbitNumber = number;
		
		int helpX = Simulate.Generator.nextInt(Simulate.MapSizeX);
		int helpY = Simulate.Generator.nextInt(Simulate.MapSizeY);
		
		while(Simulate.Mapa.CheckContent(helpX, helpY) != "Clear") {
			helpX = Simulate.Generator.nextInt(Simulate.MapSizeX);
			helpY = Simulate.Generator.nextInt(Simulate.MapSizeY);
		}
		
		this.posX = helpX;
		this.posY = helpY;
		
		Simulate.Mapa.InstertRabbit(this.posX, this.posY);
		System.out.println("Rabbit number " + RabbitNumber + " has been born on " + (this.posX) + " " + (this.posY) + "!");
		EventQueue.invokeLater(new Runnable()
		{
			public void run() {
				GUI.InsertRabbit(posX, posY);
			}
		});
		
		
	}
	
	private synchronized void Move() {
		while(isAlive) {
			
			try{
				System.out.println("Rabbit number " + this.RabbitNumber  + " is moving!");
				wait(5000);
			}catch(InterruptedException e) {
				System.out.println("Ohh noes, rabbit number " + this.RabbitNumber + " is dead but not in natural way!");
			}
		
		}
	}
	
	public void run(){
		//Move();
	}
}

Wolf.java

Kopiuj
public class Wolf implements Runnable{
	
	public void run(){
		
	}
}

Simulate.java

Kopiuj
import java.util.Random;


public class Simulate {
	public static int WolfsAmount = 0;
	public static int RabbitsAmount = 0;
	public static int MapSizeX;
	public static int MapSizeY;
	public static Map Mapa;
	public static Random Generator;
	public static Thread[] RabbitsTab;
	public static Thread[] WolfsTab;
	
	public static void main(String[] args) {
		
		if(args.length < 4) {
			System.out.println("Za malo argumentow!");
			return;
		}
		
		try{
			MapSizeX = Integer.parseInt(args[0]);
			MapSizeY = Integer.parseInt(args[1]);
			WolfsAmount = Integer.parseInt(args[2]);
			RabbitsAmount = Integer.parseInt(args[3]);
			
			if(MapSizeY < 2 || MapSizeX < 2 ||WolfsAmount < 1 || RabbitsAmount < 1) {
				System.out.println("Nieprawidlowe dane startowe!");
				return;
			}
			
			Generator = new Random();
			Mapa = new Map(MapSizeX, MapSizeY);
			GUI GuiT = new GUI(MapSizeX, MapSizeY);
			Thread GUIThread = new Thread(GuiT);
			GUIThread.start();
			
			
			
			RabbitsTab = new Thread[RabbitsAmount];
			WolfsTab = new Thread[WolfsAmount];
			
			for(int i = 0; i < RabbitsAmount; i++) {
				RabbitsTab[i] = new Thread(new Rabbit(i + 1));
				RabbitsTab[i].start();
			}
			
			
			
		}catch(NumberFormatException e){
			System.out.println("Dane startowe nie sa liczbami!");
		}
		
		
		
	}
}

Map.java

Kopiuj
public class Map {
	private String[][] Content;
	private int height;
	private int width;
	
	public Map(int x, int y){
		this.height = x;
		this.width = y;
		
		this.Content = new String[x][y];
		
		for(int i = 0; i < x; i++) {
			for(int j = 0; j < y; j++) {
				this.Content[i][j] = "Clear";
			}
		}
	}
	
	public void Clear(int x, int y) throws Exception{
		if(x < 0 || x > this.height - 1 || y < 0 || y > this.width -1) {
			throw new Exception("Out of range!");
		}
		
		this.Content[x][y] = "Clear";
	}
	
	public void ClearAll() {
		for(int i = 0; i < this.height; i++) {
			for(int j = 0; j < this.width; j++) {
				this.Content[i][j] = "Clear";
			}
		}
	}
	
	public void InsertWolf(int x, int y) {
		this.Content[x][y] = "Wolf";
	}
	
	public void InstertRabbit(int x, int y) {
		this.Content[x][y] = "Rabbit";
	}
	
	public String CheckContent(int x, int y) {
		return this.Content[x][y];
	}
}
Atlas500
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:82
0

Nie chciało mi się wgłębiać, ale: EventQueue.invokeLater() tworzy wątek rozdzielający wydarzenia. Który jest jeden i powinien "obejmować" cały program żeby, no właśnie, obsługiwać wydarzenia. Jeżeli tworzysz nowy wątek rozdzielający wydarzenie, to zastępujesz stary. Resztę synchronizacji na zasobach musisz wykonać sam. prześledź jeszcze raz ścieżkę wywołań. Jeżeli błąd wyskakuje losowo to albo jest zależny od jakiegoś mechanizmu losującego (w co wątpię) albo jakieś wątki zaczynają odwoływać się do niezsynchronizowanych zasobów kiedy nie powinny tego robić.
I, na Boga, pisz nazwy zmiennych i metod z małej litery. Są pewne zasady normalizacji, zwłaszcza jeżeli chcesz żeby ktoś czytał twój kod.


"Jeżeli człowiek to wymyślił, człowiek może to zrozumieć." Sochacki
edytowany 3x, ostatnio: Atlas500
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
0

Jak ci leci wyjątek, to po pierwsze powinieneś ustalić i pokazać skąd on leci (najlepiej całego call stack-a).


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
Lukasz_
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:140
0

Jeżeli tworzę 6 czerwonych JPaneli, to próba pokolorowania ostatniego kończy się wyjątkiem. Problem leży w EventQueue bo szybciej koloruje niż tworzę (trochę to idiotyczne według mnie ale widać jest możliwe) . Pomogło generowanie na starcie czystych paneli a losowe kolorowanie ich dopiero pod buttonem (przypuszczam że gdybym był w stanie dość szybko wcisnąć button to i tak poleciałby wyjątek) co nie koniecznie rozwiązuje problem, jedynie go omija. Nie mogę skumać jak to zrobić żeby za całe GUI odpowiadał jeden wątek i sam sobie w paradę nie wchodził, do tego robiąc to w odpowiedniej kolejności.

Atlas500
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:82
0

Nigdzie nie napisałem, że za całe gui ma odpowiadać jeden wątek, jedynie, że za obsługę wydarzeń ma odpowiadać jeden wątek.

Kopiuj
public static void main(String... a){
		EventQueue.invokeLater(new Runnable(){
			@Override
			public void run(){
				new MainClass(); //główne okno programu
			}
		});
	}

i już. Wszystkie zdarzenia z głównego okna i wszystkich obiektów które tworzy/zawiera jego klasa będą wykonywane w tym wątku. Twórcy biblioteki Swing zadbali, żeby ta prosta konstrukcja wystarczyła do zapewnienia synchronizacji obsługi wszystkich wydarzeń w programie, nie tylko z bezpośrednio tworzonego w metodzie obiektu, bo chyba w ten sposób ty to rozumiesz.
Sam program może używać dowolnej liczby wątków.


"Jeżeli człowiek to wymyślił, człowiek może to zrozumieć." Sochacki
Lukasz_
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:140
0

Przecież tak mam zrobione właśnie.

Atlas500
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:82
0

Co mi tu bajdurzysz. Masz EventQueue.invokeLater() w klasach GUI i Rabbit.


"Jeżeli człowiek to wymyślił, człowiek może to zrozumieć." Sochacki
Lukasz_
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:140
0

No dobrze, ale jak chce żeby inny wątek pokolorował JPanel to nie powinienem tego robić bezpośrednio tylko właśnie EventQueue.invokeLater() o ile się orientuję.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)