Fortlaufendes Diagramm zeichnen
-
Moin moin,
ich wollte in einem WinAPI-Fenster ein Diagramm zeichnen z.B. für die Prozessorauslastung. Nun hab ich keine wirkliche Ahnung wie ich das machen soll.. Hab schon drüber nachgedacht das mit setpixel oder so zu machen, allerdings müsste ich dann ja nicht nur den neuen Wert sondern auch die vorhergegangenen neu zeichnen, da das ganze ja fortlaufend sein soll!
Hat da vielleicht jemand eine besser Idee für mich?
MfG Inv151673
-
Ich habe jetzt nicht den kompletten Code für dich, aber wie folgt müsste es Funktionieren.
Du deklarierst ein Array aus IntegerZahlen, das Platz für meinetwegen 50 Felder hat. Dann liest du einmal die Sekunde oder einmal alle 5 Sekunden, ist dir überlassen, die Leistung aus und schreibst diesen Wert nun in das erste Feld. Wenn dort schon was ist, wird dieses dann erst in das zweite geschrieben, ist dort schon etwas, wird das was dort ist erst in das 3. Feld geschrieben und so weiter. Dann hast du ein Array mit den letzten Messungen. Nun lässt du einen Diagrammprototypen zeichnen und setzt den Linienstartpunkt auf die untere linke Ecke. nun rechnest du die Leistungswerte aus dem Array in einer Schleife um und zwar in die genauen Koordinaten des Diagrammes. Im gleichen Schritt lässt du sie schon Zeichnen und sie dienen automatisch als Anfangspunkt für die nächste Linie.
Diese Taktik wird zu Anfang etwas schäbig aussehen, aber du kannst ja feinarbeit selbst übernehmen.
-
Hm.. Also das mit den Messungen in nem Array speichern versteh ich das hab ich mir selber so ähnlich gedacht. Allerdings weiß ich nicht wie ich das machen soll, wenn das Array nun voll ist, ich also bei der 51en Messung angekommen bin. Einfach x[0] = x[1] && x[1] = x[2] usw oder wie?
Und was meinst du mit nem Diagrammprotoypen? Hab mich nie sonderlich viel mit der Winapi auseinander gesetzt mehr als nen Fenster erstellen und nen par Pixel malen kann ich nun nicht ^^
-
Z.B. ein Bitmap, das doppelt so breit ist, wie es angezeigt werden soll. Rechts wird einfach immer weiter gezeichnet (Aufpassen beim Übergang w->0) und der Auschnitt weiter nach rechts verschoben.
Der Nachteil ist natürlich, dass an bestimmten Positionen 2 Blits notwendig sind (einmal links bis zur Bitmapbreite und rechts dann von 0 bis zum Auschnittsrand).Wenn das ganze vernünftig unterteilt ist, sollte es aber kein allzu großes Problem darstellen. Ist dann eben einfach wie eine Panoramaanzeige eines Bitmaps.
-
PS: An einer Stelle (rechter Auschnittsbereich gleich Bitmapbreite) ist natürlich noch ein Blit fällig-> der rechte Bereich muss in den linken kopiert werden.
Ich hatte das eben noch vergessen (ist mir grad beim Einkaufen eingefallen).
-
Nein also grundsätzlich kannst du keine Zuweisungen mit && verknüpfen. Also ich versuche dir das mal codalisch darzustellen:
int iLeistung; //Das ist der letzte gemessene Wert int iDiaLeistung[50]; //Das Array mit allen Werten /*wir nehmen an hier kommt so allgemeiner Quelltext und wir befinden uns in einer Funktion, die gerade die aktuelle Leistung geliefert bekommen hat.*/ for ( int i = 49; i > 0 ; i-- ) //wir haben einen Zähler, der von 49 runterzählt. { iDiaLeistung[i] = iDiaLeistung[i-1]; //Alle Felder aus dem Array werden in das dannach Folgende geschrieben. //Da wir das rückwärts machen, gibt es keine Fehler } iDiaLeistung[0] = iLeistung;
Das ist der Ausschnitt, den man bräuchte, um ein Array rückwärts zu verschieben. Das 50. Feld wird als erstes mit dem 49. beschrieben. das geht rückwärts, bis das 2. mit dem 1. beschrieben wird. Letztendlich wird dem 1. die letzte Zeit zugewiesen.
Dazu müsste nun nurnoch der Zeichenbefehl kommen.
-
Ja ich weiß das das mit dem && nicht geht ^^ War jetzt nur so auf die Schnelle.. Ja ist ja vom Prinzip her so wie ich mir das auch gedacht hatte.
Aber mir gings ja viel mehr um das Zeichnen des Diagramms ^^
-
Nun also zum Zeichnen, kann man so viel erzählen, dass es darüber diverse Bücher gibt. Ich habe hier mal "Windows Programmierung" von Charles Petzold zu liegen und kann auch wie gesagt nur zu einem weiterführenden Buch empfehlen.
Aber ich möchte dir trotzdem soweit ich auf die schnelle kann weiterhelfen. also wie folgt. Ich denke das ganze läuft ja durch den Timer, den einem Windows zur verfügung stellt. also sind wir in der Timerfunktion, in der schon der neue Leistungswert eingetragen wurde. zunächst müsste das alte Diagramm vom letzten Zeichenvorgang verschwinden. Wir gehen mal davon aus, dass du im Fenster eine Breite von 500 und eine Höhe von 300 hast und über das komplette Fenster zeichnen willst.
//zunächst den Device-Context andlegen. HDC hdc; hdc = GetDC ( hWnd ); //hWnd ist das Handle zu deinem Fenster
nun zeichnen wir ein großes Viereck über das gesamte Fenster. Müsste also wie folgt funktionieren:
Rectangle ( hdc , 0 , 0 , 500 , 300 ); //devicecontext, links, oben, rechts, unten
jetzt Zeichnen wir bei 100 , 200 , 300 , 400 jeweils noch striche, die später für übersichtlichkeit sorgen:
//weil es immer das gleiche ist, und nur auf verschiedenen x-Werten gezeichnet //werden muss, kommt alles in eine Schleife. for ( int i = 1 ; i < 5 ; i++ ) { MoveToEx ( hdc , 0 , i * 100 , NULL ); LineTo ( hdc , 500 , i * 100 ); }
damit haben wir freies feld zum Zeichnen der letzten primitiven ( also die Kurve des Diagramms) . Wir müssen ganz vorne und ganz unten ansetzen zu Zeichnen. Ich gehe im übrigen davon aus, dass der Wert der jeweils im Array steht, die Prozenzahl der Auslastung deines Prozessors ist, also dort steht vielleicht so was drin wie:
iDiaLeistung[0] = 0;
iDiaLeistung[1] = 5;
iDiaLeistung[10] = 54;
so in der richtung. natürlich kommt das nicht in den Code.Zunächst legen wir uns einen eigenen Stift an, und zwar in Blau, damit man später den Graphen von den anderen Linien unterscheiden kann.
static HPEN hPen; hPen = CreatePen ( PS_SOLID , 1 , RGB ( 0 , 0 , 255 ) ); SelectObject ( hdc , hPen );
so damit wären wir eigentlich schon bei der Auswertung der Daten. Zuerst setzen wir den Stift auf die linke untere Ecke mit MoveToEx und machen dann LineTo mit allen Werten aus dem Array schon nacheinander. Da unsere Werte aus dem Array maximal 100 sind und das Feld 500 hoch ist, multiplizieren wir den Wert vorher noch mit 5 und haben so maximale Auslastung des Zeichenbereichs.
MoveToEx ( hdc , 0 , 300 , NULL ); //da wir uns wieder in einer Routine befinden, die das gleiche mit allen Werten //tut, nämlich zu ihnen einen Strich zu ziehen, können wir wieder schleifisieren for ( int j = 49 ; j > -1 ; j-- ) { LineTo ( hdc , (50 - j)*6 , iDiaLeistung[j] * 5 ); }
und da hätten wir es eigentlich schon. nun noch schön den Stift und das DeviceContext wieder freigeben und der Rechner darf in eine neue Runde starten
DeleteObject ( SelectObject ( hdc , GetStockObject ( BLACK_PEN ) ) ); /* das muss ich vielleicht erklären. der Stift, den wir vorhin angelegt haben, wurde in der Variable hpen gespeichert (oder sein Handle) da wir aber für den nächsten "render"-Durchlauf ja wieder den Schwarzen stift wollen, löschen wir den alten und setzen den Standartstift wieder ein. normalerweise würde man DeleteObject ( hpen ); SelectObject ( hdc , GetStockObject ( BLACK_PEN ) ); machen, jedoch kann man das auch zusammenfassen, indem man den Rückgabewert von SelectObject, nämlich den Handle auf das aktuell gesetzte Object abwartet und dieses Löscht. so schlagen wir zwei Fliegen mit einer Klappe*/ ReleaseDC ( hWnd , hdc );
so nun ist deine Funktion fertig. vielleicht willst du sie dir kopieren und direkt so nutzen, also hier zusammengefasst:
int iLeistung; //Das ist der letzte gemessene Wert int iDiaLeistung[50]; //Das Array mit allen Werten /*wir nehmen an hier kommt so allgemeiner Quelltext und wir befinden uns in einer Funktion, die gerade die aktuelle Leistung geliefert bekommen hat.*/ for ( int i = 49; i > 0 ; i-- ) //wir haben einen Zähler, der von 49 runterzählt. { iDiaLeistung[i] = iDiaLeistung[i-1]; //Alle Felder aus dem Array werden in das dannach Folgende geschrieben. //Da wir das rückwärts machen, gibt es keine Fehler } iDiaLeistung[0] = iLeistung; //zunächst den Device-Context andlegen. HDC hdc; hdc = GetDC ( hWnd ); //hWnd ist das Handle zu deinem Fenster Rectangle ( hdc , 0 , 0 , 500 , 300 ); //devicecontext, links, oben, rechts, unten //weil es immer das gleiche ist, und nur auf verschiedenen x-Werten gezeichnet //werden muss, kommt alles in eine Schleife. for ( int i = 1 ; i < 5 ; i++ ) { MoveToEx ( hdc , 0 , i * 100 , NULL ); LineTo ( hdc , 500 , i * 100 ); } static HPEN hPen; hPen = CreatePen ( PS_SOLID , 1 , RGB ( 0 , 0 , 255 ) ); SelectObject ( hdc , hPen ); MoveToEx ( hdc , 0 , 300 , NULL ); //da wir uns wieder in einer Routine befinden, die das gleiche mit allen Werten //tut, nämlich zu ihnen einen Strich zu ziehen, können wir wieder schleifisieren for ( int j = 49 ; j > -1 ; j-- ) { LineTo ( hdc , (50 - j)*6 , iDiaLeistung[j] * 5 ); } DeleteObject ( SelectObject ( hdc , GetStockObject ( BLACK_PEN ) ) ); ReleaseDC ( hWnd , hdc );
so, wenn etwas nicht läuft oder hie und da schreibfehler sind, möchte ich mich entschuldigen, es ist kurz vor dreiviertel 12 und ich bin müde. Ich wollte übrigens gerne noch wissen wie alt du bist, weil ich so verwundert bin, dass du WildChild heisst.
-
Vielen vielen Dank das ist echt super erklärt!
Der Name ist schon recht alt, den benutz ich eigentlich mittlerweile nicht mehr, aber ich bin nun fast 20.
-
mensch, da bin ich jetzt total stolz, ich bin gerade 16 und studiere das Fach der Informatik schon seit 6 Jahren (also zu Hause) ich will dann in zwei Jahren an einer Uni studieren und mal ein richtig guter Programmierer werden. Ich finde super, dass einem solche Communities die möglichkeit geben, sich noch in allen bereichen zu testen
-
Haha
Ja so ungefähr hatte ich mir das auch gedacht.
Vielleicht magst du den Code morgen, wenn du zeit dazu findest einmal als ganzes hier reinstellen? Wie gesagt ich bin mit der Winapi nicht so vertraut. Ich würde das alles, vielleicht ein bisschen verändert in ne Funktion klatschen und die im Zeichenbereich im WM_PAINT ausführen, aber keine Ahnung wäre vielleicht ganz hilfreich das mal zu sehen wie es sein sollte