Laufschriften gibt es in vielen Arten, an vielen Orten ist eine Laufschrift in Form von LEDs zu finden. Hier werden die Buchstaben durch LEDs angezeigt, dies sind natürlich keine durchgehenden Vektorlinien sondern nur kleine Punkte. Solche Laufschriften eignen sich als Softwarelösung auch als graphischer Effekt für eigene Programme.
Eine solche Laufschrift in der Höhe von 8 Zeilen ist einfach zu schreiben, in diesem Tutorial tasten wir uns Schritt für Schritt an eine lauffähige Variante in Visual Basic 2008 heran. Als Framework reicht .NET 2.0, daher sollte die Laufschrift auf allen gängigen Windows-System ohne weiteres lauffähig sein.

Schritt 1) Wie funktioniert das ganze mathematisch?
Die gewählte Zeilenanzahl von 8 Zeilen ist gerade für den Anfang gut, da man so eine ganze Spalte in einem einzigen Byte abbilden kann. Weniger Zeilen bieten nicht genug Platz, um sinnvoll arbeiten zu können, mehr Zeilen erfordern etwas mehr Logik bei der Erstellung. Bleiben wir daher hier einmal bei 8 Zeilen, in euren eigenen Programmen könnt ihr dann ja herum experimentieren.
Nehmen wir also an, eine Spalte besteht aus 8 Zeilen. So kann man über das Binärsystem alle 8 LEDs einer Spalte darstellen. Hierbei entspricht das oberste LED dem ersten Bit, das LED direkt darunter dem zweiten Bit und so weiter bis wir bei Bit 8 angelangt sind. dieses entspricht der untersten LED der Zeile.
Code :
1 2 3 4 5 6 7 8 | * -> 1
. -> 0
. -> 0
* -> 1
. -> 0
. -> 0
. -> 0
* -> 1 |
Ich selbst speichere hier die Werte zweistellig als Hexadezimalwert, es ist etwas anschaulicher, kostet aber mehr Platz da jede Spalte zwei Bytes statt eines in der Datendatei benötigt (von dem Text mal abgesehen).
Wir haben nun eine Methode, die LEDs einer einzelnen Spalte zu beschreiben bzw. diese später aus Daten herzustellen. Da man ja schwer ein Zeichen in einer einzigen Spalte erstellen kann, sollte ein Zeichen aus mehreren Spalten bestehen. Ob man hier jedem Zeichen eine feste Breite von 8 Spalten zuordnet (die leeren Spalten haben dann den Wert 00h) oder jedes Zeichen individuell lang macht, bleibt jedem selbst überlassen, man sollte aber in seinem Datenformat hier Klarheit schaffen. Ich habe im Anhang beide Varianten als Daten angehängt, hier mag jeder selbst entscheiden, was für ihn besser aussieht.
Schritt 2) Wie erstellen wir die Laufschrift?
Es gibt mehrere Möglichkeiten, die LED-Punkte optisch anzeigen zu lassen. Eine umfangreiche wäre die Möglichkeit, dynamisch im Programm Shapes zu erstellen und diese je nach anzuzeigenden Zeichen auszublenden oder einzublenden. Da diese Variante aber einen erheblichen Programmieraufwand bedeutet und außerdem die VisualBasicPowerPacks-DLL benötigt wird, lass ich diese Variante mal außen vor.
Eine einfache Variante wäre, intern ein Bitmap in ausreichender Größe zu erstellen und dieses dann in die Picturebox hinein zu kopieren. Dabei würde die gesamte Laufschrift auf einmal erstellt, der einzige Teil, der später zyklisch (z.B. in einem Timer oder per BackgroundWorker) abläuft, ist die Anzeige des entsprechenden Ausschnitts. Für dieses Tutorial hab ich mich für diese Variante entschieden.
Nun da auch die technische Ausführung feststeht, können wir die anzuzeigende Schrift erstellen. Dazu benötigt man ein Graphics-Objekt auf Basis einer Bitmap.
Code vbnet:
1 2 | ctlBitmap = New Bitmap(Länge, Höhe)
g = Graphics.FromImage(ctlBitmap) |
Code vbnet:
1 | g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias |
Nun müssen wir die Schrift in seine einzelnen Spalten und Zeilen zerlegen und dementsprechend auf der Bitmap plazieren. Aufgrund unserer Schriftdatei haben wir für jedes Zeichen die entsprechenden Daten zur Anzeige, je nach Art (proportional oder nicht) haben wir 1 bis 8 Spalten abzuarbeiten. Es bietet sich an, vor der Erstellung der Graphik den anzuzeigenden Text in einer Schleife durch zugehen und für jedes Zeichen die Daten auszulesen und gesammelt in einen Datenstream (in diesem Fall einen String) zu schreiben. Damit kann man später in einem Rutsch ohne viele verschachtelte Schleifen die jeweiligen Spalten anzeigen.
Code vbnet:
1 2 3 4 5 6 7 8 9 10 | For Zähler As Integer = 0 To Textlänge - 1
'Ist für das Zeichen ein Code vorhanden
If (Zeichen im Kontrollstring vorhanden) Then
'dann Code an Datenstring anhängen
Datenstream &= Daten zum gewählten Zeichen
Else
'ansonsten einen vorgegebenen Code (Rechteck) anhängen
Datenstream &= Daten eines Ersatzzeichens 'z.B. "FF818181818181FF00"
End If
Next |
Wir gehen also alle anzuzeigenden Spalten in einer Schleife durch, da wir für jede Spalte ein Byte haben, benötigen wir noch eine Schleife für die Zeilen. In dieser wird das Byte der Spalte über einen Binärvergleich mittels AND abgefragt, damit kann leicht ermittelt werden, ob an der betreffenden Stelle die LED leuchten soll oder nicht.
Code vbnet:
1 2 3 4 5 6 7 8 9 10 11 | For Spaltenzähler As Integer = 0 To Spalten - 1
Bytewert = Dezimalwert der jeweiligen Spalteninformation 'z.B. aus Hexwert umgewandelt
'arbeite die Zahl bitweise ab
For Zeilenzähler As Integer = 0 To 7
'Wenn an dieser Stelle der Punkt aktiv ist
If Bytewert And (2 ^ Zeilenzähler) Then
'zeichne ihn als Kreis in der vorgegebenen Größe und der Farbe 'Gelb' in die Graphik
g.FillEllipse(Brushes.Yellow, Rechteck(Links, Oben, Länge, Höhe))
End If
Next
Next |
Schritt 3) Wie animieren wir die Laufschrift?
Die Graphik wurde erstellt, nun müssen wir das ganze auch animieren. Anders als bei einer tatsächlichen LED-Laufschrift können wir hierbei Pixelweise vorgehen, damit läuft die Schrift schön weich durch.
Die einfachste Variante ist die Verwendung eines Timers, dieser läuft in regelmäßigen Abständen ab und ermöglicht ein gleichmäßiges Scrolling.
Code vbnet:
1 2 3 4 5 6 7 | 'Der Horizontalzähler muss statisch oder Formweit als Public dimensioniert sein, er darf nach einem Timerdurchlauf seinen Wert nicht verlieren
'Bitmap ab Startposition in Größe der Picturebox als Graphik in Picturebox einfügen
PictureBox = ctlBitmap.Clone(Rechteck(Horizontalzähler, 0, Länge der Picturebox, Höhe der Picturebox), ctlBitmap.PixelFormat)
'Startposition um 1 erhöhen
Horizontalzähler += 1
'Wenn Ende erreicht, Startposition zurücksetzen
If Horizontalzähler > (Länge der Bitmap - Länge der PictureBox) Then Horizontalzähler = 0 |
Wenn man nun den Timer startet, läuft die Schrift wie bei einer LED-Laufschrift durch.
Beispielprojekt:
Um ein schnelles Ergebnis zu ermöglichen hab ich hier den Code für eine solche Laufschrift, Man kann die Laufschrift stoppen und neu starten bzw. die LED-Größe per Numerical-Up-Down im laufenden Programm ändern.
Erstelle ein neues Projekt mit einer Form (Form1) und füge folgende Steuerelemente hinzu:
Button1: CommandButton
Picturebox1: PictureBox
Timer1: Timer
NumericUpDown1: Numerical-Up-Down
Folgender Code kommt in den Codebereich der Form:
Code vbnet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | Public sLine As String, iLength As Integer, bCancel As Integer, cnt As Integer
Public ctlBitmap As Bitmap, g As Graphics
Public iDotSize As Integer, iDotStep As Integer, bStarted As Boolean = False
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim iChars As Integer, iBLen As Integer = PictureBox1.Width
'Wenn die Ausführung abgebrochen wurde, wird der Text neu erzeugt
'Dies gilt natürlich auch für den ersten Start
If bCancel Then
'Datenstring leeren
sLine = Nothing
'Gehe den anzuzeigenden Text durch
For cnta As Integer = 0 To sText.Length - 1
'Ist für das Zeichen ein Code vorhanden
If sChars.IndexOf(sText.Substring(cnta, 1)) > -1 Then
'dann Code an Datenstring anhängen
sLine = sLine & sCharCodes(sChars.IndexOf(sText.Substring(cnta, 1))) & "00"
Else
'ansonsten einen vorgegebenen Code (Rechteck) anhängen
sLine = sLine & "FF818181818181FF00"
End If
Next
'Hier wird nun die Laufschrift als Bitmap erzeugt
'Horizontale Größe der Graphik in anzuzeigenden Spalten (= Halbe Länge des Datentrings)
iChars = Int(sLine.Length / 2) - 1
Dim iBit As Integer, iGLen As Integer
'Horizontale Größe in Pixel umrechnen und die Buffer vorne und hinten (jeweils die Größe der Picturebox) hinzufügen
iGLen = iChars * iDotStep + 2 * iBLen
'Bitmap mit den bekannten bzw. berechneten Abmessungen erzeugen
ctlBitmap = New Bitmap(iGLen, PictureBox1.Height)
g = Graphics.FromImage(ctlBitmap)
'AntiAlias aktivieren
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
'Gehe alle Spalten durch
For cnCol As Integer = 0 To iChars
'Wenn bereits alle Zeichen angezeigt wurden
If cnCol >= sLine.Length - 1 Then
'generiere nur mehr leere Felder
iBit = 0
Else
'ansonsten lese den Code der jeweiligen Spalte aus und wandle ihn in eine Integer-Zahl um
iBit = CInt("&H" & sLine.Substring(cnCol * 2, 2))
End If
'arbeite die Zahl bitweise ab
For cnRow As Integer = 0 To 7
'Wenn an dieser Stelle der Punkt aktiv ist
If iBit And (2 ^ cnRow) Then
'zeichne ihn als Kreis in der vorgegebenen Größe und der Farbe 'Gelb' in die Graphik
g.FillEllipse(Brushes.Yellow, New RectangleF(iBLen + cnCol * iDotStep + Int(0.7 * iDotStep), cnRow * iDotStep + Int(0.7 * iDotStep), iDotSize, iDotSize))
End If
Next
Next
'Zähler für Ticker (ab wo wird angezeigt) zurücksetzen
cnt = 0
'Sperre lösen und Timer aktivieren
bCancel = False
Timer1.Enabled = True
Button1.Text = "Ticker stoppen!"
Else
'Timer deaktivieren
Timer1.Enabled = False
bCancel = True
Button1.Text = "Ticker starten!"
End If
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Ermittle Größen
SetSize()
'Sperre für Erstellen der Bitmap lösen
bCancel = True
'Lade die Daten für die Darstellung der einzelnen Zeichen
subLoadChars()
'Maximale Größe der Punkte aufgrund der Formgröße setzen (für Up-Down-Steuerelement)
NumericUpDown1.Maximum = Int((Me.ClientSize.Height - 100) / 18)
'Aktuelle Größe in Up-Down-Steuerelement setzen
NumericUpDown1.Value = iDotSize
'boolean-Variable damit Up-Down-Element bei Programmstart nicht Vorrang hat
bStarted = True
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
'Wenn der Ticker angehalten wurde (Button) dann Code nicht ausführen
If bCancel Then Exit Sub
'Bitmap ab Startposition in Größe der Picturebox als Graphik in Picturebox einfügen
PictureBox1.Image = ctlBitmap.Clone(New Rectangle(cnt, 0, PictureBox1.Width, ctlBitmap.Height), ctlBitmap.PixelFormat)
'Startposition um 1 erhöhen
cnt += 1
'Wenn Ende erreicht, Startposition zurücksetzen
If cnt > ctlBitmap.Width - PictureBox1.Width Then cnt = 0
End Sub
Public Sub SetSize()
'Breite der Picturebox auf Formular abstimmen
PictureBox1.Width = Me.ClientSize.Width - 20
'Größe der Matrixpunkte bei aktueller Höhe der Picturebox ermitteln
'(8 Punkt + Abstände zwischen den Punkten + Abstände oben und unten
iDotSize = Int(PictureBox1.Height / 18)
'Abstände zwischen den Mittelpunkten der Kreise = 2 * Punktdurchmesser
iDotStep = iDotSize * 2
'Höhe der Picturebox aufgrund der Punktgröße ermitteln
iLength = Int(PictureBox1.Width / iDotStep)
'Größe der Picturebox aufgrund der ermittelten Maße setzen
PictureBox1.Width = iLength * iDotStep
PictureBox1.Height = iDotSize * 18
End Sub
Private Sub NumericUpDown1_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles NumericUpDown1.ValueChanged
'Wenn Startprozedur noch nicht abgelaufen dann raus hier
If Not bStarted Then Exit Sub
'Ticker deaktivieren
bCancel = True
Timer1.Enabled = False
Button1.Text = "Ticker starten!"
'Neue Höhe aufgrund der gewählten Pixelgröße setzen
PictureBox1.Height = NumericUpDown1.Value * 18
'Ermittle Größen
SetSize()
End Sub |
Erstelle weiters ein Modul und füge dort folgenden Code ein:
Code vbnet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Public sChars As String, sCharCodes() As String
Public Const sText As String = "Das ist ein Probetext für den Ticker. Er ist nicht sehr lang, soll aber die Funktionsweise mit den Zeichen zeigen!"
Public Sub subLoadChars()
'Festlegen, wo die Schriftdatei liegt
Dim sFile As String = Application.StartupPath & "\font_normal.txt"
Dim sTemp As String, sInhalt As String = "", iCharCount As Integer = 0, sLines() As String
'Lade die Schriftdatei
If IO.File.Exists(sFile) Then sInhalt = IO.File.ReadAllText(Application.StartupPath & "\font.txt", System.Text.Encoding.Default)
'Datei splitten
sLines = sInhalt.Split(vbNewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
'Alle Zeilen durchgehen
For cnt As Integer = 0 To sLines.Length - 1
'Zeile lesen
sTemp = sLines(cnt).Replace(vbNewLine, "")
'ist der Tag '<char>' am Anfang
If sTemp.StartsWith("<char>") Then
'Tags entfernen
sTemp = sTemp.Replace("<char>", "").Replace("</char>", "")
'und den zugeordneten Buchstaben in einen String
sChars &= sTemp
'nächste Zeile auslesen
sTemp = sLines(cnt + 1)
'und die dortigen Tags entfernen
sTemp = sTemp.Replace("<charcode>", "").Replace("</charcode>", "")
'Array erhöhen
ReDim Preserve sCharCodes(iCharCount)
'und die Informationen zum, Buchstaben einlesen
sCharCodes(iCharCount) = sTemp
iCharCount += 1
End If
Next
End Sub |
Im Anhang befinden sich außerdem folgende Dateien mit Schriftinformationen:
font_normal.txt: Schrift mit variablen Zeichengrößen
font_proportional.txt: Proportionale Schrift (wie z.B. Courier), d.h. jedes Zeichen ist 8 Punkte breit, auch wenn es eigentlich schmäler ist (z.B. das "i")
Wo die Schriftinformationen zu finden sind, wird im Modul in der Prozedur subLoadChars gleich in der ersten Codezeile festgelegt. am besten man kopiert sie in das gleiche Verzeichnis, in dem sich auch die ausführbare Datei befindet (zum Testen also nach bin/Debug).
Ich hoffe, das Tutorial ist verständlich und animiert euch, ein bißchen mit den Möglichkeiten zu spielen.
Der Doc!




Bereiche
Kategorien
Forum - Programming





tutorials.de-Systemmitteilung