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
orgrid_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.