Notifications
Clear all

[Solved] Bar/QR code reader problems

15 Posts
4 Users
0 Likes
319 Views
MadMisha
(@madmisha)
Member
Joined: 4 years ago
Posts: 340
Topic starter  

So, my setup will eventually be a Raspberry Pi with touchscreen running Raspberry Pi OS (Although Ubuntu might be considered) in kiosk mode. The program is already built and deployed using Python and Tkinter as the GUI. It takes a file off a thumb drive, parses it into a database and then, when requested, a record can be sent to a networked receipt printer for printing. Everything works fine for now running from PC. The Pi has not arrived yet (and I do not want to take apart my Pi 4s) for the kiosk version so I am doing all the testing on a windows PC for now, since that is how all the other versions that we use are currently running on.

 

My idea was to attach a USB Barcode reader so that a person could walk up and scan their bar code or QR code and it would automatically print off their receipt. The bar code reader is set up as a keyboard(default) and when scanning my QR code with notepad open, it works fine. There is an RS232 mode but I have avoided that since I had so much trouble getting different windows computers to be able to interface with the USB, I gave up on that idea (This is actually why the printer in now over the network). The code will always be a 3 or 4 digit number but I need it to run while Tkinter is running. I only bound the number keys and enter in hopes that it will work as an interrupt. I have tried a few variations but here is a sample:

def scanchange(event): #toggles barcode scanner function
    global scanFlag
    global scanGet
    print("ScanChange Entered") #*******************************************************************DEBUG*************************************
    if scanFlag: #Checks to enter or exit scan mode(Toggle)
        print("ScanChange False") #*******************************************************************DEBUG*************************************
        scanFlag = False
        #Add unbind here ***********************************************************************************************
    else:
        print("ScanChange True") #*******************************************************************DEBUG*************************************
        scanFlag = True
        for i in range(10): #Sets binding of number keys
            tearoff.bind(str(i), barcodeGet)
        tearoff.bind('<Enter>', barcodeSet) #Binds enter key for trigger

def barcodeSet(event): #Enter Key pressed(by barcode reader), get tuple of scan and ID numbers
    global scanGet
    global idNumbers
    print("Barcode Set Entered") #*******************************************************************DEBUG*************************************
    print(scanGet)
    printGet = ""
    for i in idNumbers:
        if int(i[0]) == scanGet:
            printGet = i[2]
    #print(str(printGet)) #*******************************************************************DEBUG*************************************
    scanGet = '' #Clear Holder for incomming code

def barcodeGet(n): #Number recieved, add to string
    global scanGet
    scanGet += n.char

 

When testing with my program, it either does not trigger or enters it with multiple sets of the number(The number for testing was 1000 and the number entered was 100010001000 and after a long wait) It appears to not like to listen for the enter button. I also suspect that it is really lagging. Sometimes it takes all the numbers together and then receives the enters out of order. Swapping the order of key bindings did not work.

Here is what the console shows when I scanned 3 times(about 15-25 seconds between):

ScanChange Entered
ScanChange True
Barcode Set Entered
100010001000

Barcode Set Entered


Barcode Set Entered

 

The right number of enters are there but after the rest.

Example of 3 scans using notepad, where the text is entered immediately:

1000
1000
1000

 

 

I am open to different methods of listening to the scanner but I am unsure how to do this while running Tkinter. Wait for input with no key bindings did not work at all.

 


   
Quote
Inq
 Inq
(@inq)
Member
Joined: 2 years ago
Posts: 1900
 

Posted by: @madmisha

I am open to different methods of listening to the scanner but I am unsure how to do this while running Tkinter. Wait for input with no key bindings did not work at all.

Both Windows and RasPi are multi-process/multi-threaded/multi-user OS's.  I'm not familiar with Tkinter, I can't see why it would take over the machine entirely.  I'm wondering why you can't just have a totally separate program that runs this scanner and spits it out to the printer while your Python/Tkinter POS is working the UI in the foreground.  It doesn't sound like you need a U.I. for it and it can just sit their monitoring the USB for incoming data.  If Notepad can intercept this data, surely some background process can do the same thing. 

I can't really read your code as I've never picked up Python (yet).  Some things look familiar.  But... if Notepad is receiving things as you expect then, I'm wondering if your Python code is catching the CR and LF characters and processing them.  Those are what is making Notepad put them on different lines.  Maybe your program doesn't catch those to say... I have the code, now send it to the printer.  The ASCII values of those characters are CR=13 and LF=10.  Or in HEX 0xD and 0xA if you prefer.

Posted by: @madmisha

The Pi has not arrived yet (and I do not want to take apart my Pi 4s) for the kiosk version so I am doing all the testing on a windows PC for now, since that is how all the other versions that we use are currently running on.

About the only thing, that I thought might be useful to you... There is a version of Raspberry Pi OS that runs on an Intel PC.  I had a dual-boot (w/ Windows) of it for a while because I wanted a faster machine to do a little quicker development turnaround.  

3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1122
 

@madmisha - Its nice to see a python query for a change 😀 .  

I was just having a final check of my emails et al before turning in for the night, and I had a quick look at the forum and I can help you with your code.  But it wont be until my tomorrow evening (00:44 in UK as I type).  Maybe someone will have given you suitable pointers by then, but if not I will examine your code in detail and give some suggestions.   It would probably assist me if you showed your complete code rather than just a snippet.  You could attach it as a file.   In the meanwhile you may like to google the Tkinter event loop to see if you find suitable answers.

Off to my slumbers... 


   
ReplyQuote
MadMisha
(@madmisha)
Member
Joined: 4 years ago
Posts: 340
Topic starter  

Posted by: @inq

I'm wondering why you can't just have a totally separate program that runs this scanner and spits it out to the printer while your Python/Tkinter POS is working the UI in the foreground.  It doesn't sound like you need a U.I. for it and it can just sit their monitoring the USB for incoming data.  If Notepad can intercept this data, surely some background process can do the same thing. 

It is all being done within the same process with interaction between different elements. There needs to be a visual confirmation of the number that was scanned, as well as a way to input the number in cases were there is nothing to scan. There also needs to be administrator tasks that can be done from the screen. It cannot run headless. There is more to this program, I'm just trying to keep with the relevant info to keep this post from unneeded bloat, as well as protect some of the more sensitive data.

It is not that Notepad is intercepting the data. The barcode scanner is as simple as when you scan it, it pretends to be your keyboard and types the number, followed by an enter keystroke and it does it very fast. Testing on Notepad was just clicking on the empty space in Notepad to place the cursor there and scanning a QR code.

 

You can type in Tkinter and there seems to be no problem. I have entry fields that work. I might try having an invisible entry field that gets focus and not have the keys bound. I would have to make everything else on the screen return focus immediately after their press. This does not sound like the ideal solution but merely a workaround. I will keep it in my back pocket just in case.

Posted by: @inq

Those are what is making Notepad put them on different lines.  Maybe your program doesn't catch those to say... I have the code, now send it to the printer.

Since it is just the keyboard and key strokes, the mystery is why would it treat the number keys differently than the enter key. It appears to put the enter keystroke on hold until the rest is dealt with/program has free time.


   
ReplyQuote
MadMisha
(@madmisha)
Member
Joined: 4 years ago
Posts: 340
Topic starter  

   
ReplyQuote
MadMisha
(@madmisha)
Member
Joined: 4 years ago
Posts: 340
Topic starter  

@inq and @byron

Problem solved. After some time trying to figure out why the mouse triggered it, I now learned that I needed to do this:

tearoff.bind('<Return>', barcodeSet) #Binds enter key for trigger

#Not this

tearoff.bind('<Enter>', barcodeSet) #Binds enter key for trigger

In my defense, I haven't seen a return key since my grandma's Commodore 64. 🤣 


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1122
 

@madmisha

I was working outside and it started to rain so a cupOtea and a look at your snippet, only to find you've found your solution. 😀 

Oh well, but I could not help notice all those tkinter imports etc.  I guess you have not cleaned up your test program so you are forgiven 😎 

Just in case it helps I give a brief snipped along the lines of getting a barcode read into a program using the tkinter enter widget.  I just place the barcodes, or whatever is entered into the enter widget into a list for later printing so I could check the entry was working OK.  I enter the barcode into the program by a screen button, whereas you enter it via a bound key, but no difference in effect.  Your font/colours came up really horrible on my mac screen so I used others and my example screen may seem rather drab. 

As tkinter starts with a main or root event loop into which one creates 'frames' and into the frames one creates the widgets belonging to the frame, I try to create them in that logical order, but thats not to say there's not many other ways to code it all up.

So here is a very quick and simple barcode entry program for whats its worth (probably just a quick half hours elongated tea break for me).  But a bit of fun as I've not used tkinter for several year as I prefer using pyqt5 as its screen layout facility is rather akin to the jolly old visual basic layout screen, drag the widgets where you want them to be placed on a layout grid on screen.

version = "Version: Byron"

import sys
import tkinter as tk              
from tkinter import filedialog as fd


scanned_numbers = []

# global variables
titleMaker = 'Test Version: ' + version

# program functions
def program_stop():
    sys.exit()
    
def show_help():
    pass

def we_have_a_barcode():
    # add barcode to the scanned_numbers list
    scanned_numbers.append(e_barcode.get())
    # remove barcode text from entry label
    barcode.set('')
    
def print_barcodes():
    for bc in scanned_numbers:
        print('Barcode entered is: ', bc)
     

# tkinter main loop ****************************************
root = tk.Tk()
root.title("Byron Example")
# eg. other root settings like:
# root.geometry('800x410+0+0')
# root.resizable(width=False, height=False)

# tk string variables
barcode = tk.StringVar()

# tkinter frames **************************************
f_barcode = tk.Frame(root)
# other frames can be created here if desired.
# f_other_frame = tk.Frame(root)

# frame barcode  *******************************
#  - define widgets
l_bc_enter = tk.Label(f_barcode, 
                     text = 'Enter barcode', 
                     font = 'Helvertica 18',justify = 'left',
                     fg = 'black',bg = 'seagreen')
e_barcode = tk.Entry(f_barcode,
                     textvariable=barcode,
                     font = 'Helvertica 18',justify = 'left',
                     fg = 'black',bg = 'white')
btn_bc_enter = tk.Button(f_barcode, text = 'Enter Barcode',command = we_have_a_barcode, bd = 4)
btn_bcs_print = tk.Button(f_barcode, text = 'Print Barcode', command = print_barcodes, bd = 4)
btn_help = tk.Button(f_barcode, text = 'Help', command = show_help, bd = 4)
btn_exit = tk.Button(f_barcode, text = 'Exit', command = program_stop, bd = 4)

#  - place widgets
l_bc_enter.grid(row = 0, column= 0)
e_barcode.grid(row = 0, column = 1)
btn_bc_enter.grid(row = 0, column = 2)
btn_bcs_print.grid(row = 1, column = 0)
btn_help.grid(row = 2, column = 0, padx = 30, pady = 10)
btn_exit.grid(row = 2, column = 1, padx = 30, pady = 10)

# frame other_frame *********************************
#  - define widgets
# etc
#  - place widgets
# etc


# display frames **************************************
f_barcode.grid(row = 0, column = 0)


# run tkinter event loop
root.mainloop()

 

 

This post was modified 10 months ago by byron

   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1122
 

@madmisha

I dont know why some of my code screen came out in red text, probably because I started out by copying your snippet file, only to then obliterate most of it.  The revenge of the red text... 


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6988
 

@byron Look at the file in HEX and the answer will be revealed I suspect (special/unprintable formatting codes)

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1122
 

Posted by: @madmisha

@inq and @byron

Problem solved. After some time trying to figure out why the mouse triggered it, I now learned that I needed to do this:

tearoff.bind('<Return>', barcodeSet) #Binds enter key for trigger

#Not this

tearoff.bind('<Enter>', barcodeSet) #Binds enter key for trigger

In my defense, I haven't seen a return key since my grandma's Commodore 64. 🤣 

Just to remind that binding the Retrun key at the root level (your tearoff is your root) then you are binding the function nominated to run when return is pressed to execute whenever the return key is pressed whilst you are in your tkinter event loop.  

This may be what you want, but possibly you only want to bind the return key press to run a function whenever you are in the barcode entry widget.  (you may have other entry widgets etc)

In my example I would bind the Return key to the barcode entry widget with

e_barcode.bind('<Return>',we_have_a_barcode)
 
and not with a root.bind('<Return>',we_have_a_barcode)
 
But note that I would then need to amend the we_have_a_barcode function to accept a parameter - e.g.
def we_have_a_barcode(event=None):

 

 


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1122
 

@madmisha

its evening time and I just had another quick look at my example.  Just for the sake of I thought I would tidy up and round off the example with the Return Key bindings and an extra entry box.  Also, as its something that can easily bite one in the bum, I show the using the underlying os to run call up a pdf file as its slightly different depending on the op system.

Now its time for supper, enough playing for now 😎 

version = "Version: Byron2"

import sys
import tkinter as tk              
import subprocess, os, platform

scanned_numbers = []

# global variables
titleMaker = 'Test Version: ' + version

# program functions
def program_stop():
    sys.exit()
    
def show_help():
    filepath = '/Users/BG/RPI/Projects-RPI/Program_Snippets/TST_files/help.pdf'
    # annoyinly the call to the os system to show the file differs depending on mac/windows/linux
    if platform.system() == 'Darwin':       # macOS
        subprocess.call(('open', filepath))
    elif platform.system() == 'Windows':    # Windows
        os.startfile(filepath)
    else:                                   # linux variants
        subprocess.call(('xdg-open', filepath))

def we_have_a_barcode(event=None):
    # add barcode to the scanned_numbers list
    if e_barcode.get() == '':
        return
    scanned_numbers.append(e_barcode.get())
    # remove barcode text from entry label
    barcode.set('')

def we_have_other(event=None):
    print('Other stuff entered was: ',e_other_entry.get())    
    other.set('')
    
def print_barcodes():
    print('Barcodes in List:')
    for bc in scanned_numbers:
        print('Barcode entered is: ', bc)
        
# tkinter main or root event loop **********************************
root = tk.Tk()
root.title(titleMaker)
# other root settings like: e.g.
# root.geometry('800x410+0+0') # just right for official rpi screens
# root.resizable(width=False, height=False) # remove ablilty to resize screen

# tk string variables
barcode = tk.StringVar()
other = tk.StringVar()

# bind keys
#root.bind('<Return>', we_have_a_barcode)

# tkinter frames **************************************
f_barcode = tk.Frame(root)
# other frames can be created here if desired.
# f_other_frame = tk.Frame(root)

# frame barcode  *******************************
#  - define widgets
l_bc_enter = tk.Label(f_barcode, 
                     text = 'Enter barcode', 
                     font = 'Helvertica 18',justify = 'left',
                     fg = 'black',bg = 'seagreen')
l_other_enter = tk.Label(f_barcode, 
                     text = 'Enter Other Stuff', 
                     font = 'Helvertica 18',justify = 'left',
                     fg = 'white',bg = 'blue')
e_barcode = tk.Entry(f_barcode,
                     textvariable=barcode,
                     font = 'Helvertica 18',justify = 'left',
                     fg = 'black',bg = 'white',
                     )
e_other_entry = tk.Entry(f_barcode,
                     textvariable=other,
                     font = 'Helvertica 18',justify = 'left',
                     fg = 'black',bg = 'white',
                     )
e_barcode.bind('<Return>',we_have_a_barcode)
e_other_entry.bind('<Return>', we_have_other
             )
btn_bc_enter = tk.Button(f_barcode, text = 'Save Barcode',command = we_have_a_barcode, bd = 4)
btn_bcs_print = tk.Button(f_barcode, text = 'Print Saved Barcodes', command = print_barcodes, bd = 4)
btn_help = tk.Button(f_barcode, text = 'Help', command = show_help, bd = 4)
btn_exit = tk.Button(f_barcode, text = 'Exit', command = program_stop, bd = 4)

#  - place widgets
l_bc_enter.grid(row = 0, column= 0)
e_barcode.grid(row = 0, column = 1)
btn_bc_enter.grid(row = 0, column = 2)
l_other_enter.grid(row = 1, column = 0)
e_other_entry.grid(row = 1, column = 1)
btn_bcs_print.grid(row = 2, column = 1, padx = 30, pady = 10)
btn_help.grid(row = 3, column = 0, padx = 30, pady = 10)
btn_exit.grid(row = 3, column = 2, padx = 30, pady = 10)

# frame other_frame *********************************
#  - define widgets
# etc
#  - place widgets
# etc


# display frames **************************************
f_barcode.grid(row = 0, column = 0)


# run tkinter event loop
root.mainloop()
This post was modified 10 months ago by byron

   
ReplyQuote
MadMisha
(@madmisha)
Member
Joined: 4 years ago
Posts: 340
Topic starter  

Posted by: @byron

 Your font/colours came up really horrible on my mac screen so I used others and my example screen may seem rather drab. 

Thanks, that is actually really good to know. I have used this scheme on many windows and Linux(Ubuntu, Mint and Raspberry Pi OS) but I have no ventured into using it on Mac. I hope it's more than just a disliking of dark themes. We often work in the dark so I try to make my apps for work not too bright as to not distract others around. This one in particular might not need to be that way. Could you post a screenshot? That would help me out.

Posted by: @byron

Just to remind that binding the Retrun key at the root level (your tearoff is your root) then you are binding the function nominated to run when return is pressed to execute whenever the return key is pressed whilst you are in your tkinter event loop.

There is a point of no return in my app, that is the point where I would, if conditions are met, call the key bind. After that, I actually want it to scan and print, no matter what is on screen (not including my hot key for testing, i.e. the F2 bind that triggers it. I have others for testing too, many things are conditional. Sometimes there will be no receipt printer and I don't want the buttons for that to appear or the scan to work/be bound.). But I did intend to run it at root level and be a "priority" in the background.

Posted by: @byron

As tkinter starts with a main or root event loop into which one creates 'frames' and into the frames one creates the widgets belonging to the frame, I try to create them in that logical order, but that's not to say there's not many other ways to code it all up.

I have a logical order as well. Usually it starts with a frame always called mainFrame that is global. This gives me the ability to destroy it and start again whenever I need to (I started with using winfo children to iterate through and destroy widgets before I found my new method). But with this application, I eventually have to put a canvas in the frame and I have to use that due to needing the ability to scroll (where I have more bindings for the mouse scroll wheel to work, that was a misdirection when diagnosing this problem). Starting over, I would probably have started in canvas and kept it all contained in there. I might rebuild it that way and have a graphic designer make the scheme. I did that for a game once and it worked well. I did not know that was needed when I began.

Posted by: @byron

lso, as its something that can easily bite one in the bum, I show the using the underlying os to run call up a pdf file as its slightly different depending on the op system.

The help file was only tested on windows, but also only used on windows up until this point. Thanks for that bit!

 

Thanks for the help though. You gave me a lot to look into.

This post was modified 10 months ago by MadMisha

   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1122
 

@madmisha

Posted by: @madmisha

Could you post a screenshot? That would help me out.

Here is what it looks like on my mac, cannot see the button text.

Screenshot 2023 07 05 at 21.03.00

Posted by: @madmisha

I have a logical order as well

I was using and old snippet to remind myself what to do in tkinter, but theres no preferred way as far as the examples I see and I'm sure your preferences will be just a good.   I just looked at a 'proper' program that I wrote some time ago instead of my snippets folder.  I see I created a class with methods to use tkinter as was appropriate to my program and kept it in a separate module which helps to keep things tidy in a larger program.  Once I have a working module working it always get a lot of refining and refactoring before I think its ready to go.

Just to make you hanker after an easy way to layout a GUI screen I show you the GUI designer window of PyQt5. All the widgets are drag and drop with easy access to see all the setting that can be made for each widget.  I wish tkinter has a similar feature.  Working out how the widgets will look on the tinker grid can be a bit of a pain.

Screenshot 2023 07 05 at 21.19.51

I should not temp you really, as I expect you have quite enough on your plate with your barcode project.  I hope it all works out splendidly for you 👍 


   
ReplyQuote
MadMisha
(@madmisha)
Member
Joined: 4 years ago
Posts: 340
Topic starter  

@byron 

That took all the meaningful color out of my buttons! Luckily, none of our laptops are Macs so I should be fine for now. I guess I have to do some reworking if that does come up.

I was stuck between Tkinter and the other popular one. I have never been really happy with it but I stuck with it because I knew it fairly well and learned how to automate parts of the GUI. I love PyQt5's setup, it reminds me of the Nextion Editor for their displays, as well as a little bit of ETC Paradigm lighting control. Can I programically add buttons through script, as in make a list for every record in an array? That is what tipped me over the edge to learn Tkinter, that is what I am doing on this particular app. That might be my next evolution, thanks.


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1122
 

@madmisha

Posted by: @madmisha

I love PyQt5's setup, it reminds me of the Nextion Editor for their displays, as well as a little bit of ETC Paradigm lighting control. Can I programically add buttons through script, as in make a list for every record in an array

Yes, you can programatically add all the widgets by a script.   In fact the Qt designer only produces a formatted text file that looks like a xml file.  A Qt program then needs to be run which will read this file and output a file containing a python class to build the screen.   To make use of the python class as generated then one needs to subclass it in your own python script.   Most example founds in the internet on PyQt5 do not use the Qt designer but create the Gui via code just like tKinter.

I looked at tKinter, PyQt5 (I think its now PyQt6), wxpython and Kivy way back when, and my first python Gui attempts were in tKinter.  As mentioned I quite liked the PyQt5 for the Qt designer, and, as PyQt5 was subsequently included as standard in the rpi OS (I think it still does, but maybe not anymore?), I switched to using it.  However all of the possible Gui's have plus and minus points, and it may be worth a look at them all, or at least having a read up of articles referring to them,  to see which Gui may float your boat.  


   
ReplyQuote