I am developing a Java application where when a button in the GUI is pressed, the user can click anywhere on the screen to record the x and y coordinates where they clicked.This is done by putting an undecorated, partly transparent JFrame over the entire screen that listens for a mouse click, records the x/y coords, then closes itself upon being clicked.
Transparent JFrame class (note: extends JFrame)
setUndecorated(true);
setOpacity((float) 0.75);
W = Toolkit.getDefaultToolkit().getScreenSize().width;
H = Toolkit.getDefaultToolkit().getScreenSize().height;
setSize(W,H);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
X = e.getX();
Y = e.getY();
System.out.println(X + ":" + Y);
closed = true;
dispose();
}
});
}
public void start() {
java.awt.EventQueue.invokeLater(() -> {
this.setVisible(true);
});
}
public boolean closed = false;
public int X = -1;
public int Y = -1;
method from button press (note: extends JFrame)
setVisible(false);
click_frame.start();
while(!click_frame.closed) {
}
setVisible(true);
System.out.println(click_frame.X + "," + click_frame.Y);
The problem I am having is that the while loop hangs the entire application not just the main GUI. I want to use wait() and notifyAll() because I am trying to intentionally hang the main GUI until click_frame.X and click_frame.Y have values.
When I tried other methods without the while loop, the "click_frame JFrame class" ran alongside the main GUI, instead of blocking it, which is not what I want to happen.
My question is how can I make the main GUI use wait()/notifyAll() so it will stop while the second JFrame records the input.
Swing is a single threaded framework, this means that any long running or blocking process which is executed within the context of the Event Dispatching Thread, will prevent the framework from responding to new events
My question is how can I make the main GUI use wait()/notifyAll() so it will stop while the second JFrame records the input.
Don't. Swing, like most UI frameworks is event driven, this means, something happens (at some point in time) and you respond to it.
Instead, simply make use of a MouseListener and monitor the mouseClicked event. When it occurs, you can use the MouseEvent#getLocationOnScreen method to determine where on the screen the user clicked.
When clicked, this should then report back to the caller (via some kind of observer pattern) the results of the call.
Of course, you could cheat and simply use a modal JDialog, which will block (safely) at the point the dialog is made visible, until it is dismissed (closed)
Related
I have this loop
while (true) {
game.update();
view.repaint();
Thread.sleep(DELAY);
}
In the game.update various components of the game have their position changed and those updates are reflected when the repaint() method is called on the view. The view extends JComponent and loops through the game objects and calls their print methods.
What I want to do is have a boolean called nextLevel in the game and if it's true Flash text on the screen for the player to notify them that they're going onto the next level. Maybe flash 4-5 times. Then continue the game.
Is this possible? I have been playing around with Thead.Sleep() but this only seems to pause the displaying and in the background the game is still going on.
Any ideas on how to do this?
Maybe you want to avoid threading by using a Timer object.
an example like that could be
int flashTimer = 0;
if(nextLevel) {
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
//flash something method here
flashTimer++;
}
});
timer.start();
}
and then check your flashTimer if it reaches the number you want then just stop the timer by timer.stop();
Just an idea which seems to me a bit simpler. the 1000 value is milliseconds which is passed and executes the code inside the actionPerformed method every 1 sec.
Hope it helped
I am adding a feature to a program to save some content to file. The progress is shown by a progress bar (in its own JFrame), but the progress bar is only being displayed on the last value it reads. I have a global being updated by the main thread, that represents the % of work completed, and the other thread reads this global and updates the progress bar accordingly.
Right now when it runs, the JFrame is empty, then activity completes, then the progress bar shows itself with complete amount. How do i make it update the progress as it goes along (and show the JProgressbar from the start)? Here is my code:
public class GenomeAnnotator{
private JProgressBar csvProgressBar;
private JFrame csvSaveLoadFrame; //for the progress bar
private Container csvCon;
private double csvPercentSaved; //% of work completed
public JFrame m_frame; //main program frame
....
public static void main(String[] args){
...
showGUI();
...
}
public void showGUI(){
...
JMenu file = new JMenu("File");
JMenu exptann = new JMenu("Export annotation..);
JMenuItem exptcsv = newJMenuItem("CSV format");
exptcsv.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
..determine output file + checks...
System.out.println("Writing to .csv file......");
csvSaveLoadFrame = new JFrame("Saving to csv file..");
csvProgressBar =new JProgressBar(0,100);
csvSaveLoadFrame.setSize(300,100);
csvCon = csvSaveLoadFrame.getContentPane();
csvCon.setLayout(null);
csvProgressBar.setBounds(10,10,280,20);
csvCon.add(csvProgressBar);
csvSaveLoadFrame.setResizable(false);
csvSaveLoadFrame.setVisible(true);
ORF [] ora= orfPanel.getAcceptedOrfs();
int val;
double toload = blastData.size() + ora.length; //how much work
double loaded=0.0; //how much work completed
/*Thread that will read % value from global and update prog. bar*/
Thread progressBarMover = new Thread() {
#Override
public void run() {
int previous=0;
while(csvPercentSaved<100){
csvProgressBar.setValue((int)csvPercentSaved);
//tried putting a sleep() in here when testing
//values from global is read successfully
}
}
System.out.println("Thread done!");
csvPercentSaved = 0; //reset value when done
csvSaveLoadFrame.setVisible(false);
}
};
progressBarMover.start();
for (int k=0; k<blastData.size(); k++) {
..do output work...
loaded+=1; //update % values
csvPercentSaved = (loaded/toload)*100;
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
for (int k=0; k<ora.length; k++) {
...do more ouput work...
loaded+=1;
csvPercentSaved = (loaded/toload)*100; //update % value
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
System.out.println("Output file finished!");
csvPercentSaved = 100;
}
});
exptann.add(exptcsv);
file.add(exptann);
}
EDIT
found solution here:
https://weblogs.java.net/blog/mkarg/archive/2010/01/03/did-you-know-swingworker-can-send-progress-status
Several issues there:
Most most important (and I missed this initially!), you're not doing your long running code within the background thread but rather within the Swing event thread, the EDT. I am meaning these two for loops: A) for (int k=0; k<blastData.size(); k++) {...} and B) for (int k=0; k<ora.length; k++) {...} which looks to be the code where you're loading or saving information. This will freeze your GUI right up.
Also important, you're doing Swing calls from within a background thread, including setting the progress bar's value and setting a JFrame's visiblity, something that you never want to do, and that mostly negates the benefits of using the background thread in the first place.
In other words, you're doing all your Swing threading work exactly backwards -- making Swing calls from the background thread and running the long process in the event thread.
Instead, do the opposite -- do all the long-running work in a background thread and make all of the non-thread-safe Swing calls on the EDT.
One way to do this is to use a SwingWorker, do your loading and saving from within its doInBackground(...) method
and set its progress field as progress is being made..
You would then monitor the worker's progress field in a PropertyChangeListener, this being done on the EDT, and then use this to set your progress bar's value.
Or if you have to use your own background thread, then
Have the inner class implement Runnable, not extend Thread
If you make Swing calls from within your background thread, then wrap these calls in a Runnable and queue them onto the Swing event thread via SwingUtilities.invokeLater(yourRunnable)
More minor issues:
You should not be using null layouts and absolute positioning but rather use layout managers. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Your secondary dialog window should be a JDialog, and probably a modal JDialog, not another JFrame. You're not creating and showing a new stand-alone program, but rather are displaying a dialog window off of the main GUI window. If you want the main GUI window non-functioning while the dialog is displayed, then the modal JDialog is the way to go, as it works just like a JOptionPane (which is a form of a modal JDialog), and makes the calling window non-functional while its visible.
For some of my code examples:
How do I make my SwingWorker example work properly?
For a lot more of my examples
I want my app to detect mouse clicks anywhere on the screen without having to have the app focused. I want it to detect mouse events universally even if its minimized. So far I've only been able to detect mouse events within a swing gui.
Autohotkey can detect mouse clicks and get the mouse's position at any time, how can I do this with java?
It is possible with a little trick. Should be 100% cross-platform (tested on Linux & Windows). Basically, you create a small JWindow, make it "alwaysOnTop" and move it around with the mouse using a timer.
Then, you can record the click, dismiss the window and forward the click to the actual receiver using the Robot class.
Short left and right clicks work completely fine in my tests.
You could also simulate dragging and click-and-hold, just forwarding that seems harder.
I have code for this, but it is in my Java extension (JavaX). JavaX does translate into Java source code, so you can check out the example here.
The code in JavaX:
static int windowSize = 11; // odd should look nice. Set to 1 for an invisible window
static int clickDelay = 0; // Delay in ms between closing window and forwarding click. 0 seems to work fine.
static int trackingSpeed = 10; // How often to move the window (ms)
p {
final new JWindow window;
window.setSize(windowSize, windowSize);
window.setVisible(true);
window.setAlwaysOnTop(true);
JPanel panel = singleColorPanel(Color.red);
window.setContentPane(panel);
revalidate(window);
final new Robot robot;
panel.addMouseListener(new MouseAdapter {
// public void mousePressed(final MouseEvent e) {}
public void mouseReleased(final MouseEvent e) {
print("release! " + e);
window.setVisible(false);
int b = e.getButton();
final int mod =
b == 1 ? InputEvent.BUTTON1_DOWN_MASK
: b == 2 ? InputEvent.BUTTON2_DOWN_MASK
: InputEvent.BUTTON3_DOWN_MASK;
swingLater(clickDelay, r {
print("clicking " + mod);
robot.mousePress(mod);
robot.mouseRelease(mod);
});
}
});
swingEvery(window, trackingSpeed, r {
Point p = getMouseLocation();
window.setLocation(p.x-windowSize/2, p.y-windowSize/2);
//print("moving");
});
}
I was programming a GUI today, which is doing longer calculations when pressing a button. While the calculations are running, I wanted to use intermediate results of the still running calculation and write them to a JLabel. The GUI however, should not be operable by the user before the calculation has finished.
At first I was doing something like this:
(1)
public class GUI extends JFrame {
JLabel label = new JLabel("Status: ");
public GUI(){...}
public void calculate() {
for(int i = 0; i < 10; i++) {
String one = calculationPartOne(i);
label.setText("Status: " + one);
label.repaint(); //*
calculationPartTwo(i);
}
}
}
This did not work, the JLabel would only update after the calculation has finished. I also tried to .repaint() and .validate() all components involved at the line commented *, but it did nothing.
So, after trying and searching Google/StackoOverflow the whole day I finally have a working solution, but I still do not understand why above does not work. I wanted the GUI to block, so naturally I ran the calculation in the same thread. However, calling any methods to repaint the GUI -inbetween- the calculation (making the calculation stop while the GUI is updated) did not work, and I do not understand why. Can someone explain?
In the end, I used the SwingWorker class to do my calculations, and use it's functions to update the JLabel while calculating. However, as I need the GUI to block, I now disable -all- the components before excuting the SwingWorker and have the SwingWorker re-enable all the components after finishing the calculation.
So, I use SwingWorker, to not block the EDT, but then "fake" to block the EDT by disabling everything? This seems really paradox to me.
Here is an outline of what I have now:
public class GUI extends JFrame {
JLabel label = new JLabel("Status: ");
//I didn't use a list, but it works to illustrate it here
List<Component> GUIComponents = ...;
public GUI() {...}
public void calculate() {
SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
protected Void doInBackground() throws Exception {
for(int i = 0; i < 10; i++) {
String one = calculationPartOne(i);
publish(one);
calculationPartTwo(i); //**
}
}
protected void done() {
setEnableGUI(true);
}
protected void process(List<String> chunk) {
label.setText(chunk.get(chunk.size() - 1));
}
};
setEnableGUI(false);
worker.execute();
}
public void setEnableGUI(boolean e) {
for(Component c : GUIComponents) {
c.setEnabled(e);
}
}
//**
public void calculationPartTwo() {...}
}
This works.
I hope someone can clarify. This solutions feels wrong.
why wrong? the gui thread is for responding to user events only - so you should be doing your heavy lifting in the background - which is what youre doing with a SwingWorker.
also, the best way to prevent a user from changing a componenet is to do exactly that - disable the component before starting the heavu lifting, and enable once its done.
only thing you might want to consider is displaying the results of your calculation in a modal dialog - a JDialog that will pop above the parent window and block it. you could display the intermediate results and progress in this dialog and then once the calculation is done the dialog will close and unblock the UI obscured by it. this will save you fron having to disable all gui components indiviually in a loop and will also give you an option to have a "cancel" button to halt the work immediately.
However, calling any methods to repaint the GUI -inbetween- the calculation (making the calculation stop while the GUI is updated) did not work, and I do not understand why. Can someone explain?
repaint() requests are handled by the RepaintManager and are not done immediately. The RepaintManager basically schedules the repaint. Since repainting is done on the EDT, it can't be done until the EDT is free.
So, I use SwingWorker, to not block the EDT, but then "fake" to block the EDT by disabling everything? This seems really paradox to me.
You can always use an indeterminated JProgressBar. See How to Use Progress Bars.
Or maybe you would prefer to use the Disabled Glass Pane approach.
In some cases you can use:
label.paintImmediately(...);
to force the repainting of a component. But you still have the issue of disabling the GUI so its probably not a solution you should really be using.
I want to know how to make a JDialog resizable when it has been set setUndecorated(true);. Resizing can be done by the outer frame.When it is undecorated, no frame and then the JDialog cannot be resized. How can I resize the JDialog when its outerframe is not available?
I assume you mean how can a user resize an undecorated dialog (as the developer you can always call setSize)
The only way this can be done is to implement the logic yourself.
This will require to implement a MouseListener and monitor the mousePressed, mouseReleased and mouseDragged events
You need to take into consideration that you probably only want gutter around the dialog that will trigger a resize (say 5 pixels). It's important that no other components lay within this gutter, as they will override you mouse listener.
Haven't tested this, but from what I gather it appears that you just need the following method:
// window reszie event
private void _setUpWindowResizeEvent() {
this.addMouseMotionListener(
new MouseMotionListener() {
public void mouseDragged(MouseEvent mouseEvent) {
if( mHozResizeEnabled || mVerResizeEnabled ) {
int x = mouseEvent.getX();
int y = mouseEvent.getY();
_resizeWindow(
MouseInfo.getPointerInfo().getLocation() );
sticky.setSize( getSize() );
_fireStickyUpdated();
}
}
Reference:
http://tech.groups.yahoo.com/group/JPGroup/message/1877