JavaFX nowy wątek i próba

0

Cześć,
mam problem dotyczący pobrania tekstu z TextField w innej klasie->nowym wątku. Uszczegóławiając, program pisany przy pomocy JavaFX(SceneBuiler). Mam klasę View połączoną z plikiem fxml. Klasa View implementuje interfejs Initializable i metodę initialize w której umieszczam kod dotyczący zdarzeń. W tej klasie dopisałem również metodę, która czyta pole obiektu TextField url;

public String getUrl() {
	return this.url.getText();
	}
		

Przy kliknięciu w button1 tworzony jest nowy wątek->nowa klasa wykonująca pewne funkcję. Chciałbym w niej pobrać dane z TextField url znajdującego się w klasie View. Utworzenie obiektu View i wywołanie metody getUrl(); nie skutkuje. Chyba jest jakiś problem, że nie mogę się odwołać do obiektu TextField przez obiekt View...jak go rozwiązać?

*W klasie View metoda getUrl(); zwraca to to powinna;

Z góry dziękuję za pomoc!

1

Bierzesz się za pisanie programu z wątkami i GUI a masz problem z przekazaniem referencji z jednego obiektu do drugiego...
Przekaż referencję do pola url przez konstruktor tworząc instancję tej klasy, w której chcesz z niego korzystać. Jeżeli zastanawiasz się dlaczego utworzenie nowego obiektu klasy View i wywołanie metody getUrl() nie skutkuje to wróć do podstaw programowania obiektowego i załap różnicę pomiędzy klasą a obiektem, bez tego dalej będzie tylko trudniej...

2

Przekaż tę wartość przy wywołaniu funkcji tworzącej nowy wątek, coś na zasadzie:

public void onButtonClicked(){
	this.runSomeBackgroundTask(this.url.getText());
}
	
private CompletableFuture runSomeBackgroundTask(final String url) {
	return CompletableFuture.runAsync(() -> System.out.println(url)); 
}

JavaFX uniemożliwia dokonywania zmian GUI z innych wątków.

0

Dzięki za nakierowanie!
Chyba trochę za dużo nakombinowałem, ale ostatecznie utworzyłem nową zmienną statyczną String w klasie View i przy kliknięciu przycisku uruchamiającego przed utworzeniem nowego wątek przypisuje do niej wartość z TextField url. Później już w klasie nowego wątku nie mam problemów z pobraniem tej wartości.

Ps. Nick zobowiązuje, jestem początkującym i może trochę zbyt dużo wiedzy na raz ;) Staram się to jakoś uporządkować, a ten programik przy którym sobie aktualnie pracuję po godzinach jest moim pierwszym, także proszę o wyrozumiałość ;)

0

ostatecznie utworzyłem nową zmienną statyczną String w klasie View

to akurat jest totalny fakap. Nie do tego służą zmienne statyczne. Jak się uczysz to pisz porządnie. Post wyżej masz prawidłowe rozwiązanie od @AreQ212

0

Niestety przy próbie implementacji rozwiązania od @AreQ212, cały czas wyrzuca mi wyjątek java.lang.NullPointerException. Wnioskuję, że odwołuję się do obiektu TextField przez referencję url która ma przypisaną wartość null.

0

@fresh_meat: ten fragment to jedynie koncepcja, nie znamy całości Twojego kodu, żeby móc w łatwy sposób odpowiedzieć na to skąd ten NullPointer się bierze. Wrzuć proszę kod tych klas tutaj w wątku albo do pastbina. Natomiast utworzenie zmiennej statycznej to faktycznie najgorsze z możliwych rozwiązań (szczególnie w kontekście wielowątkowości) :)

0

Okej, w takim razie prezentuję kod, zdaje sobie sprawę, że pewnie będzie to fakap (chyba jestem trochę zacofany, wcześniej nie znałem tego słowa a mi się spodobało. Dzięki @dymul), także czym więcej uwag tym lepiej ;)

public class View implements Initializable {

    @FXML
    private Button end;

    @FXML
    private Button start;

    @FXML
    public TextField url;

    @FXML
    private TextField email;

    @FXML
    private Circle circle;

    @FXML
    private PasswordField password;

    public static String fromTextFieldUrl;
	public static String fromTextFieldEmail;
	public static String fromTextFieldPassword;
	public static boolean working=false;
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		 	
	start.setOnAction(new EventHandler<ActionEvent>() {
		
		@Override
		public void handle(ActionEvent event) {
			
			String info1,info2;
			fromTextFieldUrl=url.getText();	
			fromTextFieldEmail=email.getText();
			fromTextFieldPassword=password.getText();
			working=true;
			circle.setFill(Color.RED);	
				Runnable taskThread=new MyRun();
				Thread nextThread=new Thread(taskThread);
				nextThread.start();
			url.setEditable(false);
			email.setEditable(false);
			password.setEditable(false);
		}	
	});
	
	end.setOnAction(new EventHandler<ActionEvent>() {
		
		@Override
		public void handle(ActionEvent event) {
			
			working=false;
			circle.setFill(Color.rgb(245, 240, 250));
			url.setEditable(true);
			email.setEditable(true);
			password.setEditable(true);
			
		}	
	});
	
	}//end initialize
}//end View


i klasa z metodą nowego wątku:

public class MyRun implements Runnable{
	
	@Override
	public void run() {
	String info1, info2,url,email,password;
	url=View.fromTextFieldUrl;
	email=View.fromTextFieldEmail;
	password=View.fromTextFieldPassword;
	
	while(View.working){
	System.out.println(email); 
	System.out.println(password);
		try {
			WebsiteInfo websiteInfo =new WebsiteInfo(url);
			info1=websiteInfo.chceckOutWebsite();
			Thread.sleep(10000);
			info2=websiteInfo.chceckOutWebsite();
			if(!(info1.equals(info2)))
			{
				SendAnEmail send=new SendAnEmail(email,password);
				send.setContent(url);
				try {
					send.send();
				} catch (MessagingException e) {
					
					e.printStackTrace();
				}
			}
		} catch (InterruptedException e) {
		
			e.printStackTrace();
			
		}
		
	}
		}//end run
}//end MyRun

0

Wystarczy że potrzebne wartości przekażesz w konstruktorze klasy MyRun i już nic nie będzie statyczne. Problem pojawia się przy sprawdzaniu czy pętla ma zostać przerwana (wydaje mi się że dałoby się to lepiej zrobić z jakimś nowszym API typu CompletableFuture), bo przekazanie wartości nie wchodzi w rachubę, musisz móc ją aktualizować. Stąd też Supplier. Innym rozwiązaniem byłoby też użycie AtomicBoolean (konieczność w przypadku gdybyś chciał zmieniać tę wartość w wątkach), ale że jedynie odczytujesz wartość zwykły Boolean powinien wystarczyć. W kodzie poniżej zmieniłem nieco nazwy (urlField, emailField, passwordField) i usunąłem statyczne zmienne.

public class View implements Initializable {
...

	@FXML
	private PasswordField passwordField;
	
	private boolean working = false;

	@Override
	public void initialize(URL location, ResourceBundle resources) {
		
		this.start.setOnAction(event -> {
			this.working = true;
			this.circle.setFill(Color.RED);
			final Runnable taskThread = new MyRun(this.urlField.getText(), this.emailField.getText(), this.passwordField.getText(), () -> this.working);
			final Thread nextThread = new Thread(taskThread);
			nextThread.start();
			this.urlField.setEditable(false);
			this.emailField.setEditable(false);
			this.passwordField.setEditable(false);
		});
		
		this.end.setOnAction(event -> {
			this.working = false;
			this.circle.setFill(Color.rgb(245, 240, 250));
			this.urlField.setEditable(true);
			this.emailField.setEditable(true);
			this.passwordField.setEditable(true);
			
		});
	}
}
public class MyRun implements Runnable {
	
	private final String url;
	private final String email;
	private final String password;
	private final Supplier<Boolean> shouldContinue;
	
	public MyRun(final String url, final String email, final String password, final Supplier<Boolean> shouldContinue) {
		this.url = url;
		this.email = email;
		this.password = password;
		this.shouldContinue = shouldContinue;
	}
	
	@Override
	public void run() {
		while (this.shouldContinue.get()) {
			...			
		}
	}
}
0

Dzięki @AreQ212: Większości klas, które tutaj wymieniasz nie są mi znane, ale właśnie zaglądam do dokumentacji ;)

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