Posts: 9
Threads: 4
Joined: Jan 2019
Bonjour experts !
It is still for my printer controller project.
I'm now trying to make a lib to get a flexible, and easy tom modify command list.
So the goal for now is that I want to create a custom user command list, with functions declared in a class.
So here is what I've done :
#File name = test.py
from printer import *
ML390 = Printer(1, "commands.json")
ML390.formFeed() #File name = commands.json
[
{"name":"lineFeed", "command":"x0A"},
{"name":"carriageReturn", "command":"x0D"},
{"name":"formFeed", "command":"x0C"},
{"name":"setLetterQuality", "command":"x1B x78 x32"},
{"name":"setUtility", "command":"x1B x78 x31"}
] #File name = printer.py
import platform #Lib to check os
import json
class Printer(): #Main class
def __init__(self, port, file): #"port" is the parralel port where the printer is pluged in, "file" is the json file containg the custom functions and command to send to the printer
self.os = platform.system() #Returning os in a variable
self.data = json.load(open(file)) #Opening user selected file
if type(port) != int: #Checks if the port is an int
raise Exception("PORT ERROR : Port must be an intreger") #Returns error
if port <= 0: #Checks if the port is the right one
raise Exception("PORT ERROR : Port must be 1 or higher") #returns error
if self.os == "Linux" : #If on Linux
self.port = open("/dev/lp"+str(port-1), "w") #Open as /dev/lpx
elif self.os == "Windows" : #If on Winows
self.port = open("LPT"+str(port), "w") #Open as LPTx
else: #If on oter system
raise Exception("OS ERROR : Unsupported OS") #No supported os error
for n in range(len(self.data)): #Cor all the commands defined in the json
exec(f"""def {self.data[n]["name"]}(self):
self.printText(data[n]["command"])""") #Create a custom function from the json, function name is name in the json
def printText(self, text):
text = str(text)
self.port.write(text)
self.port.flush() When running test.py, I get this error :
Error: Traceback (most recent call last):
File "test.py", line 6, in <module>
ML390.formFeed()
AttributeError: 'Printer' object has no attribute 'formFeed'
I don't know how to declare the functions in the main class, but keep the dynamic function creation when calling the class (__init__).  .
As I would say in my country : "Je suis chiffoné !"
Help me please.
Thank you in advance,
Clément.
Posts: 3,458
Threads: 101
Joined: Sep 2016
You're defining functions that are local to the __init__ method, so they won't be accessible to the class as a whole. That can be fixed, but I'm not sure it should be. Dynamically generating code is pretty sketchy, and you should be very suspicious whenever you see the words "exec" or "eval" in source.
For starters, how are you planning on calling these dynamic functions?
What if one of the functions is "printText"? Would all the others stop working, because they're trying to use a version of printText which no longer exists?
I think it'd make more sense for you to load them as a dict, then have a single method that looks them up. So instead of ML390.formFeed() , you'd do ML390.dispatch("formFeed") . Then that function would look something like: def dispatch(self, key):
if key in self.data:
return self.printText(self.data[key])
raise Exception(f"Invalid key: {key}")
Posts: 12,041
Threads: 487
Joined: Sep 2016
the best way to create a command driven execution is to use a dictionary, here's a simple example:
class SoftwareCommands:
def __init__(self):
self.commands = {
'func_a': self.func_a,
'func_b': self.func_b,
'willy nilly': self.willy_nilly
}
def func_a(self):
print("\nHi, I'm func_a")
def func_b(self):
print("\nHi, I'm func_b")
def willy_nilly(self):
n = 0
while n < 10:
print('n: {}'.format(n))
n += 1
def main():
command = None
sc = SoftwareCommands()
while command != 'quit':
command = input("Enter a command, quit to end: ").strip().lower()
if command in sc.commands:
sc.commands[command]()
if __name__ == '__main__':
main() outout:
Output: Enter a command, quit to end: func_a
Hi, I'm func_a
Enter a command, quit to end: willy nilly
n: 0
n: 1
n: 2
n: 3
n: 4
n: 5
n: 6
n: 7
n: 8
n: 9
Enter a command, quit to end: quit
Posts: 9
Threads: 4
Joined: Jan 2019
Hi nilamo and Larz60+,
Thank you for your help !
So nilmao, you're totally right. I don't know why I haven't done that before, I'm stupid. So i worked on it.
Larz60+, I saw your code, that's not exactly what I was looking for, but nilmao found what I was looking for. Anyway, I keep it in mind, it could be useful to another project.
Anyway, following your idea nilmao, here is what I ended up with :
#NAME = test.py
from printer import *
ML390 = Printer(1, "commands.json")
ML390.command("setUtility") #NAME = commands.py
{
"lineFeed":{"command":"0A", "text":"Line Feed"},
"carriageReturn":{"command":"0D", "text":"Carriage Return"},
"formFeed":{"command":"0C", "text":"formFeed"},
"setLetterQuality":{"command":"1B 78 32", "text":"Set Letter Quality"},
"setUtility":{"command":"1B 78 31", "text":"Set Utility"}
} #NAME = printer.py
import platform #Lib to check os
import json
class Printer(): #Main class
def __init__(self, port, file): #"port" is the parallel port where the printer is plugged in, "file" is the json file containing the custom functions and command to send to the printer
self.os = platform.system() #Returning os in a variable
self.data = json.load(open(file)) #Opening user selected file
if type(port) != int or port <= 0: #Checks if the port is an int
raise Exception(f"Invalid port : {port}") #Returns error
if self.os == "Linux" : #If on Linux
self.port = open("/dev/lp"+str(port-1), "w") #Open as /dev/lpx
elif self.os == "Windows" : #If on Winows
self.port = open("LPT"+str(port), "w") #Open as LPTx
else: #If on oter system
raise Exception(f"Invalid OS : {self.os}") #No supported os error
def printText(self, text):
self.port.write(str(text))
self.port.flush()
def command(self, key): #custom command generator
if key in self.data: #If the key is in the json
commands = self.data[key]["command"] #Extract the command string from the json
print(commands) #TEMPORARY
commands = commands.split(" ") #Split each hex number individually
for n in range(len(commands)): #For each hex number
commands[n] = f"\x{commands[n]}" #add "\x" before
commands = "".join(commands) #Put everything back into a single string
self.printText(commands) #Send the command to the printer
print(self.data[key]["text"]) #TEMPORARY
print(commands) #TEMPORARY
else: #If the key isn't in the json
raise Exception(f"Invalid key: {key}") #Wrong key error The new problem now is that in line 31, (you already know what will happen  ), python tells me that I have to put the hex value after. And if I replace the string by "\\x" or r"\x", when running the command, I the printer litearally prints "\x0A" or "\x0B", or ... (not the quotes).
Should I create an other thread or can you help me now ?
Thanks again (I never say it enough  ),
Clément.
Posts: 3,458
Threads: 101
Joined: Sep 2016
Does this help? >>> code = "1B"
>>> as_int = int(code, base=16)
>>> as_int
27
>>> as_hex = hex(as_int)
>>> as_hex
'0x1b'
Posts: 9
Threads: 4
Joined: Jan 2019
Hi nilamo,
I'm so happy today, you can't believe.
Everything is fixed !
Thank you !
When reading your message, this did remind me a previous message of Larz60+ in this thread https://python-forum.io/Thread-Control-a-dot-matrix-printer. And was showing the use of chr().
So I converted all my hex values into decimal values in the json.
And I changed a bit the lib to use chr()...
And that's all !
Here is the result
#NAME = commands.json
{
"lineFeed":{"command":"10", "text":"Line Feed"},
"carriageReturn":{"command":"13", "text":"Carriage Return"},
"formFeed":{"command":"12", "text":"Form Feed"},
"newLine":{"command":"10 13", "text":"New Line"},
"setLetterQuality":{"command":"27 120 49", "text":"Set Letter Quality"},
"setUtility":{"command":"27 120 48", "text":"Set Utility"},
"fontRoman":{"command":"27 107 48"},
"fontHelvette":{"command":"27 107 49"},
"fontCourrier":{"command":"27 107 50"},
"fontPrestige":{"command":"27 107 51"},
"fontGothic":{"command":"27 107 54"},
"fontBold":{"command":"27 107 55"},
"styleNormal":{"command":"27 113 0"},
"styleOutline":{"command":"27 113 1"},
"styleShadow":{"command":"27 113 2"},
"styleOutlineShadow":{"command":"27 113 3"},
"10Cpi":{"command":"27 80 18"},
"12Cpi":{"command":"27 77 18"},
"15Cpi":{"command":"27 103 18"},
"17Cpi":{"command":"27 80 15"},
"20Cpi":{"command":"27 77 15"},
"propOn":{"command":"27 112 1"},
"propOff":{"command":"27 112 0"},
"doubleOn":{"command":"27 119 49 27 87 49"},
"doubleOff":{"command":"27 119 48 27 87 48"}
} #NAME = printer.py
import platform #Lib to check os
import json
class Printer(): #Main class
def __init__(self, port, file): #"port" is the parralel port where the printer is pluged in, "file" is the json file containg the custom functions and command to send to the printer
self.os = platform.system() #Returning os in a variable
self.data = json.load(open(file)) #Opening user selected file
if type(port) != int or port <= 0: #Checks if the port is an int
raise Exception(f"Invalid port : {port}") #Returns error
if self.os == "Linux" : #If on Linux
self.port = open("/dev/lp"+str(port-1), "w") #Open as /dev/lpx
elif self.os == "Windows" : #If on Winows
self.port = open("LPT"+str(port), "w") #Open as LPTx
else: #If on oter system
raise Exception(f"Invalid OS : {self.os}") #No supported os error
def printText(self, text):
self.port.write(str(text))
self.port.flush()
def command(self, key): #custom command generator
if key in self.data: #If the key is in the json
commands = self.data[key]["command"] #Extract the command string from the json
commands = commands.split(" ") #Split each dec number individually
for n in range(len(commands)): #For each dec number
self.printText(chr(int(commands[n]))) #Send the command to the printer
else: #If the key isn't in the json
raise Exception(f"Invalid key: {key}") #Wrong key error
def sendAscii(decimals):
decimals = decimals.split(" ")
for n in range(len(decimals)):
printText(chr(decimals[n])) #NAME = test.py
from printer import *
ML390 = Printer(1, "commands.json")
ML390.command("cleanPaperBin")
ML390.command("setLetterQuality")
ML390.command("10Cpi")
ML390.command("doubleOn")
ML390.command("underlineOn")
ML390.command("boldOn")
ML390.command("styleOutlineShadow")
ML390.command("fontGothic")
ML390.command("alignCenter")
ML390.command("newLine")
ML390.printText("****************************************\n")
ML390.printText("Thank you nilamo and Larz60+ !\n\n")
ML390.printText("You solved all my problems...\n\n")
ML390.command("newLine")
ML390.command("newLine")
ML390.command("alignLeft")
ML390.command("20Cpi")
ML390.printText("...at least for now ;-p...\n\n")
ML390.command("10Cpi")
ML390.command("alignCenter")
ML390.printText("****************************************\n\n")
ML390.command("formFeed")
ML390.command("reset") [Image: printer1.jpg]
[Image: printer2.jpg]
I'm so glad it works !
Now the GUI is left to do !
Thanks again,
Clément.
|