Concurrency problem in updating Jtable/Jframes? - java

I will try to describe the problem as accurately as possible, since this is a legacy project from someone else that I am trying to fix (to the best of my abilities).I cannot build an accurate working example due to the way this project is done. The way this program was built is wrong in many places so just pinpointing the problem would help me immensely.
There is a Jframe with a menu bar and table. The frame switches between Tables based on which option you click within the menu bar. Here's the problem: When updating the table, the first table seems to be fine, but the other ones fails to update its view properly if something is changed (specifically, it doesn't update upon deletion, but insertion seems fine). This is how the main table is made:
public void start(){
mainTable=new Jtable(model){
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
/* tons of styling here */
};
}
/* a bit more styling? */
renderSecondTable();
renderThirdTable();
}
I apologize for this ugly code, the codebase itself is even worse. Does rendering other tables by calling a method within this "start()" function lead to concurrency problems? Which is why updating the view is problematic? Extra information that might help:
each table has a seperate Frame, so there is a main frame and then a smaller frame that displays the tables
buttons for each table (delete, update etc.) are also added in by calling in extra methods like RenderButtonsForTable1()
They are all AbstractTableModel
calling FireTableDataChanged() or other similar functions do not seem to do work for non-main-tables
revalidate and repaint also doesn't work
The table does NOT use removeRow or fireTableRowsDeleted to remove the rows, there are seperate functions that do this with object streams (I don't quite understand this yet). The view seems to update automatically for the main table just fine.
Again I apologize if this is extremely confusing and ugly, the original creator didn't seem to understand what he was doing either :/
Edit: More clarification!
It seems that it does in fact only have one MAIN frame, and this
frame swaps out multiple Jpanels (each with a different Table). Within each Panel there is also a scrollpane
Deleting from the main Panel causes no problems, but deleting entries from other Panels require the Frame to be reopened or else the entry just stays there (also breaks the frame/panel completely if you empty the entire table)
The entry is properly disposed of in the backend (as far as I can tell)
any insert/delete is done through an action listener for the corresponding button
Each table has a seperate Tablemodel (all extends AbstractTableModel) with its seperate functions that all do the same thing
Table is filled with data read from a database
all tables are made in the same fashion as the main one above with prepareRenderer
I also drew up a vague diagram of how the Jframe looks:

Related

GUI with multiple frames

I'm looking to build out a Java GUI with a table area and an area that will display the data of a selected row of the table. I've never tried a multi-frame set up before so before I venture to do this I wanted to check with others. Is it difficult to have two frames and have them passing data back and forth? The idea would be that I could move the details frame anywhere I like on the screen or to a second monitor and allow the table to go full-screen if the user wants. Any input or examples are appreciated.
don't to create two of more JFrames use JDialog instead,
reuse this JDialog for another action(s)
create one JFrame and one JDialog for displaying details
have to determine if and which of JTables row(s) is selected
better would be to set ListSelectionMode to the SingleSelection
maybe would be better to invoke (show that already exist) JDialog from JPopupMenu Action
You should have no problem in doing what you are after. You can have public methods in each frame which expose properties and/or structures and you then pass the instance of one JFrame to the other. This should allow you to pass data back and forth.
That being said however, I think that this scenario is valid only when you have one, two, or at most three JFrames. Having a lot of frames calling each other could result a maintenance nightmare.
there are several possibilities to do so:
you can add one of the jframes as a listener to anothe, or both to each other. For this, you have to implement a listener mechanism, like in java.awt. You can pass the information contained in the event objects - this would be the most clean alternative
you can pass the instance of the detailframe directly in the constructor of the main frame and call operations from main frame on detail frame. this is the simplest way, but you will need lot of code changes if you have some new features to add

Revalidating JList - custom elements

I'm using a JList to hold chat data for my chat program.
It uses a custom list renderer to render a custom JPanel object as the element type.
This JPanel contains two JLabels (anchored to the top, for name and time), and a JTextArea (anchored to the bottom, for chat message).
It looks like this:
Everything works great, but I want to add a hide/show feature.
Using a previously programmed PopupMenu handler, I have a popup appear when you right click on an element.
When you click hide (or show, it's a toggle) then it should minimize the element like so...
The only problem is... it doesn't update the JList cell size as you can see the large empty region where the text used to be.
However, when I type another message...
The JList fixes the cell size completing the 'hide' operation.
My question is how do you get the JList to revalidate/repaint/etc programmatically.
And don't think I haven't tried all the obvious solutions...
public void setHidden(boolean hidden) {
// this is in the custom JPanel class
System.out.println("Initial: " + this.getPreferredSize());
// TextArea is the JTextArea which we set invisible when we want to hide it.
TextArea.setVisible(!hidden); // TextArea is a variable btw
this.invalidate();
this.validate();
this.repaint();
System.out.println("After: " + this.getPreferredSize());
container.revalidate();
}
/*
* This is what the above printlns show when you hide, then show the element.
*
* Initial: java.awt.Dimension[width=176,height=38]
* After: java.awt.Dimension[width=176,height=20]
* Initial: java.awt.Dimension[width=176,height=20]
* After: java.awt.Dimension[width=176,height=38]
*/
public void revalidate() {
// container.revalidate() ^^^
// list is the list containing the chat elements
list.invalidate();
list.validate();
list.repaint();
}
The custom JPanel class uses a GroupLayout to render the components.
Do you guys have any knowledge on how to programmically cause a JList to revalidate its cell sizes?
... besides the methods that I've posted? :)
Solution:
After searching method after method and testing if they would solve my problem, I found that executing this code after a hide/show operation would cause the cell height (and width) to be recalculated and without any unwanted visual 'flicker' of the JList.
list.setFixedCellHeight(0);
list.setFixedCellWidth(0);
list.setFixedCellHeight(-1);
list.setFixedCellWidth(-1);
Without seeing any code, I can only guess: the most probable reason is that you're doing the hide under the feet of the list, that is without its model notifying its listeners. The list's ui delegate caches the cell size deep inside, which is cleared on receiving ListEvents
This is job for JTable with two Columns (Chat and Boolean) in the TableModel and with visible Chat Column only, the trick is by using by implement RowFilter where you set as parameter to the second column only String "false" (Object in the JTable with Boolean is possible filtering with returns value in the String "true" / "false")
This is a very peculiar shortcoming of the JList class. I ran into the issue myself in the course of cleaning up some of my code in unrelated areas.
For what it's worth, removing the element from the ListModel and then adding it again will produce the appropriate dimensions for the associated rendered component in the JList. It's an odd way to go about it, and seems to cause the list to behave the same way as the accepted (and preferred) solution:
list.setFixedCellHeight(0);
list.setFixedCellWidth(0);
list.setFixedCellHeight(-1);
list.setFixedCellWidth(-1);
I stumbled upon this problem because the code for my project was originally written to invoke the removeAllElements() method of the ListModel and then add all of the elements again one by one using addElement(). Everything was working great until I decided that I should rewrite the program so that it would simply leave the model alone whenever changes to the dimensions of the displayed components in the JList were requested by the user. In other words, it was unnecessary to involve the model because elements were not being added or removed from the list. Unfortunately, after changing the preferred size of the renderer, no amount of repaint() or revalidate() method calls on the JList would cause it to layout its elements correctly. In my case, only resizing the parent component (a JFrame) produced the intended behavior.

Swing JTable custom rendering

I have this kind of progamming task without JavaFx, instead it's Java Swing. I realized my knowledge is still limited.
I have one single JTable.
But, within this JTable I need a custome Cell Renderer.
The goal is to make this kind of JTable: Example image
My current solutions are: Example Image
Create a Single JTable:
get each Column and set its CellRenderer with a custom Renderer (below).
Create a new Class implements TableCellRenderer:
return different JPanel inside getTableCellRendererComponent
method using switch case (as column counted).
After hours, and hours, I think my current solutions is quite daunting tasks. Thus, My question is:
What are the simplest method of creating this Custom JTable to achieve the main goal as mentioned above?
you have two options
1) JPanel nested another JComponents and solve that by using standard LayoutManagers note scrolling isn't natural nor nice
2) JTable with JPanel can solve that, notice about scrolling inner JScrollPane inside another JScrollPane
I've been facing this problem for a while, and I decided to do it myself. Extending the existing implementation of a table, adding some concepts for what I expect from a table, and writting some editors/listeners for that. All the same, but with a treetable.
I'm working on this project called SUMI.
It contains a java package (ar.com.tellapic.sumi.treetable) that is an extension of a JXTreeTable from SwingLabs.
The project is being developed and I didn't provide any documentation yet. You can do what you want by creating a renderer and if needed, an editor, for lastly attaching actions to each object.
If you decide to use it and you need help, email me, I'll help you without any problem.
Or, you could read the source by your own.
Regards,
EDITED (again):
To clear a little bit this answer, I've just created a wiki page in the project wiki and put the relevant code there. If someone feels that the code should be inserted here, please let me know.
Basically, I try to explain how to find a straight solution to the renderer/editor problems you may find using JTable with your specifics needs by using part of my project, in order to get something like this:
Note that the screenshot was taken after clicking on the respective tick-button.
Once you create a nested panel for one row, as suggested by #mKorbel, you can add any number of them to a GridLayout(0, 1) in a JScrollPane. If rendering many rows becomes an issue, you can adopt the same approach used by JTable, illustrated here.
Even though, JTable can be customized to whatever you desire through cell renderer and cell editors, it is never preferred because you have to do a lot of messy codings for that. Instead, for your problem, I suggest to use JScrollPane and add your component (view panel as your sample jTable ) to its viewPort.
For this implementation, represent each rows with your custom class that extends JPanel. And add the required row components (that may be any components like jlabel, jtextfields or even jpanel too) in it. For the simplicity, you can use null layout for the row panel and add the components at any location you want.
I hope this will help you workout with your problem. If you got any problem in this implementation, feel free you ask again.

Java Swing Threading Problem

so here's the problem. I have a JDialog box that consists of 3 combo boxes, a text field, a few buttons and a JTable. The JTable information is filtered based on the text field and combo boxes, so for instance it starts with all of the data and gets shrunk down to only the data that starts with any string value the user decides.
What's happening though is that while the values filter correctly, if I click in the JTable (in the white space, where there are no rows) then the rows that were deleted show up, like they were invisible until I clicked on them. I've tried almost everything:
I've tried re-creating the table every time filter is clicked (bad hack that didn't even work), I've called all of the repaint, revalidate, firechanged methods, I rewrote the dialog from scratch to make sure I didn't do any stupid mistakes (if I made one I didn't find it at least), and I've tried putting them on separate threads. The only fix I haven't tried is using a swing worker, but that's because my filtering was a little too complicated for me to figure out what goes where and how to extend the swing worker correctly. The GUI is generated by netbeans (bleh), and has worked in my other dozen or so JDialogs just fine (perfectly in fact). Here's the method that doest the filtering, if any of you can help it would be greatly appreciated.
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
nameFilter = "task1";
javax.swing.table.DefaultTableModel dm = (javax.swing.table.DefaultTableModel)jTable1.getModel();
tempParameters = parameters;
String currentString;
int rowNumber = 0;
while (dm.getRowCount()>rowNumber){
currentString = (String)(jTable1.getValueAt(rowNumber,1));
if(!nameFilter.equalsIgnoreCase(currentString.substring(0,nameFilter.length()))){
dm.removeRow(rowNumber);
parameters--;
}
else rowNumber++;
}
parameters = numOfRows;
}
Update, I also implemented the filter from the comment below, and while it filtered out the correct data, it had the exact same problem. In the future I will probably use this filter feature though, so thanks.
Another update, the code is still failing even after removing everything but this chunk, and all (at least I believe..) I am doing here is doing a simple remove row call. Hope this helps a bit.
Have you tried creating a new Model every time you want to filter, instead of clearing it by deleting rows? Create new model, copy relevant rows to new Model, set new Model in table. Really shouldn't be necessary, but it might be a quick fix.
Also, I really have to wonder why you're calling toLowerCase on two strings when you're using equalsIgnoreCase to compare them.
So long as this method is called from the EDT I don't think there would be a threading problem. Try using
SwingUtilties.isEventDispatchThread()
to make sure.
If you look at the API for DefaultTableModel, updates are being sent to your JTable which will repaint itself, so I don't think that is the problem.
I would guess that it is a logic problem. If you can extract the logic into separate methods it will be easier to test and verify whether it is updating the model as you expect.
Couple of observations:
If the filter happens to be larger than the string content of the row, it'll throw in the substring call
Calling the dm.removerow is generating a bunch of tablerowsdeleted events.
You're asking for a rowcount from the model, yet are getting the value through the table (a little inconsistent, if the model gets wrapped around another model you might be acting upon different rows), so instead of jtable1.getvalueat, use the dm.getvalueat.
I think what might be happening is that as the events get fired I see there are repaint and revalidate events fired in the JTable, these can be trampling over each other as they get enqueued in the EDT.
What I would suggest is to create a new datamodel, add the rows that you want to keep, and then reassign it to your jTable1.setModel(newDm);
Also to watch for is if someone else is modifying the model while you're in your eventlistener.
Hope this helps

"Fixing" the first few columns in an SWT table to prevent them from scrolling horizontally

We have implemented a table-based editor with an SWT tree table. It pretty much does what we want it to, except that we can't find a way to ensure that the first few columns remain at their position when the user scrolls horizontally (so that one can identify the row being edited).
There are a number of third-party table controls in various degrees of being incomplete or abandoned, and a snippet that shows how to use two tables (which get out of sync when they're being scrolled vertically).
What's the best way to solve this?
This 'official' SWT snippet does what you want to do, at least on my machine - it does not get out of sync on vertical scroll. But the price is a second scrollbar - way ugly. To prevent this, you can add the style SWT.NO_SCROLL in the constructor of the left table.
To improve the thing you will have to add a Listener on SWT.Traverse to both Tables, which syncs them if the user navigated using keys, something like this:
leftTable.addListener(SWT.Traverse, new Listener() {
public void handleEvent( Event event ) {
rightTable.setTopIndex(leftTable.getTopIndex());
}
});
I wouldn't go with another Table implementation, since you lose the biggest advantage you have with SWT: using a native widget, which looks and feels 'right' in each OS. If you want to do that anyway, Grid from the Nebula project is much more mature than the alpha tag implies. Don't know if it can do what you want, though.
the "syncing problem" only appears on MacOs based SWT. If your target platform is windows you should have no problem with the given SWT-Snippet. There will be more listeners to register on both tables to synchronise all occuring events (collapse, expand, mouse scroll, etc.)

Categories