Home > Software design >  How can I get my separate GUI module to run to completion within my current main module?
How can I get my separate GUI module to run to completion within my current main module?

Time:10-26

I am working on a python application for construction layout work. It basically contains points and should eventually allow you to get information and calculations from those points. One of the functions should upload points from a text file using a separate module and if any of the points are already saved, ask the user to input a new point name and then continue asking as long as they continue not entering a unique point name. My program works for the duplicate point but then won't display the popup window from the second GUI for the second duplicate point until you exit out of the main window. I have spent hours redoing this and I just can't figure it out. I very much appreciate any and all help as well as any criticism. I'm new to this so I'm sure it's all types of messed up.

Main Gui code:

import tkinter as tk
import point_importer
import csv
from tkinter import Toplevel, ttk, TOP, BOTTOM
from tkinter import filedialog as fd
from point_importer import import_points 
from datetime import datetime
import tkinter.font as font
import pickle


filename = ''
current_azimuth = 0
point_dict = {}

class App(tk.Tk):
    def __init__(self):

        super().__init__()
        self.title('Survey Amateur')
        self.state('zoomed')
        self.resizable(False, False)

        self.columnconfigure((0,1), weight=1, uniform='column')
        self.rowconfigure(2, weight=0)

        self.main_buttons()
    
    def clear_window(self):
        for widget in self.winfo_children():
            widget.destroy()

    def main_buttons(self):
        self.clear_window()

        label_text = ttk.Label(self, text='Survey Amateur', font = ('helvetica', 44))
        label_text.grid(column=0, row=0, padx=10, pady=50, ipadx=0, columnspan=2)

        file_button = tk.Button(self, text='File', font = ('helvetica', 30), bg='grey82')
        file_button['command'] = self.file_window
        file_button.grid(column=0, row=2, padx=10, pady=50, ipadx=98, sticky='N')

        setting_button = tk.Button(self, text='Settings', font=('helvetica', 30), bg='grey82')
        setting_button.grid(column=1, row=2, padx=10, pady=50, ipadx=48, sticky='N')

        backsite_button = tk.Button(self, text='Set Backsite', font=('helvetica', 30), bg='grey82')
        backsite_button['command'] = self.backsite_window
        backsite_button.grid(column=0, row=3, padx=10, pady=50, sticky='N')

        stakeout_button = tk.Button(self, text='Stakeout', font=('helvetica', 30), bg='grey82')
        stakeout_button.grid(column=1, row=3, padx=10, pady=50, ipadx=40, sticky='N')

        empty_label = ttk.Label(self, text='', font='helvetica36')
        empty_label.grid(column=0, row=4, columnspan=2)
    
    def file_window(self):
        self.clear_window()

        label_text = ttk.Label(self, text='File', font=('helvetica', 44))
        label_text.grid(column=0, row=0, padx=10, pady=50, ipadx=0, columnspan=2)

        back_button = tk.Button(self, text='Back', font=('helvetica', 10))
        back_button['command'] = self.main_buttons
        back_button.grid(column=0, row=1, padx=10, pady=50, ipadx=0, sticky='N')

        open_button = tk.Button(self, text='Open', font=('helvetica', 30), bg='grey82')
        open_button['command'] = self.open_file
        open_button.grid(column=1, row=2, padx=10, pady=50, ipadx=0, sticky='N')

        new_button = tk.Button(self, text='New', font=('helvetica', 30), bg='grey82')
        new_button['command'] = self.new_file
        new_button.grid(column=0, row=2, padx=10, pady=50, sticky='N')

        export_button = tk.Button(self, text='Export', font=('helvetica', 30), bg='grey82')
        export_button.grid(column=1, row=3, padx=10, pady=50, sticky='N')

        import_button = tk.Button(self, text='Import', font=('helvetica', 30), bg='grey82')
        import_button['command'] = self.import_points
        import_button.grid(column=0, row=3, padx=10, pady=50, ipadx=0, sticky='N')

        empty_label = ttk.Label(self, text='', font='helvetica36')
        empty_label.grid(column=0, row=5, columnspan=2)

    def backsite_window(self):
        occupy_point = ''
        foresite_point = ''

        self.clear_window()

        label_text = ttk.Label(self, text='Set Backsite', font=('helvetica', 44))
        label_text.grid(column=0, row=0, padx=10, pady=50, ipadx=0, columnspan=2)

        Occupy_point_entry = ttk.Entry(self, textvariable=occupy_point, text='Back', font=('helvetica', 10))
        Occupy_point_entry.grid(column=0, row=1, padx=10, pady=50, ipadx=0, sticky='N')

        foresite_point_entry = ttk.Entry(self, textvariable=foresite_point, text='Open', font=('helvetica', 30), bg='grey82')
        foresite_point_entry.grid(column=1, row=1, padx=10, pady=50, ipadx=0, sticky='N')

        ts_height_entry = ttk.Entry(self, text='New', font=('helvetica', 30), bg='grey82')
        ts_height_entry.grid(column=0, row=2, padx=10, pady=50, sticky='N')

        rod_height_entry = ttk.Entry(self, text='New', font=('helvetica', 30), bg='grey82')
        rod_height_entry.grid(column=1, row=2, padx=10, pady=50, sticky='N')

        empty_label = ttk.Label(self, text='', font='helvetica36')
        empty_label.grid(column=0, row=5, columnspan=2)

    def open_file(self):
        global filename 
        filename = fd.askopenfilename(
            initialdir=r'C:\Users\Tim\Desktop\Code\Python\Surveying_Program',
            title='Browse',
            filetype = (('text files', '*.txt'), ("all files","*.*"))
        )

    def save_file(self):
        with open(filename, 'wb') as f:
            pickle.dump(point_dict, f)
    
    def new_file(self):
        global filename 
        filename = fd.asksaveasfilename(
            initialdir=r'C:\Users\Tim\Desktop\Code\Python\Surveying_Program',
            title = 'Save As',
            filetype = (('text files', '*.txt'), ("all files","*.*"))
        )
        open(filename   '.txt', 'w')
        self.save_file()

    def import_points(self):
        global point_dict
        import_file = fd.asksaveasfilename(
            initialdir=r'C:\Users\Tim\Desktop\Code\Python\Surveying_Program',
            title = 'Import File',
            filetype = (('text files', '*.txt'), ("all files","*.*"))
        )

        point_dict = point_importer.import_points(import_file, point_dict)
        print(point_dict)
        self.save_file()


if __name__ == "__main__":
    
    app = App()
    app.mainloop()

Separate point uploader module:

import tkinter as tk
import csv
from datetime import datetime
from tkinter import BOTTOM, TOP, ttk


def get_new_point_name(old_point_name):
    global entry_box
    global root
    
    root = tk.Tk()
    root.title('Error')
    window_width = 600
    window_height = 300
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    center_x = int(screen_width/2 - window_width / 2)
    center_y = int(screen_height/2 - window_height / 2)
    root.geometry(f'{window_width}x{window_height} {center_x} {center_y}')

    label_text = f'There is already a point with the name {old_point_name}. Please enter a new point name:'
    new_point_label = tk.Label(root, text=label_text, font = ('helvetica', 10))
    new_point_label.grid(row=0, column=0, ipadx=10, ipady=10)
    
    entry_box = tk.Entry(root)
    entry_box.grid(row=1, column=0, ipadx=10, ipady=10)

    enter_button = tk.Button(root, text='Enter', command=get_entry)
    enter_button.grid(row=2, column=0, ipadx=10, ipady=10)

    root.mainloop()

def get_entry():
    global new_point_name
    new_point_name = entry_box.get()
    if new_point_name in my_dict.keys():
        #root.destroy()
        get_new_point_name(new_point_name)
    else:
        root.destroy()
        return new_point_name

def import_points(file_location, point_dict):
    global my_dict
    my_dict = point_dict
    now = datetime.now()
    with open(file_location, newline='') as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        point_list = list(csv_reader)
        for item in point_list:
            if item[0] in point_dict.keys():
                get_new_point_name(item[0])
                print(new_point_name)
                item[0] = new_point_name

            point_info_dict = {}
            point_info_dict['northing'] = float(item[1])
            point_info_dict['easting'] = float(item[2])
            point_info_dict['elevation'] = float(item[3])
            point_info_dict['description'] = item[4]
            point_info_dict['time'] = now.strftime('%H:%M:%S')
            point_info_dict['date'] = now.strftime('%m/%d/%Y')

            point_dict[item[0]] = point_info_dict
    return point_dict 

CodePudding user response:

First of all some general hints:

  • import just what you need
  • when you intend to use the whole module import it once and stick to the way you imported it.
  • take a look at PEP 8 Style Guide
    • line length and how to split lines of codes for example
  • group logical blocks of code, such as construction and layout
  • when writing the same code in the same codeblock multiple times, be aware:

There must be a better way!

  • Same applies mostly if something get super complicated, like ipadx
  • Don't delete and construct your widgets every time new, rather unmap and map them:
    • pack_forget or grid_forget can help you solve it, frames also help.

main.py

import tkinter as tk
import point_importer

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title('Survey Amateur')
        self.state('zoomed')
        self.resizable(False, False)
        btncon = {
            'font'  : ('helvetica', 30),
            'bg'    : 'grey82',
            'width' : 11}
        btnlay = {
            'padx'  : 10,
            'pady'  : 50,
            'sticky': 'N'}
        #construction
        file_button = tk.Button(
            self, text='File', command=self.import_points, **btncon)
        #layout
        file_button.grid(
            column=0, row=2, **btnlay)

    def import_points(self):
        point_dict = point_importer.get_new_point_name('path', dict())

if __name__ == "__main__":
    app = App()
    app.mainloop()

point_importer.py

import tkinter as tk

def get_new_point_name(path, cnf):
    root = tk.Toplevel()
    cnf.update(
        {'x' : 100,
         'y' : 100
         })
    return cnf

Explanation why this code works and yours don't:

When you trigger your function import_points, you create a new instance of Tk. Tk is not just a widget, it is a special toplevel that comes with the tcl interpreter and the root of all decedents that will be created by this interpreter. In order to receive input from the user and to send requests to the window-manager of your operating system the interpreter runs an eventloop till either the user, the program or the window-manager decides the session needs to end.

TL;DR:

Therefore mainloop as the eventloop is the point of no-return till the session has ended. If you need an additional window, use a Toplevel instead.

  • Related