simples Balkendiagramm

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

simples Balkendiagramm

Beitrag von hjbremer »

ein Balkendiagramm um Daten grafisch darzustellen, hatten wir sicher schon oft, aber ich finde noch eins kann nicht schaden.

Es ist sicher nicht perfekt, aber als Grundlage für Anfänger sicher brauchbar. Mir reicht es.

im Beispiel sind es 31 Balken = 31 Tage. Mehr Balken sind möglich, aber bei
zu vielen Balken sieht es nicht mehr gut aus.

In der Höhe skaliert es sich automatisch, abhängig vom Maxwert im Datenfeld. Die Werte sollten 6 Stellen nicht überschreiten

Weitere Erklärungen im Code

Es läuft bei mir unter Windows XP 32 Bit

Code: Alles auswählen

Procedure Balkendiagramm2(info$, Array dinfo$(1), Array daten(1), parent)
  
;sollte ein Fix-Font sein !!!
fontnr = LoadFont(#PB_Any, "Courier New", 8)
  
winbr = 600
winhh = 400
winnr = OpenWindow(#PB_Any,0,0,winbr,winhh,info$,#PB_Window_SystemMenu|1,WindowID(parent))
  
  ;max des Datenfeldes finden
  size = ArraySize(daten())
  For j = 0 To size
      If daten(j) > maxwert: maxwert = daten(j): EndIf
  Next    

  ;Mod wert x
  Select maxwert
    Case 0 To 10: x = 1
    Case 11 To 100: x = 10
    Case 101 To 1000: x = 10
    Case 1001 To 10000: x = 100
    Case 10001 To 100000: x = 100
    Default: x = 1000
  EndSelect 
  
  ;max des Datenfeldes bis nächsten glatten Wert erhöhen 
  While maxwert % x  ;für senkrechte Y Skala 
   maxwert + 1
  Wend

  ; -----------------------------------------------------------------
  
  ;nur für Höhe + Breite eines Zeichens, sollte Fix-Font sein
  StartDrawing(WindowOutput(winnr))
    DrawingFont(FontID(fontnr))
    chrbr = TextWidth("X")
    chrhh = TextHeight("X")
  StopDrawing()
  
  balken = ArraySize(daten())
  rows = 10
  baseX = chrbr * 7           ;senkrechte Linie vom linken Fensterrand
  baseY = winhh - chrhh * 3   ;waagerechte Linie von unten
  
  ;Balkenabstand in Pixel, -1 kann auch wegfallen  
  pixelX = ((winbr - baseX) / balken) - 1  
  If pixelX < 1: pixelX = 1: EndIf    ;null verhindern
  
  scaleY = maxwert / rows             ;Abstand der Zahlen pro Reihe
  pixelY = baseY / rows               ;Abstand der Reihen in Pixel  
  If scaleY < 1: scaleY = 1: EndIf    ;null verhindern
  If pixelY < 1: pixelY = 10: EndIf
   
  nr = CreateImage(#PB_Any, winbr, winhh, 16)  ;16 Bit Farbtiefe
  dc = StartDrawing(ImageOutput(nr))
          
      Box(0, 0, winbr, winhh, #White)
      Line(baseX, 0, 0, baseY, #Blue)             ;senkrechteLinie
      Line(baseX, baseY, winbr - baseX, 0, #Blue) ;waagerechte Linie
      
      ; gestrichelte Linien + Text links der senkrechten Linie
      DrawingFont(FontID(fontnr))  
      pen = CreatePen_(#PS_DOT,0,#Gray)  ;für gestrichelte Linie	
      SelectObject_(dc,pen)
      
      r.rect
      y = baseY                          ;Basis unten für 0
      For j = 0 To rows
          If j
             MoveToEx_(dc, baseX, y, 0)
             LineTo_(dc, winbr, y)      ;gestrichelte Linie
          EndIf
          txt$ = Str(scaleY * j)        ;Text links z.B.0,5,10,15...50 
          r\top = y - chrhh / 3
          r\left = 0             
          r\right = baseX - 2
          r\bottom = r\top + chrhh             
          SetTextAlign_(dc, #TA_TOP)
          DrawText_(dc, txt$, -1, r, 2) ;2 = #DT_RIGHT
          y - pixelY                    ;Y-Pixelwert ist für nächste Zahl
      Next
      
      DeleteObject_(pen)
      
      ; -------------------------------------------
      
      baseX + 4  ;etwas Abstand zur senkrechten Linie für Text + Balken
      
      ;Text unten für Balken            
      altX = 0
      For j = 1 To balken 
          x = baseX + ((j-1) * pixelX)  ;pixelposi für nächsten Balken   
          txt$ = dinfo$(j)
          breite = TextWidth(txt$) 
          br = breite: If pixelX > breite: br = pixelX: EndIf
          If altX + br <= x           ;kein Platz, kein Text   
             r\top = baseY + 3        ;r.rect definiert den Raum für Text
             r\left = x - 2           ; -2 um baseX + 4 auszugleichen 
             r\right = r\left + br    ; + br ist Textbreite oder Balkenabstand
             r\bottom = r\top + TextHeight(txt$)             
             SetTextAlign_(dc, #TA_TOP)     ;ohne geht nix
             DrawText_(dc, txt$, -1, r, 1)  ;1 = #DT_CENTER
             ;DrawTextR(dc, r, txt$, 90, 0, 12)
             altX = x
             InflateRect_(r,1,1)            ;vergrößert r.rect für DrawEdge
             DrawEdge_(dc,r,1,#BF_RECT)	    ;nur für die Optik
          EndIf
      Next
      
      ;Balken 
      balkenbr = pixelX - 4
      If balkenbr < 1: balkenbr = 1: EndIf
       
      For j = 1 To balken 
          lh = (daten(j) * pixelY) / scaleY  ;höhe des Balken
          x = baseX + ((j-1) * pixelX)       ;pixelposi für nächsten Balken
          farbe = #Cyan                      ;
          If lh > 3 * pixelY: farbe = #Blue: EndIf
          If lh > 6 * pixelY: farbe = #Green: EndIf
          Box(x, baseY, balkenbr, -lh, farbe) ;-lh, damit Balken von unten
      Next                                    ;      nach oben gemalt wird
  
  StopDrawing()
       
ImageGadget(#PB_Any,0,0, 0,0,ImageID(nr))

FreeFont(fontnr)

ProcedureReturn winnr
EndProcedure

Procedure BalkenDiagramm(main)

;zwischen geschobene Prozedur, welche die Daten bereitstellt

DisableWindow(main, 1)

max = 31               ;31 Balken
Dim tt$(max)
Dim dtn(max)
For j = 1 To max
 tt$(j) = Str(j)
 dtn(j) = Random(441)  ;Wert zwischen 0 und 441
Next

ProcedureReturn Balkendiagramm2(Str(max) + " Werte", tt$(), dtn(), main)
EndProcedure

;------------------------------------

main = 0
OpenWindow(main,0,0,800,600,"",#PB_Window_SystemMenu|1)

subwin = Balkendiagramm(main)
       
Repeat: event = WaitWindowEvent() 
        If event = #PB_Event_CloseWindow         
           If EventWindow() = subwin
              event = 0
              DisableWindow(0, 0)
              CloseWindow(subwin)
           EndIf   
        EndIf   
        
Until event = #PB_Event_CloseWindow 

End
Noch etwas zum Experimentieren, ein Code um Text gedreht auszugeben, um unten Wörter hinzusetzen.
Man muß dann aber in der Zeile, baseY = winhh - chrhh * 3, aus der 3 eventuell eine 6 machen.
Und anstatt von DrawText_ DrawTextR aufrufen.

Code: Alles auswählen

Procedure DrawTextR(dc, *r.rect, txt$, angle, fontid, fontsize, quality=2) 

  ; Diese Procedure gibt gedrehten Text aus 
  
  GetObject_(fontid,SizeOf(LOGFONT),NewFont.LOGFONT) 

  NewFont\lfEscapement = angle * 10 
  NewFont\lfHeight = fontsize 
  NewFont\lfWeight = quality*100 
  hFont = CreateFontIndirect_(NewFont) 
  
  oldFont = SelectObject_(dc,hFont) 
  
  *r\bottom + 5  ; diesen Wert bei Wörtern erhöhen
     
  SetTextAlign_(dc, #TA_RIGHT) 
  DrawText_(dc, txt$, -1, *r, 1)  ;1 = #DT_CENTER 
  
  SelectObject_(dc,oldFont) 
  DeleteObject_(hFont) 

EndProcedure 
__________________________________________________
Thread wurde verschoben
Anfänger>Code, Tipps und Tricks
08.08.2009
RSBasic
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer