I'm assuming that this is the part that you get to after selecting whether you are a user or a driver. If we stick with the example that I gave you in your other post:
new config.py
from dataclasses import dataclass, asdict
#to make things more complicated and force you to get better
#create a dataclass of default properties
@dataclass
class Button_dc:
bg: str = "white"
fg: str = "gray20"
font: str = 'Calibri 14 bold'
border:int = 10
#make a dict instance of the dataclass
DefaultButton = asdict(Button_dc())
#make a slightly different instance
UserRoleButton = asdict(Button_dc(font='Calibri 24 bold'))
@dataclass
class Label_dc:
bg: str = "steel blue"
fg: str = "gray20"
font: str = 'Calibri 14 bold'
DefaultLabel = asdict(Label_dc())
@dataclass
class Entry_dc:
bg: str = "white"
fg: str = "gray20"
font: str = 'Calibri 14 bold'
width: int = 30
DefaultEntry = asdict(Entry_dc())
@dataclass
class Message_dc:
bg: str = "red"
fg: str = "white"
font: str = 'Calibri 14 bold'
width: int = 150
DefaultMessage = asdict(Message_dc())
In main.py select the entire user_form
method and paste the entire below in its place
def user_form(self, type, fname=None, lname=None):
self.canvas.delete("all")
self.canvas.create_window(10, 10, anchor='nw', window=tk.Label(self, text="First Name:", **cfg.DefaultLabel))
fname_ent = tk.Entry(self, **cfg.DefaultEntry)
if fname != None:
fname_ent.insert('end', fname)
self.canvas.create_window(120, 10, anchor='nw', window=fname_ent)
self.canvas.create_window(10, 40, anchor='nw', window=tk.Label(self, text="Last Name:", **cfg.DefaultLabel))
lname_ent = tk.Entry(self, **cfg.DefaultEntry)
if lname != None:
lname_ent.insert('end', lname)
self.canvas.create_window(120, 40, anchor='nw', window=lname_ent)
#don't send the entire Entry to the check ~ only the data you want to check
form_next_btn = tk.Button(self, text="next", command=lambda: self.check(type, fname_ent.get(), lname_ent.get()), **cfg.DefaultButton)
self.fnb = self.canvas.create_window(500, 340, anchor='nw', window=form_next_btn)
def check(self, type, fname, lname):
self.canvas.delete(self.fnb)
f = fname.isalpha()
l = lname.isalpha()
if f and l:
self.process(type, fname, lname)
return
if not f:
errormsg = tk.Message(self, text='Invalid entry for first name', **cfg.DefaultMessage)
self.canvas.create_window(10, 90, anchor='nw', window=errormsg)
if not l:
errormsg2 = tk.Message(self, text='Invalid entry for last name', **cfg.DefaultMessage)
self.canvas.create_window(10 if f else 170, 90, anchor='nw', window=errormsg2)
back_btn = tk.Button(self, text="back", command=lambda: self.user_form(type, fname if f else None, lname if l else None), **cfg.DefaultButton)
self.canvas.create_window(500, 340, anchor='nw', window=back_btn)
def process(self, type, fname, lname):
self.canvas.delete("all")
if type is Role.User:
print(f'{fname} {lname} is a user')
elif type is Role.Driver:
print(f'{fname} {lname} is a driver')
aside: Keep in mind that I am not trying, at all, to make your app pretty. I'm just showing you how to make it work. I'm also not writing my best code. I would do all of this completely different. I'm mushing around with your canvas
method and giving you something with a modicum of sanity to it. It seems to me that the entire reason you are using canvas is just so you can have a rounded rectangle. It hardly seems worth it, but that isn't my decision to make.
However, these concepts do not suck and can be very powerful in the proper context. Lets take the dataclasses
, for instance. Imagine if you actually wanted to do the below. You end up with a fully customized Text
widget and a very simple way to change only what you need to change while maintaining everything else you assigned. The dataclasses
that I wrote for you are little example concepts. You can bring those concepts much further.
@dataclass
class Text_dc:
background: str = Theme.Topcoat # main element
foreground: str = Theme.Flat
borderwidth: int = 0
selectbackground: str = Theme.Primer # selected text
selectforeground: str = Theme.Accent
selectborderwidth: int = 0 # doesn't seem to do anything ~ supposed to be a border on selected text
insertbackground: str = Theme.Hilight # caret
insertborderwidth: int = 0 # border
insertofftime: int = 300 # blink off millis
insertontime: int = 600 # blink on millis
insertwidth: int = 2 # width
highlightbackground:int = Theme.Topcoat # inner border that is activated when the widget gets focus
highlightcolor: int = Theme.Topcoat # color
highlightthickness: int = 0 # thickness
cursor: str = 'xterm'
exportselection: int = 1
font: str = 'calibri 14'
width: int = 16 # characters ~ often this is ignored as Text gets stretched to fill its parent
height: int = 8 # lines ~ " " " " "
padx: int = 2
pady: int = 0
relief: str = 'flat'
wrap: str = 'word' # "none", 'word'
spacing1: int = 0 # space above every line
spacing2: int = 0 # space between every wrapped line
spacing3: int = 0 # space after return from wrap (paragraph)
state: str = 'normal' # NORMAL=Read/Write | DISABLED=ReadOnly
tabs: str = '4c'
takefocus: int = 1
undo: bool = True
xscrollcommand: Callable = None
yscrollcommand: Callable = None
#common difference
NoWrap = asdict(Text_dc(**{'wrap':'none'}))
NWReadOnly = asdict(Text_dc(**{'wrap':'none','state':'disabled'}))
ReadOnly = asdict(Text_dc(**{'state':'disabled'}))
DefaultText = asdict(Text_dc())
You can use that concept in a different way. Let's assume that you wanted a completely custom Text
widget and you intended to do more than just restyle it. The below is a quick and dirty example
class ScriptEditor(tk.Text):
def __init__(self, master, **kwargs):
tk.Text.__init__(self, master, **asdict(Text_dc(font='Consolas 14', **kwargs)))
def syntax_highlight(self):
pass
#all the other **kwargs are defaulted internally,
#but we want to adjust the size for this instance
editor = ScriptEditor(root, width=120, height=80)