Swing JFreeCharts echtzeit Charts bzw Aktualisieren von Charts

Booyeoo

Mitglied
Hallo, ich hab eine ernsthaftes Problem mit JFreeCharts :-/
Habe bereits auch in das JFreeCharts Forum gepostet, allerdings konnte mir auch letztes mal hier sehr schnell geholfen werden da jemand das gleiche Problem hatte, daher hier mein Problem:

Ich habe ein Chart welches für jedes Frame aktualisiert werden sollte. Allerdings passiert zur Laufzeit gar nix mit dem Chart, erst wenn es durchgelaufen ist, wird das Chart angezeigt. Das ist aber nicht so toll. Es sollte halt kontinuierlich geändert werden, während auch sich die Frames bei dem 3D Männchen ändern. Siehe Screenshots.

Hab bereits im JFreeChart gelesen:
"5. Does JFreeChart support real-time charting?

Not really. JFreeChart includes an event-notification mechanism that ensures that charts are updated whenever the dataset is updated. However, the chart is completely repainted for each update, which limits the "frames per second" rate that you can achieve with JFreeChart. Typically, updating once per second is fine, but updating multiple times per second results in high CPU load. If you want to pursue this, do some performance testing with your specific configuration and use cases. "

Und ich würde den Performanceverlust in kauf nehmen und versuchen durch performanteren Code ersetzen (da geht noch einiges)
allerding bin ich der Meinung dass es auch schon so klappen müsste, auch wenn, von mir aus, sehr langsam. Hauptsache es läuft.

Mein Proggy vergleicht halt den aktuellen Bewegunsablauf des 3DMännchens mit anderen schon geladenen Bewegungsabläufen und dies sollte dann im Chart verdeutlicht werden.

Screenshots:

kostenlos bilder hochladen - bilduploadr.de

kostenlos bilder hochladen - bilduploadr.de


Das ist im Prinzip der Code:
Java:
while (iterating over the frames till the end){
doSomeCoolStuff();
create3DHorLineChart(TreeMap<String, Double> tmSD, JTabbedPane output); // here is the original code of the
}

 private void create3DHorLineChart(TreeMap<String, Double> tmSD, JTabbedPane output) {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        Map map1 = sortByValue(tmSD);


        for (String key : ((Map<String, Double>) map1).keySet()) {
            String[] NameAndMatch = key.split("###");
            dataset.addValue(tmSD.get(key) * 100, NameAndMatch[0], NameAndMatch[1]);
        }

        final JFreeChart chart = ChartFactory.createBarChart3D(
                "Matching probability of " + labAmc.getText().split("\\\\")[labAmc.getText().split("\\\\").length-1], // chart titel
                null, // yachse
                "Percent", // xachse
                dataset, // daten
                PlotOrientation.HORIZONTAL, // orientation
                true, // legende
                true, // tooltips
                false // urls
                );

        final CategoryPlot plot = chart.getCategoryPlot();
        plot.setForegroundAlpha(1.0f);

        // linksbüdig die labels
        final CategoryAxis axis = plot.getDomainAxis();
        final CategoryLabelPositions p = axis.getCategoryLabelPositions();

        final CategoryLabelPosition left = new CategoryLabelPosition(
                RectangleAnchor.LEFT, TextBlockAnchor.CENTER_LEFT,
                TextAnchor.CENTER_LEFT, 0.0,
                CategoryLabelWidthType.RANGE, 0.30f);
        axis.setCategoryLabelPositions(CategoryLabelPositions.replaceLeftPosition(p, left));


        // mache chart sichtbar
        ChartPanel cp1 = new ChartPanel(chart);
        if (output.getTabCount() > 0) {
            output.remove(0);
        }
        output.add(cp1);
        output.setTitleAt(0, "Matching");
        output.setSelectedIndex(0);
        output.repaint();
        cp1.repaint();
    }
Das output.repaint(); und cp1.repaint(); lösen leider nicht mein Problem und ich verstehe einfach nicht warum nix während des Durchlaufens der Animation geändert wird, sondern erst am Ende, obwohl der Aufruf mitten in der Schleife steht. :-/
Hat jemand irgendeine Idee oder ein ähnliches Problem schonmal gehabt. Vielleicht muss ich ein Delay oder sowas einbauen.

Danke für jede Antwort
Grüße
 
Zuletzt bearbeitet:
S

SlaterB

Gast
hast du schonmal ein einfaches JFrame mit einer Animation bestehend aus ein paar sich ändernen drawLine()-Befehlen gemacht oder ähnliches?

von wo wird create3DHorLineChart() letzlich gestartet, von einem ActionListener, also der GUI selber?
solange die Aktion nicht beendet ist, kann die GUI nichts anderes machen,
jede länger andauernde Aktion gehört in einen separaten Thread
 

Booyeoo

Mitglied
Danke zunächst einmal für die helfende Antwort Slater B.

Zur ersten Frage: Ne das habe ich leider noch nicht gemacht. Aber wie meinst Du das genau?

Also der ganze Prozess wird gestartet wenn ich auf einen Knopf auf der GUI drücke welche sich aber in einem anderen Frame befindet.. oder ist das egal, dass sie sich in einem anderen Frame befindet?
Dann läuft der durch und rechnet wie wild und wenn sich nach ein paar Frames zum Beispiel der Arm um 10° bewegt hat, dann sollte das Chart aktualisiert werden.
Ich glaube aber zu verstehen was Du meinst. Mich wundert dann nur warum er dann das Männchen in den einzelnen Frames aktualisiert, vielleicht weil aus einem anderen Package kommt? Zudem ist das bei mir in einer anderen Klasse. Aber die anderen Frames sind in derselben Klasse wie der Actionlistener der auf den Knopf hört. Und während der Knopf noch seinen Prozess ausführt können andere Aktionen, wie zum Beispiel das erneute Malen des Charts nicht ausgeführt werden. Stimmts so halb?

Jetzt zu der Lösung. Ich habe immer versucht mehere Threads zu umgehen, weil die für mich zu kompliziert sind und weil ich dann angst hab die Übersicht zu verlieren, wenn etwas nicht funktioniert... Ist das Tutorial auf "Java ist auch eine Insel " zu Threads ein ausreichendes?

Muss ich dann das create3DHorLineChart() in einen seperaten Thread packen, oder irgendwie das ganze Frame? Da fängts schon an... :) Und dann muss ich doch irgendwie ein notify und wait einbauen, iregendwie so war das doch... ich mache mich mal schlauer.

Grüße und Danke
 
S

SlaterB

Gast
> Mich wundert dann nur warum er dann das Männchen in den einzelnen Frames aktualisiert,

ich denke es geht nicht, oder gehts nur zum Teil nicht?

> Muss ich dann das create3DHorLineChart() in einen seperaten Thread packen, oder irgendwie das ganze Frame?

die einfachste Variante ist immer, den Code des ActionListeners oder auch nur einzelne Aufrufe davon in einen Thread zu packen,
wenn irgendwo vorher

Befehl1;
Befehl2;
Befehl3;

stand,
dann nachher

Java:
Runnable r = new Runnable() {
  public void run() {

    Befehl1;
    Befehl2;
    Befehl3;

  }
};
new Thread(r).start();
nix einfacher als das
 

Booyeoo

Mitglied
oh mir ist grad aufgefallen, dass ich die screenshots falsch verlinkt hatte.
jedenfalls ist es so wie auf dem ersten screenshot angedeutet.
das männchen wird nach und nach gezeichnet...das chart nicht.

ich hab es jetzt so versucht, leider ohne Erfolg:
Java:
try {
            Thread.currentThread().wait();
        } catch (InterruptedException ex) {
            Logger.getLogger(HMM1Main.class.getName()).log(Level.SEVERE, null, ex);
        }

        Runnable r = new Runnable() {

            public void run() {
                if (stmResults != null) {
                    System.out.println("c3DHorLine");
                    create3DHorLineChart(stmResults, tabChart4);
                }
            }
        };
        Thread show = new Thread(r);
        show.start();
        while (show.isAlive()) {
            ;
        }
        show.notifyAll();

Fehlermeldung: java.lang.IllegalMonitorStateException
dem Gefällt hier das Thread.currentThread().wait(); nicht

wenn ich den Teil mit wait und notifyAll weglasse, dann habe ich leider nix bewirkt, aber er läuft immerhin genauso durch wie vorher. Was mache ich falsch? Wo muss ich ansetzen? Stoße ich hier damit an meine Grenzen, dass ich versuche soviel wie möglich in einer Klasse zu haben? Oder wäre das im Prinzip egal?

Grüße und Danke
 
S

SlaterB

Gast
aus Sicht der klassischen JFrame-Lehre muss das Warten natürlich weg,
es ist durch den Thread ja gerade das Ziel, den ActionListener so schnell wie möglich zu beenden, damit die GUI sich wieder um andere Dinge (Zeichnen) kümmern kann,

was im Moment aber sonst alles kaputt ist, lonht sich kaum noch weiter spekulieren,
ganz grob: wieso steht nur ein create3DHorLineChart() im Thread, umfasst die 'Aktion' nicht eine ganze Schleife?
wenn diese Schleife nach wie vor im Listener abläuft, mehrere Threads startet und vielleicht noch deren Ablauf abwartet, dann ist ja wie gesagt nix gewonnen,

es ist so einfach, eine Aktion ist eine Aktion von Anfang bis Ende, am besten nur ein Befehl:
macheAlles();
das ist kein JFrame, keine Klasse, usw. wie du gerne schreibst, sondern eine Aktion, ein Vorgang, die ganzen Befehle die zur Zeichnung führen, irgendwo fangen die ja an, bald kommen sie zur Schleife 'while (iterating over the frames till the end){' usw.

diese Aktion muss von in einem Thread ausgeführt werden. fertig

-----

das aber langsam nur noch weniger zuversichtlich vermutet, kann auch gut sein, dass du andere Probleme hast als ich ahne,
ein vollständiges kurzes Testprogramm würde helfen

-----

noch was anderes aktuell gesehen:
wenn GUI-Komponenten irgendwo entfernt und neu eingefügt werden, dann immer

output.validate();
output.repaint();

am besten aber gar nicht aufwendig neue JFreeChart, ChartPanel usw. erzeugen sondern nur die Daten im Model ändern,
der Rest kann gleich bleiben
 
Zuletzt bearbeitet von einem Moderator:

Booyeoo

Mitglied
Hi, hoffe ein schönes WE gehabt, ich verzweifel mal weiter...

"es ist durch den Thread ja gerade das Ziel, den ActionListener so schnell wie möglich zu beenden, damit die GUI sich wieder um andere Dinge (Zeichnen) kümmern kann,"

Wie meinst Du das? Ich meine ich drücke ja auf einen Knopf, danach sollen halt diese Dinge passieren.

Nochmal das Konstrukt etwas genauer:

[JAVA=2962]
private void butImportFilterActionPerformed(java.awt.event.ActionEvent evt) {
filterAMCandGetHMM();
}
[/code]

Hier rufe ich halt die Methode auf die alles machen soll...

Diese Methode ist ziemlich lang... dennoch einmal hier ein kleiner Einblick den ich danach die Kurzfassung. Am besten direkt die Kurzfassung anschauen, sonst muss ich mir wieder Rügen zu meinem schlechten Schreibstil anhören :)

[JAVA=3706]
public void filterAMCandGetHMM() {
if (stmAMC != null) {
// variablen für inkrementelle algorithmen initialisiern
ssActual = null;
ssSuccessor = null;
sdProb = 0.0;
sdFactor = 0.0;
siSame = 0;
siMaxCount = 0;
siPositionInArray = 0;
if (frViewer != null) {
frViewer.simulateBL6();
}

boolean biggerThanTreshold = false;
LinkedList<String> hmmChain = new LinkedList<String>(), // hmmchain mit nur gelenken
hmmChainT = new LinkedList<String>(), // hmmchain mit tendenz
hmmChainDT = new LinkedList<String>(); // hmmchain mit freiheitsgrad und tendenz
String dof = "";
TreeMap<Integer, double[]> tempTm = null;


Object[] ignoredItems = listIgnore.getSelectedValues();
HashSet<String> ignoreSet = new HashSet<String>();
for (Object key : ignoredItems) {
// System.out.println(key.toString());
ignoreSet.add(key.toString());
}

time("start");
// gehe durch alle frames durch
for (int frame = 0; frame < stmAMC.get("root").size(); frame++) {

TreeMap<String, TreeMap<Integer, double[]>> tempTmCandidate =
(TreeMap<String, TreeMap<Integer, double[]>>) stmFilteredAMC.clone();
// gehe durch alle gelenke durch
for (String key : stmAMC.keySet()) {
// wenn gelenk nicht ignoriert werden soll (wie z.B. lclavicle, rclavicle)
//System.out.println(key.toString()+ " " + frame);
if (!(ignoreSet.contains((String) key))) {
// falls gelenk nicht in der gefilterten map
if (!(stmFilteredAMC.containsKey(key))) {
// füge es mit dem ersten framewerten hinzu
tempTm = new TreeMap<Integer, double[]>();
double[] tmpA = stmAMC.get(key).get(0);
tempTm.put(0, tmpA);
stmFilteredAMC.put(key, tempTm);
} //ansonsten wenn es da ist und den grenzwert überschreitet, hinzufügen
else {
// for (Integer frametemp : stmFilteredAMC.get(key).keySet()){
// double[] tmpA=(double[]) stmFilteredAMC.get(key).get(frametemp).clone();
// //System.arraycopy(tmpA, 0,stmFilteredAMC.get(key).get(frametemp),0, stmFilteredAMC.get(key).get(frametemp).length);
// tempTm.put(frametemp, tmpA);
// }
// OPTI siehe clone unten (optimierung scheitert)
tempTm = new TreeMap<Integer, double[]>(stmFilteredAMC.get(key));

double[] tmpAMC = stmAMC.get(key).get(frame);
// nehme immer den letzten frame des entsprechenden gelenks
double[] tmpFiltered = stmFilteredAMC.get(key).get(stmFilteredAMC.get(key).lastKey());

// und vergleiche alle winkel und evtl. koordinaten innerhalb eines frames
for (int j = 0; j < tmpAMC.length; j++) {
// wenn die werte größer dem grenzwert sind
if (tmpAMC[j] > tmpFiltered[j] + getTreshold() || tmpAMC[j] < tmpFiltered[j] - getTreshold()) {
// dann setze den allgemeinen prüfer auf true und speichere am ende die neuen werte
//System.out.println("Key: " + key + " | Frame: " + frame + " | idx: " + j);
if (!(key.equals("root"))) {
if (j == 2) {
dof = "Z";
}
if (j == 1 && lhmASF.get(":bonedata" + key + "dof")[j].equals("ry")) {
dof = "Y";
}
if (j == 1 && lhmASF.get(":bonedata" + key + "dof")[j].equals("rz")) {
dof = "Z";
}
if (j == 0 && lhmASF.get(":bonedata" + key + "dof")[j].equals("rx")) {
dof = "X";
}
if (j == 0 && lhmASF.get(":bonedata" + key + "dof")[j].equals("ry")) {
dof = "Y";
}
if (j == 0 && lhmASF.get(":bonedata" + key + "dof")[j].equals("rz")) {
dof = "Z";
}
} else {
dof = lhmASF.get(":" + key + "order")[j + 1];
}

if (tmpAMC[j] > tmpFiltered[j] + getTreshold()) {
hmmChainT.add(key + "+");
hmmChainDT.add(key + dof + "+");
}
if (tmpAMC[j] < tmpFiltered[j] - getTreshold()) {
hmmChainT.add(key + "-");
hmmChainDT.add(key + dof + "-");
}
hmmChain.add(key);
biggerThanTreshold = true;
}
}
//OPTImized
// füge in die temporäre map hinzu falls diese noch gebraucht wird
// falls biggerThanTreshold feuert
tempTm.put(tempTm.lastKey() + 1, tmpAMC);
tempTmCandidate.put(key, tempTm);
}
}
}
if (biggerThanTreshold) {
// OPTImized
for (String key2 : tempTmCandidate.keySet()) {
// nehme die kandidtaen treemap und füge alle elemente des entsprechenden frames hinzu zur
// gefilterten treemap
TreeMap<Integer, double[]> temptm2 = tempTmCandidate.get(key2);
TreeMap<Integer, double[]> newtm2 = stmFilteredAMC.get(key2);
newtm2.put(newtm2.lastKey() + 1, temptm2.get(temptm2.lastKey()));
stmFilteredAMC.put(key2, newtm2);
String[] temp = new String[hmmChain.size()];
hmmChain.toArray(temp);
ssaDiscretes1 = temp;
lhmFilteredHMM.put(labAmc.getText() + "Normal", temp);


}
ssActual = ssaDiscretes1[ssaDiscretes1.length - 1];
//ssActualHMM = ssSuccessorHMM;
//ssSuccessorHMM = ssaDiscretes1[ssaDiscretes1.length - 1];
final Runnable r1 = new Runnable() {

public void run() {
simulateCompareButton();
}
};

final Runnable r2 = new Runnable() {

public void run() {
if (stmResults != null) {
System.out.println("c3DHorLine");
create3DHorLineChart(stmResults, tabChart4);
}
}
};
final Lock lock= new ReentrantLock();
Thread tr1 = new Thread(r1);
Thread tr2 = new Thread(r2);

tr1.start();
tr2.start();
try{
tr1.join();
lock.lock();
tr2.join();
lock.unlock();
}
catch ( InterruptedException e )
{
System.out.println( e.getMessage() );
}
biggerThanTreshold = false;
}
// paralleles abspielen der zugehörigkeits wahrscheinlichkeiten

//stmFilteredAMC = (TreeMap<String, TreeMap<Integer, double[]>>) tempTmCandidate.clone();
// der clone befehl dauert fast doppelt so lange wie die obere for schleife!!!
/*

2x clone:

------------ ************ Started at: 1272799954805
STMAMC:999 - STMFILTERED:452
------------ ************ Stopped after: 4218ms

1x clone 1x for
------------ ************ Started at: 1272799602655
STMAMC:999 - STMFILTERED:452
------------ ************ Stopped after: 2673ms

*/

// spiele nächstes frame ab im frViewer

if (frViewer == null) {
frViewer = new Viewer();
frViewer.setVisible(true);
//frViewer.setBounds(1281, 10, 800, 600);
frViewer.setBounds(400, 10, 800, 600);
frViewer.simulateBL3();
frProbabilty.setVisible(true);
//frProbabilty.setBounds(2082, 10, 500, 380);
frProbabilty.setBounds(600, 10, 500, 380);
} else {
frViewer.setVisible(true);
frProbabilty.setVisible(true);
frViewer.simulateBL3();
}
}
String[] temp = new String[hmmChain.size()];
hmmChain.toArray(temp);
ssaDiscretes1 = temp;
lhmFilteredHMM.put(labAmc.getText() + "Normal", temp);
temp = new String[hmmChainT.size()];
hmmChainT.toArray(temp);
lhmFilteredHMM.put(labAmc.getText() + "T", temp);
temp = new String[hmmChainDT.size()];
hmmChainDT.toArray(temp);
lhmFilteredHMM.put(labAmc.getText() + "DT", temp);
System.out.println("STMAMC:" + stmAMC.get("root").size() + " - STMFILTERED:" + stmFilteredAMC.get("root").size());
time("stopp");
}
}
[/code]

So jetzt nochmal die Kurzfassung, der wichtige Teil beginnt halt erst bei Zeile 3835 (habe nun schon einiges mit Threads rumexperimentiert bisher ohne Eroflg, allerdings stürzt es immerhin nicht ab.):

Java:
public void filterAMCandGetHMM() {
initialisiereStatischeVariablen();
macheBerechnungsZeug();
macheBerechnungsZeugFürSpezialfall();
if (biggerThanTreshold){
rechne();
// der wichtige Teil
final Runnable r1 = new Runnable() {


                        public void run() {
                       // hier werden die Berechnungen Durchgeführt auf dessen Daten dann der Graph basiert
                            simulateCompareButton();
                        }
                    };

                    final Runnable r2 = new Runnable() {

                        public void run() {
                            if (stmResults != null) {
                                System.out.println("c3DHorLine");
                                // der Graph der immer gemalt werden soll, wenn der Spezialfall eintritt...
                                create3DHorLineChart(stmResults, tabChart4); 
                            }
                        }
                    };
                    final Lock lock= new ReentrantLock();
                    Thread tr1 = new Thread(r1);
                    Thread tr2 = new Thread(r2);

                    tr1.start();
                    tr2.start();
                    try{
                    tr1.join();
                    lock.lock();
                    tr2.join();
                    lock.unlock();
                    }
                    catch ( InterruptedException e )
                    {
                      System.out.println( e.getMessage() );
                    }
                    biggerThanTreshold = false;
                }

}

Ich verstehe jetzt halt nicht wie ich den Action Listener vorher beenden soll oder wie Du das meinst? Ich werde ja immer abhängig von diesem Knopfdruck sein.
Der Graph wird auch in einem anderen Frame gemalt, als in dem wo ich auf den Knopf drücke!
Das output.validate und output.repaint habe ich auch bereits eingebaut.

Hilft irgendwie alles nix.
Mit dem lock und den thread.join habe ich halt zu erzwingen verscuht, dass der zweite Thread auf jeden Fall komplett fertig ist bevor der weiter macht... ohne Erfolg. Da fällt mir ein, dass ich Delay noch gar nicht versucht hab...wird aber auf jeden Fall unschön. Vielleicht ist sowas auch gar nicht möglich mit JFreeCharts. Muss das jetzt mal mit Delays und nem Testproggie testen.

Das mit dem "nur das dataset ändern" will ich jetzt noch einbauen, also dass der das inkrementell macht, spart natürlich einiges an Rechenleistung. Aber meiner Meinung müsste es jetzt auch schon gehen. Der Code von dem Graph steht ja schon im anderen Post.

Ich weiß danach nicht mehr weiter... :-(

Vielleicht siehst Du ja direkt woran es liegt. Danke für die bereits hilfreichen Tipps und Vorschläge.

Grüße
 
Zuletzt bearbeitet:
S

SlaterB

Gast
perfektes Code-Beispiel, danke, ich hab nun nicht alles von deinem Text gelesen und will auch nicht alle meine Argumente nochmal wiederholen,
es ist nur einfach völlig abwegig, in dieser komplizierten filterAMCandGetHMM() irgendwas anzustellen

ganz ganz ganz simpel simpel simpel

Java:
private void butImportFilterActionPerformed(java.awt.event.ActionEvent evt) {                                                

Runnable r = new Runnable() {
  public void run() {
       filterAMCandGetHMM();
  }
};
new Thread(r).start();
    }

schon fertig, an der einfachen Stelle mit nur EINEN Methodenaufruf ansetzen, den in einen neuen Thread und alles läuft separat,
stell dir die GUI wie einen Chef der Firma vor, solange er/sie irgendeine Arbeit machen muss, bleibt alles andere (golfen, repaint) liegen,
Lösung: die ganze Aktion einfach an jemand anderen abgeben (Unterarbeiter, Thread), schon hat der Chef wieder Zeit,
 

Booyeoo

Mitglied
Slater, Du bist ein Gott. Das Beispiel mit dem Chef war der sprinkende Funke! Es war im Prinzip echt so einfach wie Du gesagt hast, nur musste ich noch synchronized einbauen.

Danach hat er dann immerhin schonmal gezeichnet, allerdings blieb dann der Graph leer. Danach habe ich synchronized auf stmResults eingebaut mit notify und wait, das ging irgendwie in die Hose, dann habe ich einfach notify und wait entfernt und synchronized gelassen und ES LÄUFT............. JUHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU! Ich kann es immernoch nicht fassen....jetzt muss ich nur noch die fehler in meinem berechnungsalgorithmus finden :)
Ich danke Dir so sehr!!!
 

Ähnliche Java Themen

Neue Themen


Oben