Python Forum
Connecting GUI code with working logic code
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Connecting GUI code with working logic code
#1
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.
Reply
#2
import?
Reply
#3
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)
Reply
#4
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.
Reply
#5
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()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#6
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)
   
Reply
#7
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
Reply
#8
(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.
Reply
#9
(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!
Reply
#10
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()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Arduino Code to Python Code boogeyman1 3 3,848 Jan-07-2022, 11:36 AM
Last Post: Larz60+
  Tk .set not working in parts of code User01 2 2,752 Feb-13-2021, 03:39 PM
Last Post: deanhystad
  [PyQt] how to transform pyqt4 code to pyqt5 code atlass218 4 6,493 Jan-20-2020, 05:45 PM
Last Post: Axel_Erfurt
  Code not working Luzaich 2 3,696 Sep-03-2018, 04:37 PM
Last Post: Gribouillis
  [Tkinter] my simple window code is not working antonmichel 8 18,454 Jan-29-2018, 06:24 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020