My JFrame containing an embedded single graph (Graphstream) freezes when I try to update it in a loop that calls Thread,sleep(). I have tried using the same update on a standalone-graph (displayed on it's own) and it works as expected.
I have a single graph embedded in JFrame as follows (AppGraph.java):
public static ViewPanel init(){
graph.addAttribute("ui.stylesheet", styleSheet);
graph.setAutoCreate(true);
graph.setStrict(false);
graph.addAttribute("ui.quality");
graph.addAttribute("ui.antialias");
initGraph();
initNodes(graph);
return attachViewPanel();
}
private static ViewPanel attachViewPanel() {
Viewer viewer = new Viewer(graph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
viewer.enableAutoLayout();
return viewer.addDefaultView(false);
}
private static void initGraph(){
FileSource fs = new FileSourceDOT();
String graph_filename = "graph.gv";
String absolute_path = System.getProperty("user.home") + File.separator + graph_filename;
fs.addSink(graph);
try {
fs.readAll(absolute_path);
} catch (IOException | NullPointerException e) {
e.printStackTrace();
} finally {
fs.removeSink(graph);
}
}
Then this is called in the JFrame class as below:
/*AppWindow.java
* Set up graph
*/
GridBagConstraints graphConstraints = new GridBagConstraints();
graphConstraints.fill = GridBagConstraints.BOTH;
graphConstraints.gridx = 0;
graphConstraints.gridy = 1;
graphConstraints.weightx = 0.5;
graphConstraints.weighty = 0.5;
graphConstraints.gridwidth = 4;
graphConstraints.gridheight = GridBagConstraints.RELATIVE;
add(AppGraph.init(), graphConstraints);`
On the JFrame are buttons for different search algorithms like BFS. During the execution of these algorithms, edges traversed are colored at fixed time intervals to create a sort of animation effect as shown below:
//BFSAlgorithm.java
private void callBFS(Node startNode, Node goalNode) {
startNode.setAttribute("parent", "null");
startNode.setAttribute("level", 0);
startNode.setAttribute("visited?");
LinkedList<Node> queueFrontier = new LinkedList<>();
int level = 1;
queueFrontier.addLast(startNode);
while (!queueFrontier.isEmpty()) {
System.out.println("Level: " + (level - 1));
LinkedList<Node> next = new LinkedList<>();
for (Node node : queueFrontier) {
if (node == goalNode) {
System.out.println(node.getId() + ": Found Found Found!!!");
if (node != startNode) {
colorEdge(node);
}
return;
}
System.out.print(node.getId() + " visited \t");
if (node != startNode) {
colorEdge(node);
}
for (Edge edge : node.getEdgeSet()) {
Node opposite = edge.getOpposite(node);
if (!opposite.hasAttribute("visited?")) {
System.out.print(opposite.getId() + " enqueued \t");
opposite.setAttribute("level", level);
opposite.setAttribute("parent", node);
opposite.setAttribute("visited?");
next.addLast(opposite);
}
}
System.out.print("\n");
}
level++;
queueFrontier = next;
sleep();
}
}
private void colorEdge(Node node) {
Edge visitedEdge = node.getEdgeBetween(node.getAttribute("parent", Node.class));
visitedEdge.setAttribute("ui.color", 0.5);
sleep();
}
private void sleep() {
try {
Thread.sleep(AppWindow.speed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
This BFSAlgorithm implements DynamicAlgorithm and extends SinkAdapter. I have extended the SinkAdapter to enable it to interact with the View as the algorithm runs. When I call the BFSAlgorithm, while the algorithm runs and the various println statements are delayed by sleep(), the GUI freezes and is unresponsive until after execution before all the visited edges are then colored. I tried implementing ViewerListener in my AppGraph.java as is documented on the graphstream documentation but it only resulted in an infinite loop that crashed the application:
/*...init() method from AppGraph.java*/
ProxyPipe fromViewer = viewer.newThreadProxyOnGraphicGraph();
fromViewer.addSink(graph);
fromViewer.pump();
while(loop) {
fromViewer.pump(); //
}
Like #Frakool and #MadProgrammer suggested in the comments, if anyone is having similar issues, using SwingWorker and Swing Timer will provide the desired results. According to the documentation:
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread. However, you might use a general-purpose timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.
Here's how I used it to stop the gui freezing. I created a private inner SwingWorker class that uses a Swing Timer as below:
private class BFSTask extends SwingWorker<LinkedList<Node>, Node>{
private ArrayList<Node> visitedList;
private int visitedIndex = 0;
private boolean traversalDone = false;
private Timer traversal = new Timer(AppWindow.speed, new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
Node lastVisited = visitedList.get(visitedIndex);
Edge visitedEdge = lastVisited.getEdgeBetween(lastVisited.getAttribute("parent", Node.class));
visitedEdge.setAttribute("ui.color", 0.5);
visitedIndex++;
if(visitedIndex >= visitedList.size()){
traversal.stop();
traversalDone = true;
if(BFSAlgorithm.this.getPathToGoal() != null){
startTimer();
}
}
}
});
#Override
protected LinkedList<Node> doInBackground() throws Exception {
Node found = publishNodeBreadthFirst(getStartNode(), getGoalNode());
if (found != null) {
return getPathToGoal(found);
} else{
return null;
}
}
#Override
protected void process(List<Node> list) {
visitedList = (ArrayList<Node>) list;
traversal.start();
}
#Override
protected void done() {
try {
BFSAlgorithm.this.pathToGoal = get();
if(traversalDone && BFSAlgorithm.this.getPathToGoal() != null){
startTimer();
}
if(BFSAlgorithm.this.getPathToGoal() == null){
throw new NullPointerException("Goal Not Found.");
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (NullPointerException e){
JOptionPane.showMessageDialog(getAppWindow(), "Goal Node Not Found!", "Error", JOptionPane.ERROR_MESSAGE);
getAppWindow().disableExceptClear();
getAppWindow().changeStatus("Goal node not found");
}
}
private LinkedList<Node> getPathToGoal(Node found) {
LinkedList<Node> path = new LinkedList<>();
Node parent = found.getAttribute("parent");
path.addLast(found);
while (parent != getStartNode()){
path.addLast(parent);
parent = parent.getAttribute("parent");
}
return path;
}
}
Related
Hi Im trying to implement an exercise execution list where each exercise should change background color representing that thats the current exercise running every T time for each exercise. I wanted to know how to implement the action of waiting T time until the exercise finish to highlight the next one.
I have something like this, but blocking the thread is not doing the trick.
ArrayList<ExerciseData> exercises;
ExerciseData ex;
int curr = 0;
int cycleSize = warmUpAdapter.getItemCount();
exercises = (ArrayList<ExerciseData>) warmUpAdapter.getExerciseList();
while( curr < cycleSize ){
ex = exercises.get(curr);
ex.setRunning(true);
warmUpAdapter.notifyItemChanged(curr);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ex.setRunning(false);
warmUpAdapter.notifyItemChanged(curr);
curr++;
}
Try to use postDelayed method of Handler:
private static Handler handler = new Handler();
private void doExercise(final int curr){
ArrayList<ExerciseData> exercises = (ArrayList<ExerciseData>) warmUpAdapter.getExerciseList();
if (curr >= exercises.size()){
System.out.println("Finished");
return;
}
final ExerciseData ex = exercises.get(curr);
ex.setRunning(true);
System.out.println("Running exercise " + curr);
warmUpAdapter.notifyItemChanged(curr);
handler.postDelayed(new Runnable() {
#Override
public void run() {
ex.setRunning(false);
warmUpAdapter.notifyItemChanged(curr);
doExercise(curr + 1);
}
}, 1000);
}
... call later to start exercises:
doExercise(0);
So I have implemented Observer Pattern. In the update function of my Observer class, I want to update the progressbar. The problem is that it instantly updates to the maximum value. I don't get an loading animation. The application calculates some edges. After the calculation of the edges, they will be drawn on the screen.
My observers are the following :
GenerateEdgeBottom
GenerateEdgeLeft
GenerateEdgeRight
I also have a KochManager and an javaFX class.
I make my progressbars in the javaFX class. In the javaFX class is a function that binds all the progressbars.
Part of my javaFX class
public void binding(GenerateEdgeLeft leftEdges, GenerateEdgeRight rightEdges, GenerateEdgeBottom bottomEdges) {
progressBarLeft.progressProperty().unbind();
progressBarLeft.setProgress(0);
progressBarRight.progressProperty().unbind();
progressBarRight.setProgress(0);
progressBarBottom.progressProperty().unbind();
progressBarBottom.setProgress(0);
progressBarLeft.progressProperty().bind(leftEdges.progressProperty());
progressBarRight.progressProperty().bind(rightEdges.progressProperty());
progressBarBottom.progressProperty().bind(bottomEdges.progressProperty());
lblLeft.textProperty().bind(leftEdges.messageProperty());
lblRight.textProperty().bind(rightEdges.messageProperty());
lblBottom.textProperty().bind(bottomEdges.messageProperty());
}
My kochmanager
public void changeLevel(int nxt) {
edges.clear();
koch.setLevel(nxt);
this.leftEdgeTask = new GenerateEdgeLeft(koch);
this.bottomEdgeTask = new GenerateEdgeBottom(koch);
this.rightEdgeTask = new GenerateEdgeRight(koch);
tsCalc.init();
tsCalc.setBegin("Begin calculating");
//System.out.println("test 3");
Platform.runLater(() -> {
//System.out.println("test 4");
application.binding(leftEdgeTask,rightEdgeTask,bottomEdgeTask);
futures.clear();
Future<?> f = pool2.submit(bottomEdgeTask);
Future<?> f2 =pool2.submit(leftEdgeTask);
Future<?> f3 = pool2.submit(rightEdgeTask);
futures.add(f);
futures.add(f2);
futures.add(f3);
try {
edges.addAll(leftEdgeTask.get());
edges.addAll(rightEdgeTask.get());
edges.addAll(bottomEdgeTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//System.out.println("test 5");
koch.getEdges().clear();
tsCalc.setEnd("End calculating");
application.setTextNrEdges("" + koch.getNrOfEdges());
application.setTextCalc(tsCalc.toString());
application.requestDrawEdges();
drawEdges();
});
}
My GenerateEdgeLeft
public class GenerateEdgeLeft extends Task<ArrayList<Edge>> implements Observer {
private KochFractal koch;
//private KochManager manager;
private int numberOfEdges;
private int counter;
private ArrayList<Edge> edges = new ArrayList<>();
public GenerateEdgeLeft(KochFractal koch){
this.koch = koch;
numberOfEdges = koch.getNrOfEdges() / 3;
this.koch.addObserver(this);
}
public GenerateEdgeLeft() {
//edges.size
}
public ArrayList<Edge> getResult() {
return edges;
}
#Override
public ArrayList<Edge> call() throws Exception {
counter = 0;
System.out.println("Thread Started Left");
koch.generateLeftEdge();
edges = koch.getEdges();
return edges;
}
#Override
public void update(Observable o, Object arg) {
edges.add((Edge) arg);
counter++;
updateProgress(counter, numberOfEdges);
updateMessage( "Left : " + counter);
}
}
So my code works just the way I want it the only issue I'm having is this.. Basically I am having a main class which controls gates on a railroad track, when a train is approaching or crossing the track from either 1 of two tracks the gates should close. The only issue I'm having is the statements for when a gate opens or closes spam like 3-5 times everytime it does something so if the gate is closing it will go..
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closed
I'm wondering why this is occuring, here is my code for the Gate class and Main class
public class Gate {
private boolean isClosed = false;
private boolean closing = false;
private boolean opening = false;
public Gate(){
}
public void close(){
if(!(isClosing() == true)){
Runnable task = new Runnable() {
public void run() {
try {
setClosing(true);
setOpening(false);
System.out.println("GATE: Closing");
Thread.sleep(400);
System.out.println("GATE: Closed");
setClosed(true);
setClosing(false);
}catch(Exception ex){
}
}
};
new Thread(task, "closeThread").start();
}
}
public void open(){
if(!(isOpening() == true)){
Runnable task = new Runnable() {
public void run() {
try {
setOpening(true);
System.out.println("GATE: Opening");
Thread.sleep(400);
setOpening(false);
if(closing == false){
setClosed(false);
System.out.println("GATE: Opened");
}
}catch(Exception ex){
}
}
};
new Thread(task, "openThread").start();
}
}
public boolean isClosed(){
return isClosed;
}
public boolean isClosing(){
return closing;
}
public boolean isOpening(){
return opening;
}
public synchronized void setClosing(boolean t){
closing = t;
}
public synchronized void setOpening(boolean t){
opening = t;
}
public synchronized void setClosed(boolean t){
isClosed = t;
}
}
public class Controller {
public static void main(String[] args){
Track t1 = new Track("Track 1");
Track t2 = new Track("Track 2");
Gate g = new Gate();
t1.simulateTrack();
t2.simulateTrack();
do{
System.out.print("");
if((t1.isApproaching() || t1.isCrossing()) || (t2.isApproaching() || t2.isCrossing())){
if(!g.isClosed() && !g.isClosing()){
g.close();
}
}else if(g.isClosed() && !g.isOpening()){
g.open();
}
}while((t1.isSimulating() || t2.isSimulating()));
}
}
Also the code for Track
import java.security.SecureRandom;
public class Track {
private static final SecureRandom gen = new SecureRandom() ;
private boolean approaching = false;
private boolean atCrossing = false;
private boolean simulating = false;
private String trackName = "";
public Track(String n){
trackName = n;
}
public void simulateTrack(){
Runnable task = new Runnable() {
public void run() {
try {
setSimulating(true);
for(int i = 0; i < 10; i++){
Thread.sleep((gen.nextInt(5000) + 2500));
setApproaching(true);
System.out.println(trackName + ": Train is now approaching.");
Thread.sleep((gen.nextInt(5000) + 3500));
setCrossing(true);
setApproaching(false);
System.out.println(trackName + ": Train is now crossing.");
Thread.sleep((gen.nextInt(1000) + 1000));
setCrossing(false);
System.out.println(trackName + ": Train has left.");
}
setSimulating(false);
} catch (Exception ex) {
}
}
};
new Thread(task, "simulationThread").start();
}
public boolean isApproaching(){
return approaching;
}
public boolean isCrossing(){
return atCrossing;
}
public boolean isSimulating(){
return simulating;
}
public synchronized void setSimulating(boolean t){
simulating = t;
}
public synchronized void setApproaching(boolean t){
approaching = t;
}
public synchronized void setCrossing(boolean t){
atCrossing = t;
}
}
This is just an idea:
By shooting the close() logic on a background thread you lose the atomicity. The main's do loop can go around 5 times before it gives up the control of the main thread and one of the "closeThread"s start executing. Don't you see multiple "GATE: Closed"s as well?
Try this (not tested, sorry):
public synchronized void close() { // added synchornized
if (!isClosing()) { // read: "if not closing"
setClosing(true); // set closing so next time close() is called it is a no op
setOpening(false); // close other loopholes so the state is correct
System.out.println("GATE: Closing");
// we're in closing state now, because the close method is almost finished
// start the actual closing sequence
Runnable task = new Runnable() {
public void run() {
try {
Thread.sleep(400);
System.out.println("GATE: Closed");
setClosed(true);
setClosing(false);
}catch(Exception ex){
}
}
};
new Thread(task, "closeThread").start();
}
}
You'll need to modify open() the same way, so that the invariants are always kept. Checking and setting the closing and opening flags are mutually exclusive, that's what you get by placing synchronized on both of them.
I have JTabbedPane with 4 tabs. JtabbedPane is situated on a JLayeredPane. 1st and 4th tab contain JTable with custom models. Each of the tables is being refreshed every 5-10 seconds.
When 1st tab is active, and JTable on 4th has just finished refreshing, I can see content of the 4th on the 1st. Look at the screenshot.
When I click on the other tab, or minimize window, that strange effect is gone. Till the next refresh of that table on 4th tab. Refreshing is done using Future<> object.
I used Swing GUI builder in Netbeans, so I have huge amount of code. Would post any piece which could be useful.
I tried to revalidate jTabbedPane, is had no effect. Both tables and jScrollPanes has opaque property set to true. So I tried to use SwingUtilities.invokeLater(). It helped a little bit - now first content update goes well, but later - the same problem.
2nd table model has method to update it's content
public void setData(LinkedList<Object[]> __rows) {
NewDevsTableModel.__rows = __rows;
fireTableDataChanged();
}
It is used here (I added SwingUtilities here)
static class checkNew implements Callable<Boolean> {
#Override
public Boolean call() {
ServiceMessage sm = ServiceMessage.getNewList();
try {
connect();
os.write(sm.serialize());
for (int i=0; i<10; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {}
if (is.available() > 0) {
break;
}
if (i == 9) {
disconnect();
return false;
}
}
byte[] actByte = new byte[is.available()];
is.read(actByte);
try {
sm = ServiceMessage.Deserialize(actByte); //may be there are no new devices
if (sm.getType() == ServiceMessageType.NODATA) {
MainWindow.jTabbedPane1.setEnabledAt(3, false);
if (MainWindow.jTabbedPane1.getSelectedIndex() == 3) {
MainWindow.jTabbedPane1.setSelectedIndex(0);
}
return true;
} else {
return false; //wrong answer type
}
} catch (ClassCastException | StreamCorruptedException e) {
//remember selection and scroll
final int scroll = MainWindow.jScrollPane3.getVerticalScrollBar().getValue();
final int[] rows = MainWindow.newDevsTable.getSelectedRows();
int col = MainWindow.devicesTable.getSelectedColumn();
String[] parts = new String(actByte).split("\n");
final LinkedList<Object[]> l = new LinkedList();
for (int i=0; i<parts.length; i++) {
String[] dev = parts[i].split(";", -1);
String descr = dev[2];
boolean iptype = (!dev[3].equals("-"));
String address = dev[4];
boolean atmtype = (dev[5].equals("+"));
if (MainWindow.newDevsTable.getRowCount() >= (i+1)) {
if ((MainWindow.newDevsTable.getValueAt(i, 4) != null) && !MainWindow.newDevsTable.getValueAt(i, 4).equals("")) {
descr = MainWindow.newDevsTable.getValueAt(i, 4).toString();
}
}
Object[] o = {dev[0], dev[1], MainWindow.language[180], MainWindow.language[4], descr, iptype, address, atmtype};
l.add(o);
}
if (!l.isEmpty()) {
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
MainWindow.newDevsPanel.setVisible(true);
MainWindow.jTabbedPane1.setEnabledAt(3, true);
((NewDevsTableModel)MainWindow.newDevsTable.getModel()).setData(l);
ButtonColumn buttonColumn = new ButtonColumn(MainWindow.newDevsTable, addAction, 2, true);
buttonColumn = new ButtonColumn(MainWindow.newDevsTable, rejAction, 3, false);
//put selection back
for (int i=0; i<rows.length; i++) {
MainWindow.newDevsTable.addRowSelectionInterval(rows[i], rows[i]);
}
MainWindow.jScrollPane3.getVerticalScrollBar().setValue(scroll);
}
});
} else {
MainWindow.jTabbedPane1.setEnabledAt(3, false);
if (MainWindow.jTabbedPane1.getSelectedIndex() == 3) {
MainWindow.jTabbedPane1.setSelectedIndex(0);
}
}
return true;
}
} catch (IOException e) {
disconnect();
return false;
} catch (ClassNotFoundException e) {
return false;
}
}
}
I submit the task this way
public static Future<Boolean> checkNewDevices() {
final Future<Boolean> task;
task = service.submit(new checkNew());
return task;
}
To refresh automatically I use separate thread
public class CheckNewPassThread extends Thread {
int pause = 10000;
#Override
public void run() {
for (;;) {
HostConnection.checkNewDevices();
try {
Thread.sleep(pause);
} catch (InterruptedException e) {}
}
}
}
Which is started when the window is opened
private void formWindowOpened(java.awt.event.WindowEvent evt) {
HostConnection.getData();
HostConnection.getDeviceAddress();
RefreshData refreshThread = new RefreshData();
refreshThread.start();
new CheckNewPassThread().start();
}
OMG, the problem was in calling jTabbedPane.setEnabledAt(3, true) to already enabled tab. Swing is fascinating
this is my first post so please inform me if I'm doing anything wrong.
I've been working on a mini-applet to get the status of several nodes. This works by having a thread of each node which checks its uptime in a network class. Once it gets the status of the node it'll put it into a hashmap. I then check the status of that hashmap and wait till its content amount is equal to the total amount of nodes that we're checking. Once done it proceeds to display them on a screen. The problem is despite making this a heavily threaded application fetching the status for all nodes still takes a few seconds. During this time I want to display a separate JFrame with a loading bar (or even use the same JFrame if it would work.
I'm not going to lie I'm a Java newbie and the code is horrible, I'm wondering if you guys could give me advice as to what I should do. Here is the code.
--main node status window--
public NodeStatusWindow() {
super(Managment.getTitle() + " - Nodes");
setLayout(new FlowLayout());
Thread threada = new Thread(new ShowLoading());
threada.start();
System.out.println("Checking node status...");
Nodes.nodeOnlineCurrent.clear();
for (int i = 0; i < Nodes.getNodes().size(); i++) {
Object currentNode = Nodes.getNodes().keySet().toArray()[i];//
Thread thread = new Thread(new CheckUptime((int) currentNode));
thread.start();
}
while (Nodes.nodeOnlineCurrent.size() < Nodes.getNodes().size()) {
System.out.println(Nodes.nodeOnlineCurrent.size() + " < " + Nodes.getNodes().size());
}
for (int i = 0; i < Nodes.nodeOnlineCurrent.size(); i++) {
JLabel[] nodeList = new JLabel[Nodes.getNodes().size()];
Object currentNode = Nodes.getNodes().keySet().toArray()[i];
nodeList[i] = new JLabel("Node: " + currentNode.toString());
nodeList[i].setOpaque(true);
if (Nodes.nodeOnlineCurrent.get(currentNode)[0] == false) {
nodeList[i].setForeground(new Color(255, 0, 0)); //Red
} else if (Nodes.nodeOnlineCurrent.get(currentNode)[1] == false) {
nodeList[i].setForeground(new Color(230, 222, 0)); //Yellow
} else {
nodeList[i].setForeground(new Color(0, 230, 0)); //Green
}
add(nodeList[i]);
}
--Loading window. This class is created in the threada object in the class above--
public class NodeStatusLoadingWindow extends JFrame {
private static final long serialVersionUID = 1369511477485416862L;
private JLabel status;
public NodeStatusLoadingWindow() {
super(Managment.getTitle() + " - Nodes");
setLayout(new FlowLayout());
status = new JLabel("Loading...");
add(status);
}
public void closeWindow() {
this.setVisible(false);
this.dispose();
}
}
Here is the showLoading thread
public class ShowLoading implements Runnable {
public void run() {
try {
GUIHandler.drawNodeStatusLoadingWindow();
} catch (Exception e) {
System.out.println("Exception! " + e.toString());
}
}
}