How to make nearly 1,70,000 marker dots on JMapViewer? [duplicate] - swing

I've got a slight problem, I'm writing a gps tracking app to track several objects at once. The data comes in over a serial interface, this is coming in fine from what I can tell. The issue is that I need to continually update the JPanel where the map is created and displayed.
public JPanel mapDisplay(){
JPanel mapPanel = new JPanel();
mapPanel.setSize(560, 540);
Coordinate start = new Coordinate (-34.9286, 138.6);
trackMap.addMapMarker(new MapMarkerDot(1Lat, 1Lon));
trackMap.setDisplayPosition(start,8);
System.out.println(1Lat);
mapPanel.add(trackMap);
mapPanel.setVisible(true);
return mapPanel;
}
This is what I have and it's happy to display the point once but won't update. If I print out the 1Lat variable in the serial method it continually prints, however it only does it once here.
A lot of the answers I've found refer to setting markers by arrays, however that won't work in this case as the objects I'm tracking could be anywhere.
Any help would be greatly appreciated :)

Is it possible to use a worker thread and not use an ArrayList? I would run the risk of missing data if I do.
Not necessarily. In a SwingWorker, your implementation of the doInBackground() method can publish() results as they become available. Note in particular that "Results from multiple invocations of publish() are often accumulated for a single invocation of process()." In your process(), simply loop through the List<Coordinate>, update the route and repaint() the map.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
import org.openstreetmap.gui.jmapviewer.Coordinate;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
import org.openstreetmap.gui.jmapviewer.MapPolygonImpl;
/**
* #see http://stackoverflow.com/a/37193636/230513
*/
public class MapWorkerTest {
private final List<Coordinate> route = new ArrayList<>();
private void display() {
JFrame f = new JFrame("MapWorker");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMapViewer map = new JMapViewer() {
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
#Override
public String getToolTipText(MouseEvent e) {
Coordinate c = (Coordinate) getPosition(e.getX(), e.getY());
return c.getLat() + " " + c.getLon();
}
};
map.setToolTipText("");
Coordinate start = new Coordinate(-34.9286, 138.6);
route.add(start);
MapPolygonImpl poly = new MapPolygonImpl(route);
poly.setColor(Color.blue);
map.addMapPolygon(poly);
map.setDisplayPosition(start, 10);
f.add(map);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
new MapWorker(map, start).execute();
}
private class MapWorker extends SwingWorker<Void, Coordinate> {
private final JMapViewer map;
private Coordinate last;
public MapWorker(JMapViewer map, Coordinate start) {
this.map = map;
this.last = start;
}
#Override
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
last = new Coordinate(last.getLat() + 0.0025, last.getLon() + 0.01);
publish(last);
Thread.sleep(1000);
}
return null;
}
#Override
protected void process(List<Coordinate> chunks) {
for (Coordinate c : chunks) {
route.add(c);
}
map.repaint();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new MapWorkerTest()::display);
}
}
Multiple route management left as a exercise.

Related

libgdx android app crashes when loading uiskin | no logs

For some reason after adding buttons to my application it has stopped functioning on android. It works as expected on desktop, but when attempting to run on android the app builds successfully, launches, then immediately crashes before it ever loads the scene. I attempted to view the logs via aLogcat, however I see nothing in these logs indicating an issue has occurred.
I have come to the conclusion that this issue has something to do with the uiskin/buttons being added to the stage, as I can add the stage without any actors and the app will still function in android. The files outlined in the uiskin.json file are all located in /android/assets directory as they should be. Is there something I have done incorrectly? Something I have missed?
uiskin.json
{
"com.badlogic.gdx.graphics.g2d.BitmapFont":{
"default_font": {
"file": "book_antiqua.fnt"
}
},
"com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle":{
"default": {
"down": "default-round-down",
"up": "default-round",
"font": "default_font"
}
},
"com.badlogic.gdx.scenes.scene2d.ui.Window$WindowStyle":{
"default": {
"titleFont": "default_font"
}
}
}
Main class
package com.freedom.thirty;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.Material;
//import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.graphics.FPSLogger;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.viewport.FillViewport;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.freedom.thirty.MyCameraInputController;
public class FreedomThirty implements ApplicationListener {
public ScreenViewport vp;
public ScreenViewport stageVp;
public OrthographicCamera cam;
public MyCameraInputController camController;
public ModelBatch modelBatch;
public AssetManager assets;
public Array<ModelInstance> allModelInstances = new Array<ModelInstance>();
public Environment environment;
public boolean loading;
// Model instances that make up the map
public ModelInstance bridge;
public ModelInstance container_001;
public ModelInstance crate_001;
public ModelInstance crate_002;
public ModelInstance crate_003;
public ModelInstance crate_004;
public ModelInstance crate_005;
public ModelInstance grass_area;
public ModelInstance gravel_area;
public ModelInstance rock_wall;
public ModelInstance water;
public ModelInstance player;
public ModelInstance skybox;
public Skin skin;
public Stage stage;
public Table table;
//public FPSLogger fpsLogger = new FPSLogger();
#Override
public void create() {
modelBatch = new ModelBatch();
assets = new AssetManager();
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.8f, 0.8f, 0.8f, 1f));
//environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
cam = new OrthographicCamera();
cam.position.set(-4f, 4f, 4f);
cam.lookAt(8f, 0f, -8f);
cam.near = 1f;
cam.far = 2000f;
cam.update();
// Create input controller for default scene camera. This is what allows the user to orbit around the scene
camController = new MyCameraInputController(cam);
// Set the position where the camera will look. This will need to come from json config file in the future
camController.target.set(8f, 0f, -8f);
vp = new ScreenViewport(cam);
vp.setUnitsPerPixel(0.017f);
vp.apply();
stageVp = new ScreenViewport();
stageVp.apply();
stage = new Stage(stageVp);
assets.load("uiskin.json", Skin.class);
assets.load("map_003.g3db", Model.class);
loading = true;
}
private void doneLoading(){ // Assets are now loaded into memory and can be accessed without error
// Load UI skin
skin = assets.get("uiskin.json", Skin.class);
// Define the buttons that will be in the ui table
final TextButton zoomOut = new TextButton("Zoom Out",skin,"default");
final TextButton zoomIn = new TextButton("Zoom In",skin,"default");
// Create UI table actor
table = new Table();
table.setWidth(stage.getWidth());
table.align(Align.center | Align.top);
// In the future determine what the current viewport height/width is, and set these
// to different sizes based on where the viewport falls between. Just like bootstrap's xs,sm,md,lg classes
table.setSize(250f, 250f);
table.setPosition(800f,400f);
zoomOut.setWidth(200);
zoomOut.setHeight(50);
zoomIn.setWidth(200);
zoomIn.setHeight(50);
// Event listeners for ui buttons
zoomOut.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y){
//Gdx.app.log("Zoom Out", "Press successful");
vp.setUnitsPerPixel(vp.getUnitsPerPixel() + 0.001f);
vp.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
stageVp.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
event.stop();
}
});
zoomIn.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y){
//Gdx.app.log("Zoom In", "Press successful");
vp.setUnitsPerPixel(vp.getUnitsPerPixel() - 0.001f);
vp.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
stageVp.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
event.stop();
}
});
// Add buttons to table
table.row().padBottom(30).padTop(30);
table.add(zoomOut);
table.add(zoomIn);
// Add table to stage
stage.addActor(table);
// Create an input multiplexer and add our stage and camController to it, set input processor to our new multiplexer
InputMultiplexer im = new InputMultiplexer(stage, camController);
Gdx.input.setInputProcessor(im);
// Load the 3d map, and character as character happens to be in the map file as of right now
// In the future each map and it's elements will be contained within it's own class, extended from
// map class
Model model = assets.get("map_003.g3db", Model.class);
bridge = new ModelInstance(model, "bridge");
allModelInstances.add(bridge);
container_001 = new ModelInstance(model, "container_001");
allModelInstances.add(container_001);
crate_001 = new ModelInstance(model, "crate_001");
allModelInstances.add(crate_001);
crate_002 = new ModelInstance(model, "crate_002");
allModelInstances.add(crate_002);
crate_003 = new ModelInstance(model, "crate_003");
allModelInstances.add(crate_003);
crate_004 = new ModelInstance(model, "crate_004");
allModelInstances.add(crate_004);
crate_005 = new ModelInstance(model, "crate_005");
allModelInstances.add(crate_005);
grass_area = new ModelInstance(model, "grass_area");
allModelInstances.add(grass_area);
gravel_area = new ModelInstance(model, "gravel_area");
allModelInstances.add(gravel_area);
rock_wall = new ModelInstance(model, "rock_wall");
allModelInstances.add(rock_wall);
water = new ModelInstance(model, "water");
allModelInstances.add(water);
player = new ModelInstance(model, "character");
allModelInstances.add(player);
player.transform.setTranslation(8f,0f,-6f); // Test character movement on the map
loading = false;
}
#Override
public void render() {
if(loading && assets.update()){
doneLoading();
}
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
camController.update();
//Gdx.app.log("Camera Position: ", cam.position.toString());
modelBatch.begin(cam);
modelBatch.render(allModelInstances, environment);
modelBatch.end();
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
//fpsLogger.log();
}
#Override
public void dispose() {
modelBatch.dispose();
allModelInstances.clear();
assets.dispose();
}
#Override
public void resize(int width, int height) {
vp.update(width, height);
stageVp.update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
What finally corrected this issue was downloading the default uiskin files from the libgdx repository on github. Apparently I missed something when I built them from scratch? The files I used from the repo are:
uiskin.json
uiskin.atlas
default.png
uiskin.png
I think it takes to long to load the uiskin file. Try instead of skin = new Skin(Gdx.files.internal("uiskin.json")); something like that:
AssetManager manager = new AssetManager();
manager.load("uiskin.json", Skin.class);
manager.finishLoading();
skin = manager.get("uiskin.json", Skin.class);
Don't forget to dispose the AssetManager, when before you finish your game.

Command Pattern Usefulness when using JComponents

So, I'm developing a program using the Swing library and I obviously have buttons and menu items. Some of these are supposed to do the same stuff, and I thought using the Command Pattern should be the way to do it, e.g. I have a "save" button and a "save" menu item and they have to implement the same saving algorithm.
Command Pattern seems to be ok but I can't get who's the receiver in all that. Isn't a comand supposed to work on an object which implements some sort of "receiver interface", so that you can use different commands on different receivers coupling them aribtrarily? It looks like there's no "receiver" in my implementation of the pattern.
Another doubt i have is should a command be implemented as a singleton, since you could potentially call its functions from differents parts of the same project, and it would be handly to instantiate it only once and make it statically invokable?
Thank you.
" I obviously have buttons and menu items. Some of these are supposed to do the same stuff,"
As #nIcEcOw noted, that's what Actions are for. This Answer Shows exactly this.
As stated in the How to use Actions :
An Action can be used to separate functionality and state from a component. For example, if you have two or more components that perform the same function, consider using an Action object to implement the function. An Action object is an action listener that provides not only action-event handling, but also centralized handling of the state of action-event-firing components such as tool bar buttons, menu items, common buttons, and text fields. The state that an action can handle includes text, icon, mnemonic, enabled, and selected status.
An There only three Actions. Ont to open, save, and new. Each Action has an ActionCommand, and icon, and and action to perform. Both the JMenuItem and JToolBar button share the same Action and do the same thing. Here is the code you can run.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class ActionTest {
public ActionTest() {
ImageIcon openIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/open.gif"));
ImageIcon saveIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/save.gif"));
ImageIcon newIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/new.gif"));
Action openAction = new AbstractAction("Open", openIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Open File");
}
};
Action saveAction = new AbstractAction("Save", saveIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Save File");
}
};
Action newAction = new AbstractAction("New", newIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New File");
}
};
JMenuItem openMenuItem = new JMenuItem(openAction);
JMenuItem saveMenuItem = new JMenuItem(saveAction);
JMenuItem newMenuItem = new JMenuItem(newAction);
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
fileMenu.add(openMenuItem);
fileMenu.add(saveMenuItem);
fileMenu.add(newMenuItem);
menuBar.add(fileMenu);
JToolBar toolBar = new JToolBar();
toolBar.add(Box.createHorizontalGlue());
toolBar.setBorder(new LineBorder(Color.LIGHT_GRAY, 1));
toolBar.add(newAction);
toolBar.add(openAction);
toolBar.add(saveAction);
JFrame frame = new JFrame("Toolbar and Menu Test");
frame.setJMenuBar(menuBar);
frame.add(toolBar, BorderLayout.PAGE_START);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ActionTest();
}
});
}
}
As stated in the quote from the above mentioned tutorial, you can do more than just add an image and an action command to the Action. You can use it to set mnemonics and accelorators. Here is a custom Action class that takes
An action command String
an icon
a description for tooltips
a mnemonic
and a key accelorator.
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon) {
super(name, icon);
this.name = name;
}
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "Open":
System.out.println("Open");
break;
case "New":
System.out.println("New");
break;
case "Save":
System.out.println("Save");
break;
}
}
}
Here's an instantiation of this Action
Action newAction = new MyAction("New", newIcon,
"Creates a new file",
new Integer(KeyEvent.VK_N),
KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
And here's the new result. You will see the actionCommand in the menu, along with the key mnemonics and accelerators, tooltips, and you will see the jtoolbar buttons share the same traits. You will also see in the final code, that never once once a component created. All you do is add the Action to the JToolBar and the JMenu and let them work their magic.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ActionInterfaceDemo extends JFrame {
public ActionInterfaceDemo() {
ImageIcon openIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/open.gif"));
ImageIcon saveIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/save.gif"));
ImageIcon newIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/new.gif"));
Action openAction = new MyAction("Open", openIcon,
"Opens a file",
new Integer(KeyEvent.VK_O),
KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
Action saveAction = new MyAction("Save", saveIcon,
"Saves a file",
new Integer(KeyEvent.VK_S),
KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
Action newAction = new MyAction("New", newIcon,
"Creates a new file",
new Integer(KeyEvent.VK_N),
KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
setJMenuBar(menuBar);
menuBar.add(fileMenu);
fileMenu.add(newAction);
fileMenu.add(openAction);
fileMenu.add(saveAction);
JToolBar toolBar = new JToolBar("Alignment");
toolBar.setBorder(BorderFactory.createLineBorder(Color.BLUE));
toolBar.add(Box.createHorizontalGlue());
toolBar.add(newAction);
toolBar.add(openAction);
toolBar.add(saveAction);
add(toolBar, BorderLayout.PAGE_START);
add(new JScrollPane(new TextArea(10, 40)), BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("Action Interface Demo");
pack();
setLocationByPlatform(true);
setVisible(true);
}
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon) {
super(name, icon);
this.name = name;
}
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "Open":
System.out.println("Open");
break;
case "New":
System.out.println("New");
break;
case "Save":
System.out.println("Save");
break;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new ActionInterfaceDemo();
}
});
}
}
UPDATE
The better explain the relationship of Action and Command Patterns
As noted in Command Pattern
The command pattern is a commonly used pattern which encapsulates a method call or action-like code into a single class. The advantages of being able to package a method (or methods) into a class become evident when you have multiple invokers for a single action (for example a button and a menu item may perform the same action).
In Swing and Borland Delphi programming, an Action is a command object. In addition to the ability to perform the desired command, an Action may have an associated icon, keyboard shortcut, tooltip text, and so on. A toolbar button or menu item component may be completely initialized using only the Action object.
So basically Swing uses the concept of the command pattern through the use of Actions
As for OP's question
"Command Pattern seems to be ok but I can't get who's the receiver in all that."
As for the receiver, the wiki uses a text editor as an example and defines the receiver as such
Receiver, Target Object: the object that is about to be copied, pasted, moved, etc. The receiver object owns the method that is called by the command's execute method. The receiver is typically also the target object. For example, if the receiver object is a cursor and the method is called moveUp, then one would expect that the cursor is the target of the moveUp action. On the other hand, if the code is defined by the command object itself, the target object will be a different object entirely.
The main more components of a Command Pattern are stated as follows
Four terms always associated with the command pattern are command, receiver, invoker and client.
Client, Source, Invoker: the button, toolbar button, or menu item clicked, the shortcut key pressed by the user.
So to put it all together:
The MenuItem (client) invokes it
Action (command object) which calls it actionPerformed which in turn
Invokes an method on the receiver.
The wiki article is good read with a Java example
When two or more components are mean to do exactly the same thingy, one should look at Action, which reduces the duplicate code.
Small example for further help :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionExample {
private JFrame frame;
private JButton button;
private JMenuItem exitItem;
private Action commonActions;
private class CommonActions extends AbstractAction {
public CommonActions(String title, String desc) {
super(title);
putValue(SHORT_DESCRIPTION, desc);
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(frame,
"Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
frame.dispose();
}
};
private void displayGUI() {
frame = new JFrame("Action Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
commonActions = new CommonActions("Exit", "To Exit the Application");
JPanel contentPane = new JPanel();
button = new JButton();
button.setAction(commonActions);
contentPane.add(button);
frame.setJMenuBar(getMenuBar());
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar getMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitItem = new JMenuItem(commonActions);
fileMenu.add(exitItem);
menuBar.add(fileMenu);
return menuBar;
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new ActionExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
ADDED an example with SINGLETON PATTERN (though I am not sure of this approach(about how good this approach is))
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionExample {
private JFrame frame;
private JButton button;
private JMenuItem exitItem;
private void displayGUI() {
frame = new JFrame("Action Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
CommonActions.setValues(frame);
JPanel contentPane = new JPanel();
button = new JButton();
button.setAction(CommonActions.getInstance());
contentPane.add(button);
frame.setJMenuBar(getMenuBar());
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar getMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitItem = new JMenuItem(CommonActions.getInstance());
fileMenu.add(exitItem);
menuBar.add(fileMenu);
return menuBar;
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new ActionExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
class CommonActions extends AbstractAction {
private static CommonActions commonActions = null;
private static JFrame frame = null;
static {
try {
commonActions = new CommonActions("Exit", "To Exit the Application");
} catch (Exception e) {
throw new RuntimeException("BINGO, an error");
}
}
private CommonActions(String title, String desc) {
super(title);
putValue(SHORT_DESCRIPTION, desc);
}
public static CommonActions getInstance() {
return commonActions;
}
public static void setValues(JFrame f) {
frame = f;
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(frame,
"Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
frame.dispose();
}
}

Closing another JFrame from another method

I've been working on this for some time, and I'd really appreciate some help right now.
I'm trying to get the JFrame containing the text input fields to close from my actionPerformed method, but I can't seem to get anything to work. JFrame.dispose wont let me access the right Jframe, and setVisible(false) is equally useless, unless I'm doing this completely wrong.
//halp
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
class PersonInput extends JPanel
implements ActionListener {
//Fields for data entry
private JFormattedTextField firstField, lastField, dateField;
public String x[] = new String[3];
public PersonInput() {
//Values for the fields
String first = "First Name";
String last = "Last Name";
String date = "MM/DD/YYYY";
//Create the text fields and set them up.
firstField = new JFormattedTextField();
firstField.setValue(new String(first));
lastField = new JFormattedTextField();
lastField.setValue(new String(last));
dateField = new JFormattedTextField();
dateField.setValue(new String(date));
dateField.setColumns(10);
JButton ok = new JButton("OK");
ok.setVerticalTextPosition(AbstractButton.BOTTOM);
ok.setHorizontalTextPosition(AbstractButton.CENTER);
ok.setActionCommand("ok");
ok.addActionListener(this);
ok.setToolTipText("Confirms user input and continues with the program.");
JPanel buttons = new JPanel(new GridLayout(0,1));
buttons.add(ok);
//Layout the text fields in a panel.
JPanel fieldPane = new JPanel(new GridLayout(0,1));
fieldPane.add(firstField);
fieldPane.add(lastField);
fieldPane.add(dateField);
//Put the panels in this panel, labels on left,
//text fields on right.
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
add(fieldPane, BorderLayout.CENTER);
add(buttons, BorderLayout.LINE_END);
}
public void actionPerformed(ActionEvent e) {
if ("ok".equals(e.getActionCommand()))
{
JFrame frame1 = new JFrame("People Sorter");
x[0] = firstField.getText();
x[1] = lastField.getText();
x[2] = dateField.getText();
JOptionPane.showMessageDialog(frame1, "Person has been added.");
dispPerson();
frame.setVisible(false);
}
}
public void dispPerson()
{
System.out.println(x[0] + x[1] + x[2]);
}
public static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Person Input");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add contents to the window.
frame.add(new PersonInput());
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}
I'm all ears if anyone has any ideas; I've been stressed over this all day. Thanks much for lending me your time!
EDIT: Just for clarification, the frame I'm trying to close is the one instantiated in the createAndShowGUI method.
it seems that the problem is that we are trying to merge both static and non static contents. For a short explanation static contents can be referred without need of creating an instance (object) of that class. Which means that createAndShowGUI can be called:
inside another static method (like main)
From class reference PersonInput.createAndShowGUI()
or from an object, but that method or attribute will be always the same, static attributes are shared.
I can suggest 2 ways to solve your problem.
One is pass the object frame to PersonInput
//halp
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
class PersonInput extends JPanel
implements ActionListener {
//Fields for data entry
private JFormattedTextField firstField, lastField, dateField;
public String x[] = new String[3];
JFrame frame;
public PersonInput(JFrame frame) {
this.frame = frame;
//the rest of your code
}
The other way is to have the frame object outside the method and declare it static.
static JFrame frame = new JFrame("Person Input");;
public static void createAndShowGUI() {
//Create and set up the window.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add contents to the window.
frame.add(new PersonInput());
//Display the window.
frame.pack();
frame.setVisible(true);
}
Remember that static variable cannot be referenced from a static context

TreeModelListener not responding to changes in TreeModel which it subscribes to

I'm having some bother understanding why I cannot get a TreeModelChanged listener to respond to changes in the model which it subscribes to.
I have managed to reproduce the problem in a small example.
The SysOut message does not print to the console whenever a new node is added to the tree.
I intend to replace the SysOut message with some commands to redraw the tree. At the moment I am using a SysOut message just to prove that the listener is not being fired.
Am I missing something fundamental?
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class TreeTest {
private JTree t;
private DefaultTreeModel m ;
public static void main(String[] args) {
new TreeTest();
}
public TreeTest() {
//Draw Frame & Panel - set dimensions
JFrame f = new JFrame();
f.setSize(new Dimension(800,600));
JPanel p = new JPanel();
p.setSize(new Dimension(800,600));
//Create a Tree Model. Give it a String at the root.
m = new DefaultTreeModel(new DefaultMutableTreeNode("Root"));
//Create a tree and add the Model to it
t = new JTree();
t.setModel(m);
//Try a Tree Model Listener
m.addTreeModelListener(new TreeModelListener() {
private void doSomething() {
//Should fire whenever a node is added to the model
System.out.println("Responding to TreeModelListener");
}
#Override
public void treeStructureChanged(TreeModelEvent e) {
doSomething();
}
#Override
public void treeNodesRemoved(TreeModelEvent e) {
doSomething();
}
#Override
public void treeNodesInserted(TreeModelEvent e) {
doSomething();
}
#Override
public void treeNodesChanged(TreeModelEvent e) {
doSomething();
}
});
//Add listener to a button which adds nodes to the tree when clicked
JButton addNode = new JButton("Add node");
addNode.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New Node");
DefaultMutableTreeNode root = (DefaultMutableTreeNode) m.getRoot();
root.add(newNode);
}
});
JScrollPane s = new JScrollPane(t);
p.add(s);
p.add(addNode);
p.setVisible(true);
f.add(p);
f.setVisible(true);
}
}
that's because the model doesn't know about the addition, it happens under its feet. Use the methods on DefaultTreeModel to do the insertion:
model.insertNodeInto(newNode, root, root.getChildCount())
Edit
a TreeNode is just a (more or less) dumb data structure. As you can see in the api, it's not an Observable, so there is no way for the model which uses that data structure to detect if anything changed on the node. To make it aware of the change, you have to do one of two things
use the node manipulation methods of the model
update the node and notify the model manually (calling nodesWereInserted)
The first is the preferable way (keeps control where it belongs), the second might be needed in more complex contexts (though I would strongly recommend to never do it, that's why SwingX DefaultTreeTableModel doesn't have them exposed :)

Need the height of an invalidated Swing component

The basic setup is this: I have a vertical JSplitPane that I want to have a fixed-size bottom component and a resizing top component, which I accomplished by calling setResizeWeight(1.0). In this application there is a button to restore the "default" window configuration. The default height of the window is the desktop height, and the default divider location is 100 pixels from the bottom of the split pane.
To set the divider location to 100px, I take the JSplitPane height - 100. The problem is, just before this I resize the JFrame, and since the code is in a button callback, the JSplitPane has been invalidated but not yet resized. So the divider location is set incorrectly.
Here is a SSCCE. Click the button twice to see the problem. The first click will resize the window, but the divider location remains the same (relative to the bottom of the window). The second click properly moves the divider, since the window size didn't change.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSplitPane;
public class SSCCE {
/**
* #param args unused
*/
public static void main(String[] args) {
new SSCCE();
}
private final JFrame f = new JFrame("JSplitPane SSCE");
private final JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,true);
public SSCCE() {
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
sp.add(new JLabel("top"));
sp.add(new JLabel("bottom"));
sp.setResizeWeight(1.0);
f.getContentPane().add(sp);
f.getContentPane().add(new JButton(new AbstractAction("Resize to Default") {
#Override
public void actionPerformed(ActionEvent e) {
restoreDefaults();
}
}),BorderLayout.PAGE_END);
f.setSize(400,300);
f.setVisible(true);
}
void restoreDefaults() {
f.setSize(f.getWidth(), getDesktopRect(f.getGraphicsConfiguration()).height);
sp.setDividerLocation(sp.getSize().height - 100); // Does not work on first button press
}
Rectangle getDesktopRect(GraphicsConfiguration gc) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension size = toolkit.getScreenSize();
Insets insets = toolkit.getScreenInsets(gc);
return new Rectangle(insets.left, insets.top, size.width - (insets.left + insets.right), size.height - (insets.top + insets.bottom));
}
}
I have thought of a few ways I might get around this, but they all seem sort of hackish. So far the best idea I've had has been to call f.validate() in between setting the frame size and setting the divider location, but I'm concerned there might be side effects to forcing validation early.
The other option I thought of is to use EventQueue.invokeLater() to put the call to set the divider location at the end of the event queue. But that seems risky to me - I'm assuming the JSplitPane will have been validated at that point, and I'm concerned that may be a faulty assumption to make.
Is there a better way?
Took a while (probably due to being early morning here :-) to understand the problem, so just to make sure I got it:
the size of the bottom component can be whatever the user decides at all times
when resizing the frame all height change should happen to the top component
there's an option to restore to default sizes, independent of any setting before
"default" means the bottom component must have a fixed height of xx
If so, the solution is to separate the frame resizing from the sizing the bottom component. Your second option is dead on: resize the frame and wrap the bottom comp resize into a invokeLater (EventQueue or SwingUtilities, doesn't matter).
void restoreDefaults() {
f.setSize(f.getWidth(), getDesktopRect(f.getGraphicsConfiguration()).height);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
sp.setDividerLocation(sp.getSize().height - 100);
}
});
}
That's guaranteed to work as expected, because the invokeLater puts the request as last after all already queued events:
/**
* Causes <i>doRun.run()</i> to be executed asynchronously on the
* AWT event dispatching thread. This will happen after all
* pending AWT events have been processed. [...]
* If invokeLater is called from the event dispatching thread --
* for example, from a JButton's ActionListener -- the <i>doRun.run()</i> will
* still be deferred until all pending events have been processed.
You could create a custom action class that handles the button click and the resize event. This approach would look like this:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSplitPane;
public class SSCCE {
/**
* #param args unused
*/
public static void main(String[] args) {
new SSCCE();
}
private final JFrame f = new JFrame("JSplitPane SSCE");
private final JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,true);
public SSCCE() {
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
sp.add(new JLabel("top"));
sp.add(new JLabel("bottom"));
sp.setResizeWeight(1.0);
f.getContentPane().add(sp);
CustomListener resizeViaButtonListener = new CustomListener("Resize to Default");
f.getContentPane().add(new JButton(resizeViaButtonListener), BorderLayout.PAGE_END);
f.addComponentListener(resizeViaButtonListener);
f.setSize(400,300);
f.setVisible(true);
}
void restoreDefaults() {
f.setSize(f.getWidth(), getDesktopRect(f.getGraphicsConfiguration()).height);
sp.setDividerLocation(sp.getSize().height - 100); // Does not work on first button press
}
Rectangle getDesktopRect(GraphicsConfiguration gc) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension size = toolkit.getScreenSize();
Insets insets = toolkit.getScreenInsets(gc);
return new Rectangle(insets.left, insets.top, size.width - (insets.left + insets.right), size.height - (insets.top + insets.bottom));
}
class CustomListener extends AbstractAction implements ComponentListener {
CustomListener(String actionDescription) {
super(actionDescription);
}
private boolean resizedViaButtonClick = false;
#Override
public void actionPerformed(ActionEvent arg0) {
resizedViaButtonClick = true;
f.setSize(f.getWidth(), getDesktopRect(f.getGraphicsConfiguration()).height);
sp.setDividerLocation(sp.getSize().height - 100);
// you need this also here because if the component is not resized when clicking the button
// it is possible that the divider location must be changed. This happens when the user clicks
// the button after changing the divider but not resizing the frame.
}
#Override
public void componentResized(ComponentEvent e) {
if ( resizedViaButtonClick ) {
resizedViaButtonClick = false;
sp.setDividerLocation(sp.getSize().height - 100);
}
}
#Override
public void componentHidden(ComponentEvent e) { /* do nothing */ }
#Override
public void componentMoved(ComponentEvent e) { /* do nothing */ }
#Override
public void componentShown(ComponentEvent e) { /* do nothing */ }
}
}
This way the code that is responsible for handling the logical task of setting the standard size will be in one single and easy to understand class.
nothing complicated, basic Swing Rules
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class SSCCE {
/**
* #param args unused
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
SSCCE sSCCE = new SSCCE();
}
});
}
private final JFrame f = new JFrame("JSplitPane SSCE");
private final JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
true);
public SSCCE() {
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
sp.add(new JLabel("top"));
sp.add(new JLabel("bottom"));
sp.setResizeWeight(1.0);
f.getContentPane().add(sp);
f.getContentPane().add(new JButton(new AbstractAction(
"Resize to Default") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(sp.getLastDividerLocation());
restoreDefaults();
}
}), BorderLayout.PAGE_END);
f.setPreferredSize(new Dimension(400, 300));
f.pack();
f.setVisible(true);
}
void restoreDefaults() {
//EventQueue.invokeLater(new Runnable() {
// #Override
// public void run() {
f.setPreferredSize(new Dimension(f.getWidth(),
getDesktopRect(f.getGraphicsConfiguration()).height));
f.pack();
sp.setDividerLocation(sp.getSize().height - 100);
// Does not work on first button press
// }
//});
}
Rectangle getDesktopRect(GraphicsConfiguration gc) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension size = toolkit.getScreenSize();
Insets insets = toolkit.getScreenInsets(gc);
return new Rectangle(insets.left, insets.top,
size.width - (insets.left + insets.right),
size.height - (insets.top + insets.bottom));
}
}
but I think pack() may be better than validate()
I generally try to avoid invoking setPreferredSize() on any component. I would rather let the layout manager do its job. In this case this would mean setting the size of the frame and let the BorderLayout take all the available space.
void restoreDefaults() {
// f.setSize(f.getWidth(), getDesktopRect(f.getGraphicsConfiguration()).height);
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
Rectangle bounds = env.getMaximumWindowBounds();
f.setSize(f.getWidth(), bounds.height);
f.validate();
sp.setDividerLocation(sp.getSize().height - 100); // Does not work on first button press
}