Zugriff zwischen Klassen

Garog0201

Mitglied
Ursprung war dieses Thema: http://www.java-forum.org/java-basics-anfaenger-themen/163755-konstantes-update.html
Ich möchte mein Problem noch etwas anders darstellen, es war wohl etwas spät, und ich verstehe meine obige Beschreibung selbst nicht mehr :lol:

Class Main
Java:
public class Main{

    static Integer wert;

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setSize(1024, 600);
        Test system = new Test();
        frame.setContentPane(system);
        new Thread(system).start();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);

        JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 15);
        slider.setBounds(450, 450, 200, 200);
        slider.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                wert =((JSlider) e.getSource()).getValue();
            }
        });
        frame.add(slider);
    }
}

Und die Class Test
Java:
public class Test extends JComponent implements Runnable {

    @Override
    public void run() {
        while (true) {

            System.out.println(Variable "wert" von Main);
        }
    }
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.black);
        g.drawString(wertvonmain.toString(),100,100);
    }
}

Meine Frage nun dazu.
Wie komme ich innerhalb der while-Schleife in der Klasse Test an die Variable "wert" der Klasse Main ?

Mein Versuch dazu bisher war die Class Main sebst an Test zu übergeben und dann mit getter zu arbeiten.
Lege ich in der Main einen getter für wert an und versuche anstelle von
Java:
        Test system = new Test();
ein
Java:
        Test system = new Test(this);
(die Class test natürlich entsprechend angepasst)
Dann funktioniert dies nicht mit der Meldung "Main.this cannot be referenced from a static context".
Das this ist dort bestimmt falsch, aber ich weiß nicht was da hingehört...
 
Zuletzt bearbeitet:
Im Prinzip ist es niemals richtig main an eine andere Klasse weiter zu geben.

Du solltest auch nicht dein Frame in der Klasse main erzeugen sondern Dir eine eigene Klasse z.B. MainGUI erstellen welche dann mit invokeLater gestartet wird. Nur so bekommst du später keine Probleme mit Nebenläufigkeiten.

Diese Klasse MainGUI hat dann halt eine Methode show() und die macht alles weitere. Die statische main() Klasse macht also nichts anderers als eine Instanz der Klasse MainGUI zu erstellen und diese mit show() zu "Starten". Alles weitere läuft dann in der nicht statischen Klasse MainGUI ab.

Dort könntest du dann theoretisch auch genau dieses Konstrukt machen das Du vorhattest mit dem MainGUI übergeben etc. Das ist aber auch in 99% der Fälle ein Codesmell. Du solltest nur in absoluten Notfällen Deine aufrufende Klasse selber übergeben. Du erzeugst sonst einfach eine direkte Abhängigkeit zwischen den beiden Klassen, denn deine Testklasse kann dann ohne die MainGUI Klasse nicht leben. Das ist aber eigentlich eine Grundgedanke des OOP, dass eine Klasse möglichst unabhängig von allen anderen Klassen leben können sollte, damit man sie jederzeit auch an anderen Stellen weider verwenden kann. Deshalb solltest Du überlegen ob Du nicht besser der TestMethode nur den einen Wert übergibst den du brauchst.

Gruß

Claus
 
Vielen Dank erstmal für das grundsätzliche Aufklären, dies werde ich auch als erstes so umsetzen und anpassen. Da bin ich noch zu frisch in Java und in Videotrainings und Büchern finden sich XY verschieden "Anfänge".

Das ich der Methode Test einen Wert übergebe habe ich auch schon probiert, allerdings habe ich dann das Problem, dass ich nur den einen Wert bei der Übergabe in der Methode test zur Verfügung habe.
Wobei mir da gerade in den Sinn kommt, ich könnte ja auch den JSlider übergeben und diesen dann in Test abrufen... Ist das eine brauchbare Idee ? das werde ich doch gleich mal testen.. nach dem umbauen der Klassenaufrufe 🙂
 
Ich weiß ja leider nicht was deine Test Methode nachher machen soll aber für mich klingt das gerade so als würde es eine gute Idee sein deine TestMethode einfach von JSlider erben zu lassen und dann statt einem JSlider deine MeinSlider Klasse zu benutzen die dann entsprechend die Aktionen durchführt.

Gruß

Claus
 
In der Test Klasse soll mir eine Kurve gezeichnet werden.

http://www.java-forum.org/java-basics-anfaenger-themen/163719-probleme-repaint.html#post1039132
Bei der Frage eines Users habe ich ihm ein Beispiel gesendet, das trifft in etwa auf meinen Code zu den ich aktuell habe.
Diesen versuche ich nun etwas "sauberer" zu gestellten und nicht alles in der Main Klasse unterzubringen und mit mehr Objekten zu arbeiten


Edit
Ich habe den Code nun mal etwas umgebaut (das mit der Main Methode noch nicht) und übergebe nun den JSlider.
So hatte ich mir das in etwa auch vorgestellt.

Java:
package com.company;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import static javax.swing.JFrame.EXIT_ON_CLOSE;

public class Main{

    static JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 15);

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setSize(1024, 600);
        Test system = new Test(slider);
        frame.setContentPane(system);
        new Thread(system).start();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);


        slider.setBounds(450, 450, 200, 200);
        frame.add(slider);
    }
}

Java:
package com.company;

import javax.swing.*;
import java.awt.*;

public class Test extends JComponent implements Runnable {
    JSlider jslider;
    Integer[] aktuell;

    public Test(JSlider jslider) {
        this.jslider = jslider;
        aktuell = new Integer[900];
        for (int i = 0; i < 900; i++) {
            aktuell[i] = 0;
        }
    }

    @Override
    public void run() {
        while (true) {
            long t = System.currentTimeMillis();

            for (int i = 0; i < 900; i++) {
                if (i > 0)
                    aktuell[i - 1] = aktuell[i];
            }
            aktuell[899] = 360 - jslider.getValue();

            this.validate();
            this.repaint();

            long d = t - System.currentTimeMillis();
            int wait = 10 - (int) d;
            if (wait > 0)
                try {
                    Thread.sleep(wait);
                } catch (InterruptedException e) {
                }
        }
    }
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.white);
        g.fillRect(46, 40, 904, 360);
        g.setColor(Color.black);
        for (int i = 1; i < 900; i++) {
            g.drawLine(i + 46, aktuell[i - 1] + 40, i + 46, aktuell[i] + 40);
        }
    }
}
 
Zuletzt bearbeitet:
Warum bekomme die Klasse Test dann nicht eine Methode drawGraph(double sliderValues) die dann den neuen Grafen in Abhängigkeit des neuen Sliderwertes zeichnet?

Gruß

Claus
 
Warum bekomme die Klasse Test dann nicht eine Methode drawGraph(double sliderValues) die dann den neuen Grafen in Abhängigkeit des neuen Sliderwertes zeichnet?

Ich habe meinen vorherigen Beitrag noch editiert. Das sollte in etwa dem entsprechen, was du meinst oder ?

Grundgedanke dahinter ist halt das ich in einer eigenen Klasse alle "statischen" Komponenten wie Buttons, Beschriftungen usw. einmalig anlege und dann später nur den Teil zum Kurvenzeichnen in einem eigenen Thread laufen lasse.
Der obige Code ist lauffähig, vielleicht einfach kurz durch den Compiler schieben, dann siehst du was ich in etwa meine.
 
Zuletzt bearbeitet:
Es ist auch extrem unfein den Grafen in einem Zeitintervall zu zeichnen obwohl sich eventuell stundenlang der Slider nicht ändert.

Du solltest Dein ganze Konzept über den Haufen werfen und noch einmal darüber nachdenek wie so etwas ausehen muss.

Du hast eine Objekt Slider und ein Objekt Panel. Wenn sich der Sliderwert ändert, dann soll in dem Panel der dazugehörige Graf gezeichnet werden.

Das ist doch ein ganz einfache Konstrukt. Du erzegust einen EventListener zum Slider und wenn der eine Werteänderung ansagt, dann schickst du deinem Panel die Nachricht "Zeichne den Graf neu mit dem Wert X".

In dem Fall würde ich das JFrame (also das MainGUI) als Nachrichtenzentrale verwenden. Diese kennt das Panel und den Slider. Das MainGUI hat also eine Methode sliderValueChanged(int newValue) die vom Slider aufgerufen wird. In dieser Methode wird dann der drawGraph(int newValue) vom Panel aufgerufen.

Gruß

Claus
 
Der Slider ist für mich ja nur zum Testen, "später" wird die Kurve ein Analogsignal darstellen und dies Zeitabhängig. Wie ein EKG z.B. das läuft ja auch immer, nicht nur wenn das Herz gerade schlägt 🙂

Das aktuelle Problem ist aber ein Grundproblem was ich oft habe, Werte zwischen einer Klasse und einer anderen Klasse zu tauschen, gerade wenn Klasse A Klasse B aufruft und ich in B etwas von A möchte.
Daher suche ich dort nach guten Möglichkeiten.
Der JSlider z.b. wird bald durch eine andere Klasse "ReadAnalog" ersetzt. Diese ist nur noch nicht fertig und ich bastel erstmal an der Kurve selbst 🙂

EDIT:
Das ermitteln des Analogwert muss parallel zum Kurvenzeichnen laufen, also in zwei Threads. Da ich dort einen digitalen Eingang einlese und mit einem A/D Wandler arbeite.
Gleichzeitig muss das aufzeichnen der Kurve stoppbar sein und per Button die Werte in eine Datei schieben.

Das ganze habe ich auch schon und funktioniert alles, nur finde ich das "wie" selbst unschön und versuche nun etwas aufzuräumen 🙂
 
Zuletzt bearbeitet:
Siehst du und gerade deshalb ist es totaler Mist den Slider zu übergeben. Denn dann must du später deine Test Klasse wieder ändern denn du hast ja keinen Slider mehr. Warum also nicht gleich die Klasse so designen das es ihr egal ist woher sie die Daten bekommt?

Und auch ein eigengehendes Analog signal wird die Daten in einem bestimmten Intervall liefern das du mit deinem Grafen synchronisieren solltest. Einfach zu sagen mein Graf wird 10x pro Sekunde gezeichnet ist und bleibt Schrott! Wenn die Analogwerte 50x pro Sekunde reinkommen zaichnest du nur jeden 5ten Graf. Das ist totaler Mist. Du zeichnest nicht alle Daten aber du liest alle Daten ein, also 5x so viele wie du eigentlich verarbeitest. Das verschwendet Resourcen. Oder anders herum du bekommst nur 5x pro Sekunde neue Daten, dann zeichnest du die gleiche Kurve 2x. Auch das verschwndet unnötig Resourcen.

Ich bleibe also dabei: Wenn zeitabhängig, dann muss die Datenquelle das Tempo angeben NIEMALS die Datenausgabe.

Gruß

Claus
 
Zuletzt bearbeitet:
nagut, das kann ich dann ja recht einfach bestimmen, da ich ja auch das Intervall der analogen Abfragen bestimmen.

Um aber bei deinem Vorschlag zu bleiben, wenn ich abhängig von der Änderung des Sliders die Kurve aktuallisieren, wie realisiere ich dann einen EKG mäßigen Vorlauf ? Gar nicht, den die Kurve muss ja trotzdem weiterzeichnen, egal ob der Slider oder der Analogwert einen anderen Wert angibt wie der davor.

Um aber deine Idee weiterzuführen. Wenn ich die Klasse für den Trend nun so umänder das ich einen Wert übergebe, wie sollte ich den Wert dann aktuallisieren ?
Ich lege in der Klasse Main das Objekt "Kurve" an und übergebe einen Int wert, den vom vom Slider.
In der Klasse Kurve ist nun eine while(true) Schleife, wie komme ich da nun an die aktuellen Daten vom Slider ?
Oder meinst du das ich eine set Methode in der Klasse für die Kurve erstellen sollte, die ich aufrufe beim Ändern des Sliders und den Wert dann für die Kurve übernehme, die Kurve wie bisher aber kontinuierlich weitergezeichnet wird ?
 
Also nur damit ich das verstanden habe....

Du willst später quasi eine Hüllkurve zeichnen von analogen Daten die kontinuierlich reinkommen?
Um das zu testen willst du im Moment immer eine Gerade zeichnen die ständig weiterläuft und immer den Wert des Sliders anzeigt?
 
Hüllkurve ist nicht ganz richtig.
Kompiliere am besten einfach mal kurz das aus dem #5 Post http://www.java-forum.org/java-basi...zwischen-klassen-post1039161.html#post1039161

Da sieht man es dann ganz genau, nur das der Wert später nicht der Istwert des Sliders ist sondern ein Analogsignal.
Aber das mit dem Slider oder dem Analogsignal ist ja relativ egal, unterm Strich habe ich einen int wert den ich in der While Schleife benötige von einem Object das in einer anderen Klasse ist und deren Value sich parallel immer ändern kann.
Also übergebe ich jetzt per getter den JSlider an die Test Klasse (später die Analogklasse) und lese dann in der While den Wert über einen getter aus... so in etwa ist ja auch der Code aus Post #5, nur noch nicht konsequent mit getter gemacht
 
Zuletzt bearbeitet:
Du mit Deiner While Schleife.....

Was du brauchst ist ein vernünftiges Datenmodel. Das bedeutet du brauchst eine Klasse welche die Daten vorhält, die das Panel dann zeichnen soll.
Dein Panel fragt dann in dieser Klasse die Daten ab die es zeichnen soll. Also würdest du in deiner While Schleife nichts anderes machen als von Bildschirmpixel 0 bis Bildschirmpixel X gehen (Keine Ahnung wie breit dein Panel ist) und für jeden Wert beim Datenmodel anfragen getYValueForXPosition() und das dann zeichnen.
Das ist alles was dein Panel macht. Mehr nicht!
Weiterhin braucht dein Panel noch eine Methode refreshGraph() oder so die immer dann aufgerufen wird wenn sich das Datenmodel geändert hat.

Nun zum Datenmodell:

Nehmen wir mal an du hast 1000 Pixel in der Breite, dann hast du da 1000 Daten drin. Meinetwegen ein einfaches Array. Schlauerweise würdest du dieses als Ringcache verwalten. Das bedeutet du füllst erstmal alles mit 0 und dann wenn der erste Wert kommt, dann steckst du ihn in in den ersten Arrayeintrag und markierst dein ArrayStart bei 0 und den Füllstand mit 1. Dann schickst du ein refreshGraph an dein Panel. Die Kurve wird nun gezeichnet mit natürlich erstmal nur einem Wert.
Jetzt füllst du solange das Array auf, bis es voll ist. Würdest du das jetzt zeichnen hättest du deine erste komplett bildschirmfüllende Kurve.
Kommt jetzt der nächste Wert also der 1001te, dann steckst du diesen wieder in den ersten ArrayEintrag und setzt den ArrayStart auf 1. Die getYValueForXPosition() Methode weis jetzt, dass wenn die Anfage nach Pixel x=0 kommt, er diese nicht in Arrayeitnrag 0 sondern in arrayeintrag 1 findet und die für x Position 999 nicht in 999 sondern in 0. So funktioniert ein Ringcache.

Wenn du nun noch keine kontinuierlich ankomenden Daten hast und das mit dem Slider simulieren möchtest, dann must du das Befüllen dieses Arrays halt simulieren. Sprich du machst eine Methode die von einem Timer aufgerufen alle x/tel sekunden den Slider wert nimmt und in das Datenmodell schreibt. Das schreiben geht natürlich indem du dem Datenmodel eine Methode addValue(int Wert) spendierst, welche nach obigem Schema den Wert in das Array schreibt und den Refresh im Panel aufruft.

Gruß

Claus
 
Na was mache ich den anderes mit meiner while Schleife wie das was du jetzt vorgeschlagen hast ?
Du hast aber das Problem noch nicht ganz verstanden. Du möchtest auch immer bei einem neuen Datenwert erst ein refresh machen, das soll aber nicht der Fall sein, sondern die Kurve soll immer weiter laufen, also kontinuierlich die Kurve erweitern mit dem aktuellen Wert, egal ob sich der geändert hat oder nicht, dann sind halt 100 Punkte auf der selben Y Koordinate. Ich kann nur sagen schau dir den Code mal kompiliert an, dann siehst du das es etwas anders funktionieren soll.
Die Daten werden als FIFO in ein Array geschoben, entsprechend sieht, und soll, die Kurve dann auch aus.
Ich zeichne die Kurve nicht von links nach rechts mit geänderten Werten, sondern von rechts nach links mit -t Sekunden.

Aber zu deinem ersten Absatz
Das bedeutet du brauchst eine Klasse welche die Daten vorhält, die das Panel dann zeichnen soll.
Dein Panel fragt dann in dieser Klasse die Daten ab die es zeichnen soll.

Damit hast du mein Problem umschrieben 😉 Ich brauche in Klasse B etwas aus Klasse A, beide werden in Klasse C aufgerufen.
Ich muss also alle z.B. 10 ms in Klasse B den Wert eines Objekts aus Klasse A abfragen. Das geht nach dem was ich bisher gesehen habe nur, wenn ich das betreffende Objekt aus A an B übergebe.
Wie sollte es sonst gehen ?
Um bei dem Beispiel zu bleiben, A und B werden in C aufgerufen.
Slider in A, Graph in B, C Mainklasse
Der Slider in A verändert seinen Wert, wie kommt das nun nach B oder von mir aus auch nach C und von C nach B.
Und zwar ungefragt von B also getriggert vom Slider A aus.
Ich habe schon das Problem den sich verändernden Wert des Sliders von A immer aktuell nach C zu bekommen.
Schließlich müsste ich ja durch das ändern von A irgend ein "getSliderwert" in C anstoßen.
 
Ich habe mich mal etwas mit dem Stichwort "Datenmodel" befasst.

Um das mal einfach zu schreiben was ich nun gemacht habe, in Anlehnung an deine Beiträge.
Main Klasse erstellt ein Objekt "Datenmodel" dieses hat nun alle Daten die ich brauch und per getter/setter sind diese erreichbar.
Dieses eine Objekt reiche ich nun an alle weiteren Objekte mit durch.
So habe ich an allen Stellen die Daten zur Verfügung und kann auf sie zugreifen, lesend und schreibend.
Die Antwort auf meine Frage wie ich den Zugriff zwischen Klassen realisieren wäre so gesehen also
"Mit einer weiteren Klasse in der die Daten abgebildet sind, das Objekt der Klasse wird dann überall mit bzw. durchgereicht."
Die while Schleife und den eigenen Thread habe ich inzwischen durch einen Timer/TimerTask ersetzt der mir nun konstant ein repaint() der Kurve macht.
 

Zurück
Oben