German BT-Scanner + B4J

Heinz

Active Member
Licensed User
Longtime User
Hallo, ich will mir ein Programm in B4J (später auch in B4A) machen, das eine
Mitarbeiternummer ( QR-Code) mit einem BT-Scanner einliest. Diese Nummer
darf am aktuellen Tag nur einmal vorkommen, sodaß man am selben Tag nicht
2mal eingescannen, bzw. gespeichert werden kann. (Essenszuschlag pro Tag).
Um die Nummer eindeutig für die Map zu halten, wird sie so (4711|25.07.2023=25.07.2023)
als Wertepaar in die Map gespeichert. So kann sie nur einmal am Tag, aber mehrere Tage im
Monat erscheinen. Auch soll gleichzeitig eine .csv Datei geführt werden.
Die App soll später autark laufen, d.h. sie wird morgens gestartet und abends
beendet (ggf. auch über einen Monat).

Mit dem
TextField1_TextChanged (Old As String, New As String)
Event scheint das nicht richtig zu gehen. Mache ich was falsch oder wie
könnte ich das Textfeld sonst abfragen ?
Der Scanner hat Bluetooth und somit eine HID-Tastatur. Somit habe ich
mit BT in der App nichts am Hut. Der Scanner verbindet sich von selber mit
dem BT vom PC.

Scanner App:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private TextWriter1 As TextWriter
    Private Button1 As Button
    Private TextField1 As TextField
    Private Datei1 As String
    Private csvdatei As String
    Private Datum As String
    Private zeile As String
    Private MA As String
    Private MN As String
    Private found As Int
    Private Personal As Map
    Private ListView1 As ListView
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("layout1")
    MainForm.Show
    Personal.Initialize
    'InitListview
End Sub

Sub Button1_Click
  
End Sub

Private Sub TextField1_TextChanged (Old As String, New As String)
    ' Scanner hat als Endezeichen Chr(13) -> Return
    DateTime.DateFormat = "dd.MM.yyyy"
    Datum = DateTime.Date(DateTime.Now)
    DateTime.DateFormat = "MM_yyyy"
    Datei1 = DateTime.Date(DateTime.Now) & ".txt" ' Immer je Monat eine Datei z.b. 07_2023
    csvdatei = DateTime.Date(DateTime.Now) & ".csv"
    TextWriter1.Initialize(File.OpenOutput(File.DirApp, csvdatei, True))
    MA = TextField1.Text
    MA = New & "|" & Datum
    Personal.Clear
    If File.Exists(File.DirApp, Datei1) Then
        File.ReadMap(File.DirApp, Datei1)
        For Each MA As String In Personal.Keys
            Dim MN As String = Personal.Get(MA)
            If MN = Datum Then
                found = 1
            End If
        Next
        If found = 1 Then
            xui.MsgboxAsync("Mitarbeiter + Datum schon vorhanden", "Fehler !")
            ' MitarbeiterNr soll nur einmal pro Tag vorkommen
            ' beim mehraligen Scannen wird ignoriert.
        Else
            Personal.Put(MA & "|" & Datum, Datum)
            File.WriteMap(File.DirApp, Datei1, Personal)
            ListView1.Items.Add(New & "," & Datum)
            TextWriter1.WriteLine(New & "," & Datum)
        End If
    Else
        Personal.Put(New & "|" & Datum, Datum)
        File.WriteMap(File.DirApp, Datei1, Personal)
        ListView1.Items.Add( New & "," & Datum)
        TextWriter1.WriteLine(New & "," & Datum)
    End If
      
    TextField1.Text = ""
    'TextField1.RequestFocus
End Sub

Private Sub TextField1_FocusChanged (HasFocus As Boolean)
  
End Sub

Sub InitListview
    DateTime.DateFormat = "MM_yyyy"
    Datei1 = DateTime.Date(DateTime.Now) & ".txt"
    If File.Exists(File.DirApp, Datei1) Then
        Personal = File.ReadMap(File.DirApp, Datei1)
        For Each MA As String In Personal.Keys
            Dim MN As String = Personal.Get(MA)
            zeile = MA.SubString2(0, MA.IndexOf("|")) & "," & MN  ' trennen : z. B.  4711|25.07.2023 -> 4711,25.07.2023
            ListView1.Items.Add(zeile)
        Next
    End If
End Sub

Im Designer hatte ich nur ein Textfeld und eine Listbox erstellt.
 

Heinz

Active Member
Licensed User
Longtime User
Irgendwie zeigt es nicht das Richtige an, bzw. zuviel. siehe Bild.
Der letzte Eintrag ist richtig, nur die 3 davor nicht. Die sollten gar
nicht kommen. Es sieht so aus, als würde das Event (TextField1_TextChanged)
4mal ausgelöst. Also bei jeder Ziffer, die ins Editfeld reinkommt.

Und die .csv Datei wird nichts geschrieben
In der Textdatei (Map) steht folgendes (also einmal Datum zuviel im Key) :
#Wed Jul 26 07:26:27 CEST 2023
1001|26.07.2023|26.07.2023=26.07.2023
 

Attachments

  • Fehler.png
    Fehler.png
    12.5 KB · Views: 67
Last edited:

Alexander Stolte

Expert
Licensed User
Longtime User
4mal ausgelöst. Also bei jeder Ziffer, die ins Editfeld reinkommt.
Das ist richtig.
Was du dagegen machen kannst ist folgendes:
Du deklarierst eine Variable und füllst diese mit den aktuellen Uhrzeit Ticks sobald das TextCahnge Event ausgelöst wird. Mit einem Timer prüfst du dann einfach ob diese Variable älter als x Millisekunden alt ist und wenn das der Fall ist, kannst du dir sicher sein das die HID Tastatur fertig ist. Das ist etwas unsauber aber das wäre meine Idee für das Problem.
 

Heinz

Active Member
Licensed User
Longtime User
Ich habe es jetzt rausgefunden und anders gelöst., indem ich die Länge
der Mitarbeiternr. abfrage.
Bloß kann ich dann nur 4stellige Mitarbeiternummern (wie in meinem Fall) einscannen.

Scanner App:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private TextWriter1 As TextWriter
    Private Button1 As Button
    Private TextField1 As TextField
    Private Datei1 As String
    Private csvdatei As String
    Private Datum As String
    Private zeile As String
    Private MA As String
    Private MN As String
    Private found As Int
    Private Personal As Map
    Private ListView1 As ListView
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("layout1")
    MainForm.Show
    Personal.Initialize
    InitListview
End Sub

Sub Button1_Click
  
End Sub

Private Sub TextField1_TextChanged (Old As String, New As String)
If New.Length = 4 Then
    found = 0
    DateTime.DateFormat = "dd.MM.yyyy"
    Datum = DateTime.Date(DateTime.Now)
    DateTime.DateFormat = "MM_yyyy"
    Datei1 = DateTime.Date(DateTime.Now) & ".txt" ' Immer je Monat eine Datei z.b. 07_2023
    csvdatei = DateTime.Date(DateTime.Now) & ".csv"
    TextWriter1.Initialize(File.OpenOutput(File.DirApp, csvdatei, True))
    If File.Exists(File.DirApp, Datei1) Then
       File.ReadMap(File.DirApp, Datei1)
       If Personal.ContainsKey(New & "|" & Datum) Then
          found = 1
       End If
    End If 
    If found = 1 Then
            xui.MsgboxAsync("Mitarbeiter + Datum schon vorhanden", "Fehler !")
            ' MitarbeiterNr soll nur einmal pro Tag vorkommen
            ' beim mehraligen Scannen wird ignoriert.
    Else
        Personal.Put(New & "|" & Datum, Datum)
        ListView1.Items.Add(New & "," & Datum)
        TextWriter1.WriteLine(New & "," & Datum)
        TextWriter1.Close
    End If
    File.WriteMap(File.DirApp, Datei1, Personal)
    TextField1.Text = ""
    TextField1.RequestFocus
End If 
End Sub

Private Sub TextField1_FocusChanged (HasFocus As Boolean)
  
End Sub

Sub InitListview
    DateTime.DateFormat = "MM_yyyy"
    Datei1 = DateTime.Date(DateTime.Now) & ".txt"
    If File.Exists(File.DirApp, Datei1) Then
        Personal = File.ReadMap(File.DirApp, Datei1)
        For Each MA As String In Personal.Keys
            Dim MN As String = Personal.Get(MA)
            zeile = MA.SubString2(0, MA.IndexOf("|")) & "," & MN  ' trennen : z. B.  4711|25.07.2023 -> 4711,25.07.2023
            ListView1.Items.Add(zeile)
        Next
    End If
End Sub

Ob ein Chr(13) oder gar ein CRLF im New vorkommt bzw. abzufragen, scheint nicht zu funktionieren.
Das wäre flexibler, wenn z.b. 4stellige UND 5stellige Mitarbeiternummern existieren.
Oder gibt es einen HOTKEY, den man bestimmen könnte und diesen abfragen ?
So habe ich das in meinem XProfan gemacht.
Wie kann ich den neu eingefügten item in der Listview selektieren ?
Ansonsten könnte ich das fast 1 : 1 für B4A übernehmen. File.DirApp müßte z.b. auf
File.DirIntent und das Listview mit AddSingleLine usw. Und mit
EditText1_EnterPressed dürfte das noch einfacher gehen.
 
Last edited:

Alexander Stolte

Expert
Licensed User
Longtime User
Wie kann ich den neu eingefügten item in der Listview selektieren ?
Das neue Item ist ja ganz unten in der Liste, also dann ganz einfach:
B4X:
CustomListView1.GetPanel(CustomListView1.Size -1).Color = CustomListView1.PressedColor 'Your Selected Color
Denk an den Loop wenn du meine Variante verwendest, falls du nur einen Selektiert haben willst. Wenn du Erel seine Klasse verwendest, dann machst du das mit dieser.
 

Alexander Stolte

Expert
Licensed User
Longtime User
Oder gibt es einen HOTKEY, den man bestimmen könnte und diesen abfragen ?
check mal das aus:
 

Heinz

Active Member
Licensed User
Longtime User
Der Event TextField1_Action kann man im Designer aktivieren und
wäre ja genau das richtige für mich.
Das wird auch bei deinem Link vorgeschlagen und der Fragende
hat bestätigt, daß es mit RETURN klappt. Bei TAB oder zusammengesetzten
Keys (CRLF) ist es so eine Sache.

Aber wie man das in dem SUB TextField1_Action behandelt, findet
man leider nichts im Forum. Da müßte doch irgendwo eine Info
zu finden sein. Auch gibt es keine Parameter, worauf man dann
schließen könnte, wie etwa bei
TextField1_TextChanged (Old As String, New As String)
auf Old und New.
 

Heinz

Active Member
Licensed User
Longtime User
Auch hier bin ich schlauer geworden.
Der Event TextField1_Action erkennt das Return automatisch.
Ich mußte nur den Inhalt von TextField1_TextChanged (Old As String, New As String)
nach TextField1_Action verschieben. Die Variable New, die ja nicht mehr existiert,
habe ich durch
MA = TextField1.Text
ersetzt.
Funktioniert jetzt,wie es soll.
Danke nochmals.

PS : Im Nachhinein denke ich, daß _Action den Windowsbesonderheiten
geschuldet ist. Gibt man normalerweise in ein einzeiliges Editfeld per Tastatur
ein Return, meckert Windows mit einem Warnton. Vielleicht wird dadurch eine
bestimmte Message verschickt, die sich Erel hierfür zunutze gemacht hat.
Genauso wird es auch bei TextField1_TextChanged (Old As String, New As String)
sein, wo die Windowsmessage $00B8 : em_GetModify dahinter steckt.

Ist halt bei B4J etwas anders (durch Java). In normalen Programmiersprachen
kann man sich oft mit der API, SubClassing, Callbackfunktionen, Messages u. ä.
behelfen, wenn es die Funktionalität der Sprache nicht hergibt. API-Funktionen
können sie fast alle. SubClassing eines Controls z.B. habe ich ja schon öfters
gemacht.
 
Last edited:
Top