JPanel not visible - java

I've been having a problem with my program that has been driving me crazy and I cannot understand why this is happening. I have a GUI that when the "Add" button is pressed, a new system listing appears (Which is a class called SystemPanel that extends JPanel and cotains system details, which is created and then put into the Frame's main panel.)
To put it shortly, when I try to add a new SystemPanel, it does not appear for whatever reason. I have code using JSch that connects to the system and verifies whether its processes are online or not, but the line of code that does this is after the creation of the SystemPanel. It is only after the code for testing the processes of the system are executed that the SystemPanel becomes visible, and I can't understand why this is the case. Here is the code for adding a new SystemPanel:
public void actionPerformed(ActionEvent e) {
//If the "Add" button is pressed
if (e.getActionCommand() == "Add") {
PopupWindow popup = new PopupWindow(this);
popup.setVisible(true);
String[] results = popup.getResults();
if (results[0] != null && results[1] != null && results[2] != null && results[3] != null && results[4] != null) {
SystemPanel newSystem = new SystemPanel(this, results[0], results[1], results[2], results[3], results[4]);
systemsPanel.add(newSystem);
revalidate();
systemsList.add(newSystem);
System.out.println("Did the stuff");
boolean[] status = SystemChecker.checkOnline(results[0], results[1], results[2], results[3]);
}
}
}
The PopupWindow is a custom JDialog that allows the user to enter the required information which is returned in a String array and is used to create a new SystemPanel. The checkOnline function grabs the user's inputs and uses them to connect to the system and determine whether the processes are working or not, and returns the results into a boolean array, true for working, false for not.
What's even weirder is that I have another part of my program that reads from an .ini file to obtain existing systems and then creates SystemPanels based on the data that it reads. Through this method, the SystemPanels are added the way I want and work perfectly for some reason, even though the code for adding the panels is hardly any different. Code:
for (int i = 0; i < systems.size(); i++) {
SystemPanel newSystem = new SystemPanel(this, systems.get(i)[0], systems.get(i)[1], systems.get(i)[2], systems.get(i)[3], systems.get(i)[4]);
systemsPanel.add(newSystem);
revalidate();
systemsList.add(newSystem);
}
for (int i = 0; i < lineNum; i++) {
boolean[] status = SystemChecker.checkOnline(systems.get(i)[0], systems.get(i)[1], systems.get(i)[2], systems.get(i)[3]);
systemsList.get(i).updateIcons(status);
}
This code grabs the details from the file and then makes the SystemPanels based on those details. Here, all of the SystemPanels are added and show up before the connection is tested, which is what I want to happen when I add one normally.
Why is it that the SystemPanel doesn't appear until the connection is tested, even though the code for displaying the SystemPanel is executed before the connection test? Any help would be greatly appreciated, thanks.

Try it of the current event queue handling, on which actionPerformed is done.
public void actionPerformed(ActionEvent e) {
EventQueue.invokeLater(() -> { ... your code here ... });
}
Also you cannot add the same component to two parents, every component object has a single parent (container).
(Java 8 notation)

Related

Pushing a button (from far away)

So what I am doing is, I am checking if the player right clicks air using the PlayerInteractEvent, then I am getting the player's target block and saving it as a variable named "block".
This all is working fine but what i want to do with this is check if that block is a button and if so "push" it, but it isn't doing that right.
I have tried casting the block to org.bukkit.material.Button after checking if its type was indeed a button, then I set it to powered like so:
((Button) block.getState().getData()).setPowered(true);, but that didn't do anything so I then tried: block.setData((byte) (block.getData() | 0x8)));, and that did turn it on but it stayed on.
So how do I do this correctly?
Here is the full code:
#SuppressWarnings("deprecation")
#EventHandler
public void onPlayerInteraction(PlayerInteractEvent e) {
if (e.getAction().equals(Action.RIGHT_CLICK_AIR)) {
Block block = e.getPlayer().getTargetBlock((Set<Material>) null, 200);
if (block.getType().equals(Material.STONE_BUTTON) || block.getType().equals(Material.WOOD_BUTTON)) {
block.setData((byte) (block.getData() | 0x8));
}
}
}
PS: I am using the Spigot 1.12.2 API.
First of all, try to trigger RIGHT_CLICK_BLOCK action, because Button is a block. Secondly, event called before you push the button, so if you want to change the state of buttom after player clicked, you should run task later to change it a bit later after event passed.
Also, you don't need to check hand item type point to null. If ItemStack is not null, Material will never be null, and in case if ItemStack will be null, you will catch NPE, trying to get Material. And in versions 1.9 and above you might also check EquipmentSlot, because PlayerInteractEvent calls twice (as hand and offhand).
ItemStack item = e.getPlayer().getInventory().getItemInMainHand();
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getHand() == EquipmentSlot.HAND
&& (item == null || item.getType() == Material.AIR)) {
// Your code
}
To change button state in new way, get MaterialData from the block state, cast it to Button, change power state, write all back to block and update:
Button button = (Button) block.getState().getData();
button.setPowered(true);
block.getState().setData(button);
block.getState().update();
Furthermore, you don't really need to check facing at block, if Action is RIGHT_CLICK_BLOCK, then you can get it directly from event.
Block block = e.getClickedBlock();
So, I think, the final code may looks like that:
Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("YourPluginName");
ItemStack item = e.getPlayer().getInventory().getItemInMainHand();
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getHand() == EquipmentSlot.HAND
&& (item == null || item.getType() == Material.AIR)) {
Block block = e.getClickedBlock();
Bukkit.getServer().getScheduler().runTaskLater(plugin, new Runnable() {
public void run() {
Button button = (Button) block.getState().getData();
button.setPowered(true);
block.getState().setData(button);
block.getState().update();
}
}, 2L);
}

UndoableEditListener never called in JTextPane/JTextArea

I am trying to attach an UndoableEditListener to a JTextPane or JTextArea that queues up edits into an UndoManager.
textPane.getDocument().addUndoableEditListener(new UndoableEditListener() {
#Override
public void undoableEditHappened(UndoableEditEvent event) {
undoQueue.addEdit(event.getEdit());
}
});
But undoableEditHappened is never called when I type "aaa" in the text window.
Thinking it's Java's fault, not mine, I crack AbstractDocument.class open with Eclipse debugger to watch the event trigger. It has a private listeners array. AbstractDocument stores all its listeners in odd indices in the listeners array, with the listeners' type Class<>'s in the even indices.
protected void fireUndoableEditUpdate(UndoableEditEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == UndoableEditListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((UndoableEditListener) listeners[i + 1]).undoableEditHappened(e);
}
}
}
See the line if (listeners[i] == UndoableEditListener.class)? When I add the undo change listener, the debugger shows listeners containing my listener, along with UndoableEditListener.class in the index before it. But, when the debugger comes to that if-statement, all the even indices in the array listeners show as DocumentListener.class in the debugger. Consequently, the if-statement is always false and the listener never called.
What the heck? Is this a Java 8 bug? Or am I missing a step the examples forgot to mention?
The problem was in the JTextPane. I was overriding its setText method to force it to call read, the alternative to setText that normalizes all kinds of newline while remembering them. But JTextPane.read appears to not trigger an UndoableEditEvent on the document.
If I leave setText alone, then UndoManager.undo works.

Need to split a thread so it runs in multiple threads. Also need to figure out a synchronized issue - JAVA

This program is about showing the oldest, youngest ect person in a network.
I need to figure out how I can improve it, so I dont get the ConcurrentModificationException. I get this when I ask for displaying more of these multiple time, like asking for youngest, oldest, and make it refresh to tell me whos the current youngest.
public void randomIncreaseCoupling(int amount, double chance, double inverseChance) {
randomChangeCoupling(amount,chance,inverseChance,true);
}
public void randomDecreaseCoupling(int amount, double chance, double inverseChance) {
randomChangeCoupling(amount,chance,inverseChance,false);
This code is used in the network to randomly change the date outcome.
Also, I have this running in a Thread currently, but I need to fasten it, so I need to run each of the 'functions' to run in their own Thread.
The Class MainController is starting the Thread by:
public void startEvolution() {
if (display == null)
throw new Error("Controller not initialized before start");
evolutionThread = new NetworkEvolutionSimulator(network, display);
evolutionThread.start();
}
When I click on any button ex a button to show me the oldest in this network, it is done by:
public void startOldest() {
if (display == null)
throw new Error("Not properly initialized");
int order = display.getDistanceFor(Identifier.OLDEST);
Set<Person> oldest = network.applyPredicate(PredicateFactory.IS_OLDEST,
order);
display.displayData(Identifier.OLDEST, summarize(order, oldest));
I tried to make it like:
public void startOldest() {
if (display == null)
throw new Error("Not properly initialized");
int order = display.getDistanceFor(Identifier.OLDEST);
Set<Person> oldest = network.applyPredicate(PredicateFactory.IS_OLDEST,
order);
display.displayData(Identifier.OLDEST, summarize(order, oldest));
evolutionThread2 = new NetworkEvolutionSimulator(network, display);
evolutionThread2.start();
But this starts main thread over and over when I press the button. What I want is that this specific function and the others when I press the cercain button it has to start each of them in their own threads so I will be able to use more than one of them at a time. How shall I do this?
I can explain more if needed.
Thanks in advance.
My first post, so sorry if I didn't follow a specific rule.
You could use the synchronized keyword -
The synchronized keyword can be used to mark several types of code blocks:
Instance methods
Static methods
Code blocks inside instance methods
Code blocks inside static methods
Everywhere you're using your set oldest you could add a synchronized code block like this
synchronized(oldest) { ... }

Error removing element from JList with DefaultListModel

I have a standard JList that will be changed while the program is running. In order to make life easier I have created a DefaultListModel and assigned it to the JList:
JList CharList = new JList();
DefaultListModel CharListModel = new DefaultListModel();
CharList.setModel(CharListModel);
I am able to load a file into the list, and later I can add items to the list like this:
File ChFile = new File (CharListFile);
FileReader freeder = new FileReader (ChFile);
BufferedReader breeder = new BufferedReader(freeder);
String line;
while((line=breeder.readLine())!=null)
{
int pos = CharList.getModel().getSize();
CharListModel.add(pos, line);
}
...
...
//and to add items..
int pos = CharList.getModel().getSize();
CharListModel.add(pos, NewCharName);
However, I need to be able to remove items from the list, and this is giving me some considerable trouble!
I have tried the most obvious way (Yes an item is selected, and I have already retrieved both the index and the string at that index):
CharListModel.removeElement(CharList.getSelectedValue());
However, this gives me a 'java.lang.ArrayIndexOutOfBoundsException: -1' error.
I have tried all of the permutations that you can see in the code below (Some are commented out but you get the idea):
DefaultListModel model = (DefaultListModel) CharList.getModel();//CharListModel;
int selectedIndex = CharList.getSelectedIndex();
if (selectedIndex != -1) {
//model.remove(selectedIndex);
//model.removeElement(CharList.getSelectedValue());
//model.removeElementAt(selectedIndex);
}
as well as a few other permutations as well:
CharListModel.removeElementAt(CharList.getSelectedIndex());
//or
CharListModel.remove(CharList.getSelectedIndex());
//or
CharList.remove(SelItemIndex);
In each case I get the same 'ArrayIndexOutOfBoundsException' error, even though the selected index is previously found with no trouble. And yes, I know I just said 'previously' so something could have changed things, but here is the code that runs directly before I try to remove the element:
int SelItemIndex = CharList.getSelectedIndex();
if(SelItemIndex == -1)
{
JOptionPane.showMessageDialog(null, "You have to select something!");
return;
}
String SelItem = CharList.getModel().getElementAt(SelItemIndex).toString();
//Create warning
final JComponent[] inputs = new JComponent[]
{
new JLabel("<html>Bla Bla " + SelItem + " Are you sure?</html>")
};
int n = JOptionPane.showConfirmDialog( null, inputs,"Deletion Confirmation Warning", JOptionPane.YES_NO_OPTION);
if( n == 1)
{
//Do not delete
return;
}
That is all there is before trying to remove the selected element.
For the life of me I don't know why this is not working! Am I missing something really silly here?
A confusing update
In the ActionPerformed event of a JButton I have used this code - The comments in the code explain why this is so confusing!:
DefaultListModel CharListModel = (DefaultListModel)CharList.getModel();
if( CharListModel.contains(CharList.getSelectedValue()) == true)
{
//Selected item is found
int selItemIndex = CharListModel.indexOf(CharList.getSelectedValue());
if(selItemIndex != -1)
{
//Selected item index is NOT -1 and is correct
CharListModel.remove(selItemIndex);
//Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
}
else
{
//NEVER reached
JOptionPane.showMessageDialog(null, "OUCH!");
}
}
As you can see, the index of the selected item is correct right up to the point of removing it, whereupon I once again get the out of bounds exception. I also tried this in the same place but with the same results:
CharListModel.removeElement(CharList.getSelectedValue());
Even more confusion
In an attempt to work out what is going on I have created a new DefaultListModel, enumerated the old one, and put each name into the new model, except the one that I want to remove (Remember I can get the index, the object and the text, I just cant delete it).
This has worked and I now have a DefaultListModel with the correct items in it, however, the moment that I try to CharList.setModel(NewModel); I once again get the out of bounds exception.
This has got me pulling out hair! Can anyone offer any ideas to try?
A Resolution of sorts
Not really a resolution at all, but to work around this maddening problem I am using the method laid out above, where I create a copy of the list model, minus the item that I want to delete and then simply catch the exception when using setModel, since the updated list model is added to the list just fine, and subsequent actions such as adding items etc. work okay, right up until I try to delete an item again anyway!
Thanks if you tried to help - and if you have any ideas about how to hunt down this problem, by all means post away!
regards
Max
For reference, I added the code below to this example. If it's not helpful, it may be a useful sscce for updating your question.
panel.add(new JButton(new AbstractAction("Remove") {
#Override
public void actionPerformed(ActionEvent e) {
int index = list.getSelectedIndex();
if (index != -1) {
model.remove(index);
}
}
}));
I had a similar problem. It turned out the error stemmed not from removing the element, but rather displaying the list. When implementing the
public void valueChanged(ListSelectionEvent e)
method, which updates the list on the screen, make sure to check if the model is null before you set the values. This is what caused the exceptions in my case.
Creating a list, removing from there, and then updating the model with the list is also a usable workaround.
I have faced the same issue. It seems when the item is removed from the model, it gets removed from the array as well. Hence is messes up the array index.
As a work around, I moved the array contents into a List and remove the List contents from the model. Now it works fine for me.
I have the same problem, i fix this doing that:
Button Action
int index = mylist.getSelectedIndex();
MyObject = (MyObject) mylist.getSelectedValue();
int Size = mylistmodel.getSize();
if (index >= 0 && MyObject != null && Size > 0) {
modeloLista.removeElementAt(indice);
}
And.
listValueChanged
if (list.getSelectedValue() != null) {
your code..
}

JCheckBox' value resets / doesn't change

I have several components, all inheriting JCheckBox without overwriting anything from it, stored in a vector, which is then traversed and each of the components is added to a dialog
CreateLists(); // initialises the checkbox vector
for(int i = 0; i < checkBoxes.size() ; i++){
myPanel.add(checkBoxes.elementAt(i));
}
Some of these checkboxes are already selected.
My problem now is: When I open the dialog and select or unselect any checkbox, the value of the checkboxes in the vector doesn't change. Selected stays selected and unselected stays unselected.
I also tryed to get the new values by using JPanels getCompoents(), but the values of these are wrong, too.
An ItemListener in the checkbox inheritors confirmes that the changes do happen, but whenever I try to get the new values, there just the same as those with which the checkboxes were initialised.
Here is a console output I used to keep track of the changes:
create lists
print values:
checkBox1 = true
checkBox2 = true
checkBox3 = false
checkBox2 clicked new value = false
checkBox3 clicked new value = true
print values:
checkBox1 = true
checkBox2 = true
checkBox3 = false
Here is some more code and information:
CreateList() compares a watchlist with the watchable things, creates the checkboxes accordingly (true = watched etc) and adds them to the new initalised vector.
To read the values i use this:
Component[] components = pnlGesamt.getComponents();
for(int i = 0; i < components.length; i++){
if(components[i] instanceof WLElementCheckBox){
WLElementCheckBox cb = (WLElementCheckBox) components[i];
System.out.println(cb.WlElement().Name() + " = " + cb.isSelected());
}
}
The JCheckBox inheritor:
private WatchListElement wlElement;
public WLElementCheckBox (WatchListElement wl, boolean selected)
{
super();
WlElement(wl);
setSelected(selected);
setText(wlElement.Name());
addItemListener(new MIL());
}
public WatchListElement WlElement ()
{
return wlElement;
}
public void WlElement (WatchListElement wlElement)
{
this.wlElement = wlElement;
}
public class MIL implements ItemListener{
public void itemStateChanged(ItemEvent arg0) {
System.out.println("Ckb " + wlElement.Name() +" geklickt Item neu = " + isSelected());
}
}
There is a small possibility that the changes to your checkboxes are not visible because you're querying them from another thread (such as main) instead of the Event Dispatch Thread (EDT). Swing is not thread-safe. When you check the checkboxes, the values are set on the EDT, but other threads might have the old state saved in memory and don't check to see if it's been updated. So, the first thing you need to do is ensure that when you're checking the states of your checkboxes, you're doing it from the EDT, using a call to SwingUtilities.invokeAndWait() or SwingUtilities.invokeLater().
For more information on why this happens, search for Thread Safety in Java.
(Also, don't be tempted to use synchronization to solve the problem or you might end up with a deadlock in your GUI).
One final pro tip: using new Java for-loop syntax makes code easier to read and write, as well as more efficient in some cases. Your code...
for(int i = 0; i < checkBoxes.size() ; i++){
myPanel.add(checkBoxes.elementAt(i));
}
... can turn into...
for(JCheckBox c : checkBoxes) {
myPanel.add(c);
}
This works on arrays and anything that is Iterable (which includes Collection).

Categories