Posts: 7
Threads: 2
Joined: Apr 2024
Jun-27-2024, 10:41 PM
(This post was last modified: Jun-27-2024, 10:41 PM by darter1010.)
I have a working non-GUI python script to do some scraping and showing me some financial data and it is working and I trust it so I would like to add on a recently written GUI section in a way that keeps the GUI somewhat apart from my 'logic part'. I was wondering if I can run the GUI part and just pipe in the results and then let my tested logic work on that. Maybe later I can integrate these two concerns but any ideas on a way to do this while keeping the code mostly apart? The GUI part has only a small role - to generate about eight strings for financial accounts. So I could run the GUI part and then let the other code take over without integrating the code too much? Thanks.
Posts: 12,046
Threads: 487
Joined: Sep 2016
Posts: 7
Threads: 2
Joined: Apr 2024
Jul-02-2024, 01:15 AM
(This post was last modified: Jul-02-2024, 01:17 AM by darter1010.)
Yes I'll work on that but my quick solution was just to have the first script call the second. It's okay for now.
s1= ' '.join(final_list) # the bank arguments
some_command="python3 "+f_path +" "+ s1
subprocess.run(some_command, shell=True)
Posts: 38
Threads: 0
Joined: Jun 2024
You can definitely keep the GUI and logic separate. One way to do this is to have your GUI collect the data and then write those eight strings to a file or pass them via standard input/output. Your logic script can then read the data from the file or standard input and process it as usual. This keeps your code modular and makes future integration easier. You could also look into using Python’s subprocess module to run the GUI and pipe the results to your main script.
Posts: 1,145
Threads: 114
Joined: Sep 2019
Jul-09-2024, 06:28 PM
(This post was last modified: Jul-09-2024, 06:28 PM by menator01.)
I usually take the mvc approach when keeping data manipulation separate from the gui view.
Example
import tkinter as tk
from random import randint
class Model:
''' The Model class handles all data manipulations '''
def __init__(self):
pass
def add(self, args):
''' Add data to database or other storage '''
return args
def edit(self, id):
''' Edit the data by id '''
return f'Record for ID {id} has been edited.'
def delete(self, id):
''' Delete a record by id '''
return f'Record for ID {id} has been deleted.'
def getone(self, id):
''' Pull a data record by id '''
return f'Retrieved record for ID {id}.'
def getall(self):
''' Pull all data records '''
return 'All records have been retrieved'
class View:
''' The View class handles presentation display '''
def __init__(self, parent):
self.parent = parent
self.parent.columnconfigure(0, weight=1)
self.parent.rowconfigure(0, weight=1)
self.text = 'Dynamic Text:'
container = tk.Frame(self.parent)
container.grid(column=0, row=0, sticky='news', padx=4, pady=4)
container.grid_columnconfigure(0, weight=3)
label = tk.Label(container, text='MVC Approach', font=(None, 24, 'bold'))
label.grid(column=0, row=0, sticky='new')
self.text_label = tk.Label(container, text=self.text, anchor='w', padx=4, pady=4)
self.text_label['relief'] = 'ridge'
self.text_label.grid(column=0, row=1, sticky='new', pady=8)
self.buttons = []
self.btns = ('Add', 'Edit', 'Delete', 'Get Record', 'Get All Records', 'Reset Text','Exit')
btn_container = tk.Frame(container)
btn_container.grid(column=0, row=2, sticky='new', padx=4, pady=4)
for i in range(len(self.btns)):
btn_container.grid_columnconfigure(i, weight=3, uniform='buttons')
for index, button in enumerate(self.btns):
bg_color = 'tomato' if button == 'Exit' else 'lightgray'
hover_color = 'red' if button == 'Exit' else 'whitesmoke'
self.buttons.append(tk.Button(btn_container, bg=bg_color, text=button, cursor='hand2'))
self.buttons[index]['activebackground'] = hover_color
self.buttons[index].grid(column=index, row=0, sticky='news', padx=4, pady=4)
class Controller:
''' Controller class handles communications between the View and Model classes '''
def __init__(self, model, view):
self.model = model
self.view = view
self.text = self.view.text
# Button commands
commands = [self.add, self.edit, self.delete, self.getone, self.getall, self.reset, self.view.parent.destroy]
for index, button in enumerate(self.view.buttons):
self.view.buttons[index]['command'] = commands[index]
# Methods for button commands
def add(self, data='Data has been added to database.'):
self.view.text_label.configure(text = f'{self.text} {self.model.add(data)}')
def edit(self):
self.view.text_label.configure(text = f'{self.text} {self.model.edit(self.randomnumber())}')
def delete(self):
self.view.text_label.configure(text = f'{self.text} {self.model.delete(self.randomnumber())}')
def getone(self):
self.view.text_label.configure(text = f'{self.text} {self.model.getone(self.randomnumber())}')
def getall(self):
self.view.text_label.configure(text = f'{self.text} {self.model.getall()}')
def reset(self):
self.view.text_label.configure(text=self.view.text)
def randomnumber(self):
return randint(1, 500)
if __name__ == '__main__':
root = tk.Tk()
controller = Controller(Model(), View(root))
root.mainloop()
Posts: 7
Threads: 2
Joined: Apr 2024
Okay I used your code as a guide. Very helpful, thanks! I wanted a root window, a big frame and then three sub-frames (a,b,c)to place various widgets in. To test it out I have a label updated by the current date and also a quit button. So I will keep studying your code! (I tried to simplify this a lot so I could see what is happening - it takes me a while to work it out).
import sys
# ~ sys.path.append('/home/whe67/TkinCode/')
# ~ from settings01 import *
import tkinter as tk
from random import randint
import datetime
SOME_FONT_1="Helvetica 20"
class Model:
''' The Model class handles all data manipulations '''
def __init__(self):
pass
def add(self):
print ('in add method now')
#print (args)
# print (view.string_variable.get())
def show_date(self):
some_date_now= datetime.datetime.now()
print ('date is: ',some_date_now)
#view.label_two.configure(text= some_date_now)
return some_date_now
def goodbye(self):
print ("bye from Model")
root_window.quit()
#view.parent.quit #not sure why this works or parent
class View:
''' The View class handles presentation display '''
# ~ tk.LabelFrame.__init__(parent,width=750, height=700, cursor='dot',relief="raised",bg='blue' ,text='THIS FRAME HOLDS SUB-FRAMES') #
def __init__(self, parent):
self.parent = parent
self.parent.title("Title for root window")
self.text = 'this text was assigned to self.text in the View class'
parent.configure(bg='lightblue') #our root window background can be set - it has been named parent (so root ->parent in constructor above ) since is will be the parent to frame_one
self.frame_one = tk.LabelFrame(self.parent,text="FRAME this is a LabelFrame named frame_one",width=580, height=160,bg='cyan2',cursor='dot' )
self.frame_one.grid() #this is the big frame that will hold other frames
self.button_list=[]
self.create_more_widgets()
def create_more_widgets(self):
''' this creates some sub frames that will go into frame_one and puts buttons and other widgets into those sub-frames '''
# create two sub frames
self.frame_a =tk.LabelFrame(self.frame_one, text="Frame a",width=580, height=160, bg="lightblue",
fg="white", padx=15, pady=15)
self.frame_a.grid(row=1,column=0)
self.frame_b =tk.LabelFrame(self.frame_one, text="Frame b",width=680, height=160, bg="purple",
fg="white", padx=15, pady=15)
self.frame_b.grid(row=0,column=0)
self.frame_c =tk.LabelFrame(self.frame_one, text="Frame c",width=680, height=160, bg="teal",
fg="white", padx=15, pady=15)
self.frame_c.grid(row=2,column=0)
# create buttons - register them with the grid and also append them to a button_list maybe for later
self.button_one = tk.Button(self.frame_a, text="b1 using Model add")
self.button_one.grid(row=0,column=0)
self.button_list.append(self.button_one)
self.button_two = tk.Button(self.frame_a, text="b2 : view.parent.quit")
self.button_two.grid(row=0,column=1)
self.button_list.append(self.button_two)
self.button_three = tk.Button(self.frame_a, text="b3 : show date")
self.button_three.grid(row=0,column=2)
self.button_list.append(self.button_three)
#create labels
self.label_one= tk.Label (self.frame_b, text= "SELECTO-MATIC",bg='blue',fg='black',font = SOME_FONT_1)
self.label_one.grid(row=0, column=0)
string_variable = tk.StringVar(self.frame_b, "Hello Everyone!!")
self.label_two = tk.Label(self.frame_b, text=string_variable.get(), font = SOME_FONT_1,pady=10)
self.label_two.grid(row=1,column=0)
class Controller:
''' Controller class handles communications between the View and Model classes '''
def __init__(self, model, view):
self.model = model
self.view = view
self.text = self.view.text
print ('in Controller constructor we have (from the View)', self.text)
# Button commands
commands = [self.control_add, self.control_goodbye,self.control_show_date]
self.view.button_one.configure(command= commands[0])
self.view.button_two.configure(command= commands[1])
self.view.button_three.configure(command= commands[2])
# Methods for button commands
def control_add(self, data='abcdefgh'):
print ('now at add method for Controller')
self.view.label_two.configure(text = 'xyzaaa')
def control_show_date(self):
self.returned_val = self.model.show_date()
print ("returned:", self.returned_val)
self.view.label_two.configure(text = self.returned_val)
def control_goodbye(self):
print ('flow moving from controller to model where quit happens')
self.model.goodbye()
if __name__ == '__main__':
root_window = tk.Tk()
controller = Controller(Model(), View(root_window)) #so the View class needs the root window reference so it can make a window - then feed it - the View class- into the controller
root_window.mainloop()
menator01 likes this post
Attached Files
Thumbnail(s)
Posts: 12,046
Threads: 487
Joined: Sep 2016
FYI:
This is something to take a look at.
You can create some very attractive windows, with all of them using new ttk with styles.
ttkbootstrap
GitHub
There are predefined themes, Light and Dark.
Has built in Creator
An example that uses multiple frames
Example:
customtkinter
showcase
Example which uses multiple frames
I have tried both with very good results, and currently working on a project using ttkbootstrap.
Both packages have merits.
You can recreate all of this using tkinter ttk (new) or save a lot of time using one of the packages available.
Even if you don't use ither of the above, you can look at the source code to get some ideas.
menator01 likes this post
Posts: 7
Threads: 2
Joined: Apr 2024
(Jul-09-2024, 06:28 PM)menator01 Wrote: I usually take the mvc approach when keeping data manipulation separate from the gui view.
Example
import tkinter as tk
from random import randint
class Model:
''' The Model class handles all data manipulations '''
def __init__(self):
pass
def add(self, args):
''' Add data to database or other storage '''
return args
def edit(self, id):
''' Edit the data by id '''
return f'Record for ID {id} has been edited.'
def delete(self, id):
''' Delete a record by id '''
return f'Record for ID {id} has been deleted.'
def getone(self, id):
''' Pull a data record by id '''
return f'Retrieved record for ID {id}.'
def getall(self):
''' Pull all data records '''
return 'All records have been retrieved'
class View:
''' The View class handles presentation display '''
def __init__(self, parent):
self.parent = parent
self.parent.columnconfigure(0, weight=1)
self.parent.rowconfigure(0, weight=1)
self.text = 'Dynamic Text:'
container = tk.Frame(self.parent)
container.grid(column=0, row=0, sticky='news', padx=4, pady=4)
container.grid_columnconfigure(0, weight=3)
label = tk.Label(container, text='MVC Approach', font=(None, 24, 'bold'))
label.grid(column=0, row=0, sticky='new')
self.text_label = tk.Label(container, text=self.text, anchor='w', padx=4, pady=4)
self.text_label['relief'] = 'ridge'
self.text_label.grid(column=0, row=1, sticky='new', pady=8)
self.buttons = []
self.btns = ('Add', 'Edit', 'Delete', 'Get Record', 'Get All Records', 'Reset Text','Exit')
btn_container = tk.Frame(container)
btn_container.grid(column=0, row=2, sticky='new', padx=4, pady=4)
for i in range(len(self.btns)):
btn_container.grid_columnconfigure(i, weight=3, uniform='buttons')
for index, button in enumerate(self.btns):
bg_color = 'tomato' if button == 'Exit' else 'lightgray'
hover_color = 'red' if button == 'Exit' else 'whitesmoke'
self.buttons.append(tk.Button(btn_container, bg=bg_color, text=button, cursor='hand2'))
self.buttons[index]['activebackground'] = hover_color
self.buttons[index].grid(column=index, row=0, sticky='news', padx=4, pady=4)
class Controller:
''' Controller class handles communications between the View and Model classes '''
def __init__(self, model, view):
self.model = model
self.view = view
self.text = self.view.text
# Button commands
commands = [self.add, self.edit, self.delete, self.getone, self.getall, self.reset, self.view.parent.destroy]
for index, button in enumerate(self.view.buttons):
self.view.buttons[index]['command'] = commands[index]
# Methods for button commands
def add(self, data='Data has been added to database.'):
self.view.text_label.configure(text = f'{self.text} {self.model.add(data)}')
def edit(self):
self.view.text_label.configure(text = f'{self.text} {self.model.edit(self.randomnumber())}')
def delete(self):
self.view.text_label.configure(text = f'{self.text} {self.model.delete(self.randomnumber())}')
def getone(self):
self.view.text_label.configure(text = f'{self.text} {self.model.getone(self.randomnumber())}')
def getall(self):
self.view.text_label.configure(text = f'{self.text} {self.model.getall()}')
def reset(self):
self.view.text_label.configure(text=self.view.text)
def randomnumber(self):
return randint(1, 500)
if __name__ == '__main__':
root = tk.Tk()
controller = Controller(Model(), View(root))
root.mainloop()
Thanks again. I am using the recommended framework and now am stuck on how to have a label get continuously updated by the current time. Just from reading other web pages it looks like the below code that I am using might help but it only shows the time at the onset. Just once.
if __name__ == '__main__':
root_window = tk.Tk()
controller = Controller(Model(), View(root_window)) #so the View class needs the root window reference so it can make a window - then feed it - the View class- into the controller
root_window.after(100, controller.control_show_time )
root_window.mainloop() My code has the controller calling the model and it does seem to work but not continuously.So I am wondering what the approach should be for this. Thanks.
Posts: 7
Threads: 2
Joined: Apr 2024
(Jul-11-2024, 10:55 AM)Larz60+ Wrote: FYI:
This is something to take a look at.
You can create some very attractive windows, with all of them using new ttk with styles.
ttkbootstrap
GitHub
There are predefined themes, Light and Dark.
Has built in Creator
An example that uses multiple frames
Example:
customtkinter
showcase
Example which uses multiple frames
I have tried both with very good results, and currently working on a project using ttkbootstrap.
Both packages have merits.
You can recreate all of this using tkinter ttk (new) or save a lot of time using one of the packages available.
Even if you don't use ither of the above, you can look at the source code to get some ideas.
The examples look great. Thanks for the tip!
Posts: 1,145
Threads: 114
Joined: Sep 2019
Jul-15-2024, 03:02 AM
(This post was last modified: Jul-15-2024, 03:02 AM by menator01.)
You will need to call the root window twice for continuous update.
I've added the code to my example
import tkinter as tk
from random import randint
from datetime import datetime
class Model:
''' The Model class handles all data manipulations '''
def __init__(self):
pass
def add(self, args):
''' Add data to database or other storage '''
return args
def edit(self, id):
''' Edit the data by id '''
return f'Record for ID {id} has been edited.'
def delete(self, id):
''' Delete a record by id '''
return f'Record for ID {id} has been deleted.'
def getone(self, id):
''' Pull a data record by id '''
return f'Retrieved record for ID {id}.'
def getall(self):
''' Pull all data records '''
return 'All records have been retrieved'
class View:
''' The View class handles presentation display '''
def __init__(self, parent):
self.parent = parent
self.parent.columnconfigure(0, weight=1)
self.parent.rowconfigure(0, weight=1)
self.text = 'Dynamic Text:'
container = tk.Frame(self.parent)
container.grid(column=0, row=0, sticky='news', padx=4, pady=4)
container.grid_columnconfigure(0, weight=3)
label = tk.Label(container, text='MVC Approach', font=(None, 24, 'bold'))
label.grid(column=0, row=0, sticky='new')
'''
Container frame to hold tow labels. Normally would probably do different
depending on the project but, this is just an add on example
'''
frame_it = tk.Frame(container)
frame_it.grid(column=0, row=1, sticky='new', padx=4, pady=4)
frame_it.grid_columnconfigure(0, weight=2)
frame_it.grid_columnconfigure(1)
self.text_label = tk.Label(frame_it, text=self.text, anchor='w', padx=4, pady=4)
self.text_label.grid(column=0, row=0, sticky='new', padx=(4,2))
self.text_label.configure(
highlightbackground='#333333', highlightcolor='#333333',
highlightthickness=1,
)
self.clock_label = tk.Label(frame_it, padx=4, pady=4, bg='black', fg='lime')
self.clock_label.grid(column=1, row=0, sticky='new', padx=(2,4))
self.clock_label.configure(
highlightbackground='#333333', highlightcolor='#333333',
highlightthickness=1, font=('tahoma', 10, 'bold')
)
self.buttons = []
self.btns = ('Add', 'Edit', 'Delete', 'Get Record', 'Get All Records', 'Reset Text','Exit')
btn_container = tk.Frame(container)
btn_container.grid(column=0, row=2, sticky='new', padx=4, pady=4)
for i in range(len(self.btns)):
btn_container.grid_columnconfigure(i, weight=3, uniform='buttons')
for index, button in enumerate(self.btns):
bg_color = 'tomato' if button == 'Exit' else 'lightgray'
hover_color = 'red' if button == 'Exit' else 'whitesmoke'
self.buttons.append(tk.Button(btn_container, bg=bg_color, text=button, cursor='hand2'))
self.buttons[index]['activebackground'] = hover_color
self.buttons[index].grid(column=index, row=0, sticky='news', padx=4, pady=4)
class Controller:
''' Controller class handles communications between the View and Model classes '''
def __init__(self, model, view):
self.model = model
self.view = view
self.text = self.view.text
# Call clock for first load
self.clock()
# Call self.view parent.after to get it started (This is the root window)
self.view.parent.after(1000, self.clock)
# Button commands
commands = [self.add, self.edit, self.delete, self.getone, self.getall, self.reset, self.view.parent.destroy]
for index, button in enumerate(self.view.buttons):
self.view.buttons[index]['command'] = commands[index]
# Methods for button commands
def add(self, data='Data has been added to database.'):
self.view.text_label.configure(text = f'{self.text} {self.model.add(data)}')
def edit(self):
self.view.text_label.configure(text = f'{self.text} {self.model.edit(self.randomnumber())}')
def delete(self):
self.view.text_label.configure(text = f'{self.text} {self.model.delete(self.randomnumber())}')
def getone(self):
self.view.text_label.configure(text = f'{self.text} {self.model.getone(self.randomnumber())}')
def getall(self):
self.view.text_label.configure(text = f'{self.text} {self.model.getall()}')
def reset(self):
self.view.text_label.configure(text=self.view.text)
def randomnumber(self):
return randint(1, 500)
def clock(self):
'''
Method creates and formats the time %H 24 hour format %I 12 hour format
self.view.clock_label.configure updates the label
self.view parent.after(1000, self.clock calls repeats the call every second)
'''
time = datetime.now().strftime('%I:%M:%S %p')
self.view.clock_label.configure(text=f'Current Time {time}')
self.view.parent.after(1000, self.clock)
if __name__ == '__main__':
root = tk.Tk()
controller = Controller(Model(), View(root))
root.mainloop()
|