I wrote my program in two parts, I wrote the actual functionality first, and then the GUI to display it all.
I need to wait/pause for a user to click a "Done" button from another class(DisplayImages) before continuing executing code.
My DisplayImages class takes a list of MyImage. The images are then displayed in a jpanel and the user selects a couple of images
and then clicks a 'Done' button. How can I wait for a response or something like that?
public class One{
ArrayList<MyImage> images = new ArrayList<MyImage>();
public One(){
DisplayImages displayOne = new DisplayImages(images);
displayOne.run();
//I need to pause/wait here until the user has pressed the done button
//in the DisplayImages class
images.clear();
images = displayOne.getSelectedImages();
//do stuff here with images arrylist
}
}
DisplayImages class
public class DisplayImages extends JFrame{
private ArrayList<MyImage> images = new ArrayList<MyImage>();
private ArrayList<MyImage> selectedImages = new ArrayList<MyImage>();
public DisplayImages(ArrayList<MyImage> images){
this.images = images;
}
public void run(){
//code creates a jpanel and displays images along with a done button
//user presses done button
done.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
setVisible(false);
selectedImages = getSelectedImages();
//here I would send a signal to continue running in class One
}
});
}
private ArrayList<MyImage> getSelectedImages(){
//code returns list of all the images the user selected on JPanel
return results;
}
}
If for some reason you need to open and process the dialog in the same method then using the dialog approach suggested with JOptionPane seems fine. However this seems bad design (opening a frame and waiting for input in a constructor?). I would prefer an approach similar to the one below (please read my inline comments):
public class One {
ArrayList<MyImage> images = new ArrayList<MyImage>();
public One() {
// perform only initialization here
}
// call this method to create the dialog that allows the user to select images
public void showDialog() {
DisplayImages displayOne = new DisplayImages(images);
// pass a reference to this object so DisplayImages can call it back
displayOne.run(this);
}
// this will be called by the action listener of DisplayImages when Done is clicked
public void processSelectedImages(List<MyImage> selectedImages) {
images.clear();
images = selectedImages;
// do stuff here with images arrylist
}
}
public class DisplayImages {
...
public void run(final One callback){ // Note that now a reference to the caller is passed
// creates jpanel and displays images along with a done button
// user presses done button
done.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
setVisible(false);
selectedImages = getSelectedImages();
// here is how we can send a signal to notify the caller
callback.processSelectedImages(selectedImages);
}
});
}
...
}
As a side note please do not name your methods run() if you are not implementing the Runnable interface and/or using threads. This is very confusing
That is quite easy, some people here are thinking to complicated. You will need no multithreading, you just need a modal dialog. JOptionPane provides easy access to them.
I modified your code:
public class One{
ArrayList<MyImage> images = new ArrayList<MyImage>();
public One(){
DisplayImages displayOne = new DisplayImages(images);
int n = JOptionPane.showConfirmDialog(null, displayOne);
if (n == JOptionPane.OK_OPTION){
//I need to pause/wait here until the user has pressed the done button
//in the DisplayImages class
images = displayOne.getSelectedImages();
//do stuff here with images arrylist
}
}
}
MyImage class
public class DisplayImages extends JPanel{
private ArrayList<MyImage> images = new ArrayList<MyImage>();
public DisplayImages(ArrayList<MyImage> images){
this.images = images;
//code creates a jpanel and displays images along with a done button
}
public ArrayList<MyImage> getSelectedImages(){
//code returns list of all the images the user selected on JPanel
return results;
}
}
You will have to use threading.
http://docs.oracle.com/javase/tutorial/essential/concurrency/
Then you can set up each instance on a separate thread, and either use the built in Object.notify and Object.wait
Or have yourself a global flag variable, static, available to both classes, which you change to notify the other class that the done button was clicked.
Related
I have a main frame which has an array list containing a list of items for an order. I then have a button which creates a new window that has a form allowing the user to pick multiple options for an item, this information is then put into an object.
I want to return this object back to the original frame so that I can add it to the order array list. However I'm not sure how to go about this as my frames have their code auto generated as I'm using netbeans.
You should use a listener (interface) system. Create an interface and implement it in your main frame, when you create your second frame you pass the first one as parameter. By this way you will be able to call a method, let's say, onItemSelected in the second frame whenever you want in an elegant way.
Using interfaces is more convenient, you can have multiple listener.
There is an example:
class MyFrame extends JFrame implements ItemSelectedListener {
void onButtonClick() {
new SecondFrame(this);
}
#Override
public void onItemSelected(List<String> items) {
// do your stuff with the selected items here
}
}
interface ItemSelectedListener {
void onItemSelected(List<String> items);
}
class SecondFrame extends JFrame {
private ItemSelectedListener itemSelectedListener;
private JTextField name;
private JButton buttonOk;
SecondFrame(ItemSelectedListener listener) {
itemSelectedListener = listener;
name = new JTextField();
buttonOk = new JButton("OK");
getContentPane().add(name);
getContentPane().add(buttonOk);
buttonOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<String> myFormItems = new ArrayList<>();
// fulfill your list with all informations that you need
myFormItems.add(name.getText());
// notify your main frame that the user finished to complete the form
itemSelectedListener.onItemSelected(myFormItems);
}
});
}
}
I have a class (TaskInOurProgram) which contains ImageView in constructer.
Here is a little code snipped - declaration and then constructer:
private ImageView closeTask;
private ImageView pauseTask;
private final Image pauseImage = new Image("file:images/pause.png");
private final Image closeImage = new Image("file:images/close.png");
public TaskInOurProgram(String name, String configName, String extensionOfTheFile) {
this.nameOfTheFile = name;
this.configName = configName;
this.extensionOfTheFile = extensionOfTheFile;
this.closeTask = new ImageView();
closeTask.setImage(closeImage);
this.pauseTask = new ImageView();
pauseTask.setImage(pauseImage);
}
Then I have a observableArrayList which contains many of these TaskInOurProgram objects.
private ObservableList<TaskInOurProgram> tasksInTheTableView = FXCollections.observableArrayList();
I have a tableView which displays this list of objects. So then it looks like this.
Object are not there from the beginning, they need to be added by clicking at button ADD TASK and because it is observableList, it will show this "tasks" immediately after adding.
WHAT I NEED: What I need is to create method inside of the controller which will do something after click on "pause" ImageView. I really do not know how to do it, I was thinking about listeners somehow. And before that I need to put click event on imageView.
Can you guys, please, help me with this? Thanks for any idea.
Try using a Button with a Graphic instead of a plain ImageView. That way you can use the Button.setOnAction() method.
Where you are currently defining your ImageView, change it to the following:
this.pauseTask = new Button("", new ImageView(pauseImage));
this.pauseTask.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
pause();
}
});
You can define a method somewhere else in your program, something like this:
private void pause() {
// your code here
}
Use setOnMouseClicked() method for imageView to handle click events.
pauseTask.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event)
{
pauseTask();
}
});
Then you can handle it in other method
void pauseTask()
{
}
I have 2 classes, one class is a JFrame (MainUIHolder.java) and the other class is a JDialog (EditValuationsDialog.java). MainUIHolder can call EditValuationsDialog on button click event.
Once EditValuationsDialog is open, user can enter data in its fields and press its "Add" button. OK, here is the issue now. Once the user press the "Add" button, the EditValuationsDialog should inform that to the MainUIHolder.
Below is the code.
MainUIHolder
Action edit = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
JTable table = (JTable)e.getSource();
int rowNum = Integer.valueOf(e.getActionCommand());
Object valueAt = table.getModel().getValueAt(rowNum, 0);
EditValuationsDialog edit = new EditValuationsDialog(null,true);
edit.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
edit.setTitle("Edit Valuations");
edit.setClientName(portfolioViewClientName.getText());
edit.setPortfolioType(portfolioViewInvestmentTypeCombo.getSelectedItem().toString());
edit.setPortfolioId(id);
edit.setOngoingValuationsId(Integer.parseInt(String.valueOf(valueAt)));
edit.setLocationRelativeTo(table);
edit.setVisible(true);
//CATCH THE CALL FROM EditValuationsDialog HERE!!!!//
}
};
EditValuationsDialog
//Action Listeners
private class AddBtnAction implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(someCondition)
{
return String / int to MainUIHolder (See where I want to catch it in MainUIHolder)
}
else
{
do nothing
}
}
}
In my code I have indicated from where the call to MainUIHolder should be generated and in what place I must catch that call in MainUIHolder. How can I do this call back work?
You could...
Add a static method to EditValuationsDialog that shows the dialog, evaluates the results and returns the value you are expecting...
public void actionPerformed(ActionEvent e)
{
int result = EditValuationsDialog.showDialog();
}
public class EditValuationsDialog ... {
//...
private int result = -1;
//...
public int getResult() {
return result;
}
//...
public static int showDialog(Component source, int rowNum, Object valueAt) {
EditValuationsDialog edit = null;
Window parent = SwingUtilities.windowFor(source);
if (parent instanceof Frame) {
edit = new EditValuationsDialog((Frame)parent,true);
} else if (parent instanceof Dialog) {
edit = new EditValuationsDialog((Dialog)parent,true);
} else {
edit = new EditValuationsDialog(null,true);
}
edit.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
edit.setTitle("Edit Valuations");
edit.setClientName(portfolioViewClientName.getText());
edit.setPortfolioType(portfolioViewInvestmentTypeCombo.getSelectedItem().toString());
edit.setPortfolioId(id);
edit.setOngoingValuationsId(Integer.parseInt(String.valueOf(valueAt)));
edit.setLocationRelativeTo(source);
edit.setVisible(true);
return edit.getResult();
}
//...
private class AddBtnAction implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(someCondition)
{
result = 0;
}
else
{
result = 1;
}
EditValuationsDialog.this.dispose();
}
}
}
Or you could...
Simply evaluate the results of getResult() from the above example directly...
Side note: Because I don't like extending from top level containers like JDialog, I tend to create some of my panels/components with static showDialog methods, thing something along the lines of a login panel for example. It means I could re-use the panel else where, but provides me with the convenience of been able to popup a dialog when I need to. I've also used JOptionPane from time to time to show these panels, but it depends on the complexity of the available actions...
Make the dialog modal (setModal(true)). Then the code after dialog.setVisible(true) is executed after the dialog is closed.
BTW it's better to pass the MainUIHolder JFrame instance as parent of the dialog.
You could add an interface to the EditValuationsDialog something like this:
Interface EditValuationsDialogInterface {
public void onAddClicked(addedVlue);
}
and then add it as such:
edit.setOnAddButtonCallback(new EditValuationsDialogInterface () {
#Override
onAddClicked(addedVlue){
//DO SOMETHING
}
});
in your EditValuationsDialog's add button onclick call add this:
onAddButtonClickedCallback.onAddClicked(retunrValue);
This allows you to have a direct link back to the original calling class.
Not sure how well I will explain this; I'm quite new to programming...
So I'm trying to make a desktop application that draws musical notes of a given type on some sheet music when the user selects the button corresponding to that type of note. Currently, if the user selects the "Whole Note" button, the user can then start clicking on the screen and the note will be drawn where the click occurred. It will also make a "ding" sound and write some info about that note to a text file.
That's all well and good, but unfortunately when the user selects a new button, say the "Quarter Note" button, for each mouse click there will be two notes drawn (one whole, one quarter), two dings, and two packets of info written to the file. I have no idea how to make this work! Currently, I'm trying to use threads, such that each button creates a new thread and the thread currently in use is interrupted when a new button is pressed, but that doesn't resolve the issue.
Initially, an empty linked list of threads ("NoteThreads") is constructed. Also, a private class known as SheetMusicPane (given the variable name "smp") is constructed in order to draw the sheet music. The buttons are added in the main constructor (public CompositionStudio), whereas the method containing the mouse listener (see what follows) is contained in the SheetMusicPane private class. Not sure whether that is part of the problem.
I have a button action listener:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!threads.isEmpty()) {
NoteThread oldThread = threads.remove();
oldThread.interrupt();
}
NoteThread newThread = new NoteThread(e.getActionCommand());
threads.add(newThread);
newThread.run();
}
});
that produces a thread:
private class NoteThread extends Thread {
private String note;
public NoteThread(String note) {
this.note = note;
}
public void run() {
smp.getShape(smp.getGraphics(), note);
}
}
that when, on running, calls this method with graphics and a mouse listener:
public void getShape(final Graphics g, final String note) {
this.addMouseListener(new MouseListener() {
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
addShape(g, p.x, p.y, note);
int pitch = 12;
piano.playNote(pitch);
advance(1.0, piano);
try { addToFile(pitch, note);}
catch(FileNotFoundException fnfe) {}
catch(IOException ioe) {}
}
});
}
The above method is responsible for drawing the note ("addShape()"), making the ding sound, and writing to the file.
Thanks in advance for any help you can give!
what you're trying to do does not require multithreading. This is the approach that I'd take:
set up a set of toggle buttons or radio buttons to select the note to paint. this way, only one note will be selected at a time. add action listeners to those that store in an adequately scoped variable what note is selected, or infer that every time a note should be drawn. this way, you don't even add action listeners to the buttons. in any case, don't spawn new threads.
in your mouse listener, find out what note to draw, and do that - only one note.
if you can, stay away from multithreading, especially as a beginner. also, I think you confuse adding and running listeners here. each call to getShape() adds a new listener, meaning they accumulate over time, which might be the cause of your problems.
PS: welcome to stackoverflow! your question contained the important information and I could infer that you tried solving the problem yourself. It's pleasant to answer such questions!
One solution would be to simply fetch all the listeners (which should be 1) and remove them before adding the new listener:
public void getShape(final Graphics g, final String note) {
MouseListener[] listeners = this.getMouseListeners();
for (MouseListener ml : listeners) {
this.removeMouseListener(ml);
}
this.addMouseListener(new MouseListener()...);
}
An alternative, since you have a finite number of buttons, would be to create a finite set of listeners, eg:
private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;
Create a reference to the "current" listener (private MouseListener current;), and have a means of deciding which listener to use whenever getShape is called (a series of if conditions on the note String would work, although I would prefer some refactoring to use an enum personally). Then you could do something along the lines of:
private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;
private MouseListener current;
...
public void getShape(final Graphics g, final String note) {
if (current != null) {
this.removeMouseListener(current);
}
if (...) { // note is Whole Note
current = wholeNote;
} else if (...) { // note is Quarter Note
current = quarterNote;
} // etc.
this.addMouseListener(current);
}
Another alternative would be to change your listener so that you only ever need the one, but clicking a button changes a variable which the listener has access to. For example:
// In the listener
public void mouseClicked(MouseEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
addShape(g, p.x, p.y, currentNote);
int pitch = 12;
piano.playNote(pitch);
advance(1.0, piano);
try { addToFile(pitch, currentNote);}
catch(FileNotFoundException fnfe) {}
catch(IOException ioe) {}
}
// In the same class
protected String currentNote;
...
public void getShape(final Graphics g, final String note) {
currentNote = note;
}
How can I display a series of images one by one every time the user clicks the next button?
I have few images stored in array. I used button next to go from image to image. How to programme the button? I used action listener but I just don't know how to get back to the array.
I know I'm being lazy here by not writing the entire code, but here goes anyway.
class whatEver implements ActionListener{
JButton btnNext = new JButton();
static int fileNumber = 0;
static int totalFiles;
void functionLoadInterface () //Function with
{
btnNext.addActionListener(this);
//Initialize array ImageArray with file paths.
}
public void cycleImage()
{
String file = ImageArray[fileNumber%totalFiles];
//Code to load the image wherever follows
}
public void actionPerformed(ActionEvent e)
{
filenumber++;
this.cycleImage();
}