I've got several unrelated swing classes that are instances of JFrame. The issue is, I'd like to be able to create a controller class with a static method to load the appropriate frames as needed. I'm experiencing difficulty with the following code as many of components are failing to load. I suspect that the issue is with the event dispatch thread. Below is the code that doesn't work:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Controller {
private Controller() {
}
public static void initialize(final JFrame frame) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame selectionFrame = frame;
}
});
}
public static void main(String[] args) {
Controller.initialize(new SampleFrame());
}
}
The program loads the frame and toolbar components but other components fail to load. NOTE: The program runs just fine if I refactor the Controller like so :
public class Controller {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SampleFrame();
}
});
}
}
Related
So I wanted to make a timer to know when the user presses a button, however it doesn't seem to work the way it should.
When I put something in the public void actionPerformed() method it doesn't repeat at all - it should do it every 10th millisecond as I told it to. I have no clue what it might be because there are 0 warnings and 0 errors.
Here is the code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class timertest {
static Timer timer = new Timer(10,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test");
}
});
public static void main(String[] args) {
timer.start();
}
}
Because you are not starting it inside the Event Dispatch Thread.
public class TimerTest {
static Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test");
}
});
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> { //Run in EDT
timer.start();
});
}
}
Also, have in mind that it is highly recommended (plus it helps us) to follow standard naming conventions - All class names should start with an Uppercase letter.
I am trying to change the text inside a jLabel from the main method, the reason for this is because there are conditions that I need to meet for the change to happen and it is not trigger based.
Code:
public class TheMain extends javax.swing.JFrame {
public TheMain() {
initComponents();
}
public void changeLabel1(){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jLabel1.setText("looo");
}
});
}
public void changeLabel2(){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jLabel2.setText("looo");
}
});
}
public static void main(String args[]) {
TheMain some = new TheMain();
if(condition){
some.changeLabel1();
}else{
some.changeLabel2();
}
}
}
I tried printing some stuff inside changelabel1 and changelabel2 just to check if they are successfully called and it did print but I'm guessing it is not possible to implement the UI changes inside them, or am I mistaken?
if(condition){
some.changeLabel1();
}else{
some.changeLabel2();
}
The above logice needs to be defined in TheMain class because that is where the label variables will be defined.
The main() method is just used to create the GUI. There should be no application logic in the main() method.
following Scenario:
JavaFxMainApp
JavaFXUpdaterApp
Both are JavaFX applications with a GUI and a void main() method.
The Updater has to be able to start the JavaFXMainApp by accessing
the JavaFxMainApp.jar ONLY knowing about the main class -> it can only! call
main().
The JavaFxMainApp has to also be able to run on its own, by starting
main().
I cannot start multiple VMS and the two apps have no means of communication.
Problem with this is:
Application.launch() can only be executed once per JVM.
The standard javaFx way to start the app:
Updater + MainApp
public static void main(String[] args) {
launch(args);
}
Here it is impossible to fullwill Requirement 1). As both main() methods call launch().
A second approach i found is:
Updater + MainApp
public static void main(String[] args) {
Application app2 = JavaFxMainApp.class.newInstance();
Stage anotherStage = new Stage();
app2.start(anotherStage);
}
First off, i lose the possibility to pass args, but i can live with that as they would not be used anyways.
Major Problem here is, that this code ONLY works, if the JVM already has a JavaFx Thread running, hence it requires that launch() has been called in the JVM at some point before. This is not the case as none of the two calls launch() anymore.
Hybrid approach:
Updater calls launch(), MainApp takes the second approach
Requirement 2) cannot be fulfilled, als starting MainApp without launcher Updater is impossible now.
Another idea i had where dirty "try launch() catch()-> try second approach() in both apps, but that couples both apps and make the setup less flexible.
Is there a way to accomplish this without having to override JavaFxs' LauncherImpl or Application classes to fit these needs?
Can you do something like this:
public class MainApp extends Application {
private Parent uiContent ;
public static final double DEFAULT_WIDTH = 800 ;
public static final double DEFAULT_HEIGHT = 600 ;
public Parent getContent() {
if (uiContent == null) {
uiContent = initializeUI();
}
return uiContent ;
}
public Scene createScene() {
return new Scene(getContent(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
public void initializeAndShowStage(Stage stage) {
stage.setScene(createScene());
stage.show();
}
private Parent initializeUI() {
// probably wise to check we are on the FX Application thread here...
Pane root = ... ;
// build ui....
return root ;
}
#Override
public void start(Stage primaryStage) throws Exception {
initializeAndShowStage(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
And
public class UpdaterApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// whatever you need to launch the updater app here...
}
// invoke from the FX Application Thread to "start" main app:
private void showMainApp(Stage stage) {
MainApp app = new MainApp();
app.initializeAndShowStage(stage);
}
private void showMainApp() {
showMainApp(new Stage());
}
public static void main(String[] args) {
launch(args);
}
}
This would be the far preferred approach. If you have requirements that force you to call main, then you could try something like this:
public class MainApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// .... build UI etc
}
public static void main(String[] args) throws Exception {
if (Platform.isFXApplicationThread()) {
Stage someStage = new Stage();
MainApp app = new MainApp();
app.start(stage);
} else {
launch(args);
}
}
}
Then your updater app can just call MainApp().main(new String[0])); from the FX Application Thread.
This feels like a bit of a hack though.
You want your MainApp to start of its own and you also want an UpdateApp to start the MainApp when required then you can follow my step. I have tried and this model is working.
This is the starting point. You need to call this Class to begin your application
public class StartAnApp {
public static void main(String[] args){
new Thread(new MainApp()).start(); // this will call your MainApp
}
}
This is the First Application which will start. As to start a JavaFX Application you need to have a main() methods. So ensure to provide a main method in this class.
public class MainApp extends Application implements Runnable{
public MainApp(){} // constructor
#Override
public void start(Stage stage){
Text text = new Text("MainApp");
Button startUpdate = new Button("Start Update");
// When this button is pressed. It will launch UpdateApp Application
startUpdate.setOnAction( e -> {
Platform.runLater(new Runnable(){
#Override
public void run(){
new UpdateApp().start(new Stage());
}
});
});
Group root = new Group(text);
Scene scene = new Scene(root,300,400);
stage.setScene(scene);
stage.setX(0);
stage.setY(0);
stage.show();
}
// This method will be used when you first start an Application for
// which main method is required
public static void main(String[] args){
launch(args);
}
// This will be used when you call this Application from another JavaFX application
// if you have closed this application before
#Override
public void run(){
launch();
}
}
This is your UpdateApp. This method does not have main() method.
public class UpdateApp extends Application implements Runnable{
public UpdateApp(){} // constructor
#Override
public void start(Stage stage){
Text text = new Text("UpdateApp");
Button startAnother = new Button("Start Another");
// When this button is pressed. It will launch MainApp Application or you can add any other JavaApplication
startAnother.setOnAction( e -> {
Platform.runLater(new Runnable(){
#Override
public void run(){
new MainApp().start(new Stage());
}
});
});
Group root = new Group(text);
Scene scene = new Scene(root,300,400);
stage.setScene(scene);
stage.setX(350);
stage.setY(0);
stage.show();
}
// This will be used when you call this Application from another JavaFX application
#Override
public void run(){
launch();
}
}
I have a class on which it hold a JFrame, and on my JFrame, it contains some methods that i would want to call. The problem is that my class is in the main thread and mFrame is now delegated to the EDT thread, so how do i sent instructions to EDT to run those methods?
Update:
This is my FrameRadar that extends JFrame, as you see i have a update method, basically there is also a update on mDisplay and mRadar, those are jpanel.
FrameRadar Class ...(
public class FrameRadar{
...
public void update() {
mAccessor.getmDisplay().update();
mAccessor.getmRadar().update();
}
This is my MainRadar class, it contains a FrameRadar on which i also have a update method. That update method will call jframe's update.
class MainRadar(
....
private FrameRadar mFrame;
....
public StateMainGame() {
init();
}
public void init() {
....
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
mFrame = new FrameRadar();
mFrame.setVisible(true);
}
});
}
....
public void update() {
mFrame.update();
}
So as you see, MainRadar.Update-> FrameRadar->Update-> Jpanels->Update. The problem is that i cannot use
mFrame.update();
I have read about that when programming Java Swing, we should put these components into Java Event Queue, because Java Swing thread is not-thread safe.
But, when I use Event Queue, I don't know how to update component properties (for example : set text for label or change something..). Here is my code :
public class SwingExample {
private JLabel lblLabel;
SwingExample(){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
lblLabel = new JLabel("Hello, world!", JLabel.CENTER);
frame.getContentPane().add(lblLabel); // adds to CENTER
frame.setSize(200, 150);
frame.setVisible(true);
}
public void setLabel(){
lblLabel.setText("Bye Bye !!!");
}
public static void main(String[] args) throws Exception
{
SwingExample example = null;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
example = new SwingExample(); // ERROR : Cannot refer to non-final variable inside an inner class defined in different method
}
});
// sometime in the futures, i want to update label, so i will call this method...
example.setLabel();
}
}
I know, if I write SwingExample example = new SwingExample(); the error won't appear again, but if i use that, I cannot process example.setLabel later.
Please tell me about this error and how to fix this.
Thanks :)
By having your SwingExample instance as a field, you can reference it inside the inner classes without it being final.
public class SwingExample {
private JLabel lblLabel;
private static SwingExample instance;
SwingExample() {
// code omitted
}
public void setLabel() {
lblLabel.setText("Bye Bye !!!");
}
public static void main(String[] args) throws Exception {
EventQueue.invokeLater(new Runnable() {
public void run() {
instance = new SwingExample();
}
});
// ...
EventQueue.invokeLater(new Runnable() {
public void run() {
instance.setLabel();
}
});
}
}