'_tkinter.tkapp' object has no attribute 'result' Error - python

I'm creating a program that displays a graph of real estate in cities in the States. I was able to create a main function, which mainly concerns creating a graph of each city.
Now, I am trying to add a new feature, which lets users choose one city among many options by creating a combobox.
What I want to make is basically just allows users to click one option among many cities in combobox, and when users click it, it should automatically call the main function so that the main function can generate the selected graph.
I am using tkinter and Custom Tkinter modules for my GUI design.
Code:
#Libraries
(...) # This is for graphing features
#Tkinter
from tkinter import *
import tkinter as tk
from PIL import ImageTk, Image
import customtkinter as ctk
import requests
import tkinter.messagebox
ctk.set_appearance_mode("Light")
ctk.set_default_color_theme("dark-blue")
class App(ctk.CTk,tk.Tk):
WIDTH = 780
HEIGHT = 520
def __init__(self):
super().__init__()
self.geometry(f"{700}x{500}")
self.title("Title of My Program")
self.protocol("Window Closed", self.stop) # "stop" function called when program closed
self.resizable(0,0)
# Options for light & Dark mode
self.option1 = ctk.CTkOptionMenu(master=self,
values=["Light", "Dark", "System"],
width=30,
height=30,
command=self.windowcolor)
self.option1.place(x=5, y=10)
self.option1.set("System") # Select default color for buttons
# Create center label
self.label1 = ctk.CTkLabel(master=self,
text="Graph is generated if you click one option from the below combobox.")
self.label1.place(x=200,y=10)
# City list
cities = ["LA", "CA", "IN", "AK" # etc ...]
# Center Combobox
global combobox1
self.cb_var = StringVar()
self.combobox1 = ctk.CTkComboBox(
master=self,
values=cities,
command=main,
variable=self.cb_var,
)
self.combobox1.place(x=280, y=50)
# Create center frame
self.frameCenter = ctk.CTkFrame(master=self,
width=682,
height=370,
corner_radius=5)
self.frameCenter.place(x=9, y=120)
global main
def main(self): # Main function
self.tkinter.messagebox.showinfo(title="Combobox", message="Clicked")
if combobox1.command == "CA":
graph_CA(self)
# graph photo is generated here
self.data = pd.read_excel("MyExcelFile.xlsx", sheet_name="MySheetName")
# Remove ctkCombobox, create a window fits for graph photo
def graph_CA(self):
# Graphing features added here
# Function that changes color of window
def windowcolor(self, new_appearance_mode):
ctk.set_appearance_mode(new_appearance_mode)
# Function that stops program
def stop(self, event=0):
self.destroy()
if __name__ == "__main__":
app = App()
app.mainloop()
Problem: When I run this code, everything works fine except it generates this error:
'str' object has no attribute 'tkinter'
after I click any options from the center combobox.
The main function works fine and generates graphs well but the program stops even before reaches the main function.
Question: How can I make a combobox that can call the main function when a user clicks any options from it?

Try removing "self." from messagebox function like this:
def main(self): # Main function
#self. removed
tkinter.messagebox.showinfo(title="Combobox", message="Clicked")

Related

Logic after tkinter object does not get executed in a class

I have this script where I initialize a tkinter object with some labels, entries, and buttons with commands. when I run the script, the init function executes fine and the instance of the tkinter window gets spawned and the buttons work just fine. The problem is that, the logic after the init function does not get executed and the tkinter window stays looping for ever unless I hit the exit button and then it just finishes the script. I want to be able to use the inputs from the tkinter.Entry() and the tkinter.filedialog.askopenfilename() functions to do more stuff later in the application such as loading an excel sheet based on the path passed on the askopenfilename() sections but I can't get it to continue.
Note: I am new to using classes so I believe my mistake is somewhere in the way I am laying down the building blocks of the instance itself. Also, while putting this scrip together so many other questions arised and it has been very hard to find concise answers online. I will post some of those questions here in case someone wants to answer them (not needed though)
1- Do I need to declare every single new variable that come in new methods of the class in the init function, if so, why? I just did it here because pycharm game me warning every time I did not do it but not sure what is the reason behind it.
2- When calling a variable on a new method within the class, and the variable is already defined in the init fucntion, do I need to include the variable in the arguments section of the method? if so, do I add the variable with the self argument or not?
import pandas as pd
from openpyxl import load_workbook
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter.messagebox import showinfo
from datetime import datetime
import openpyxl
class App(tk.Tk):
def __init__(self):
super().__init__()
# Define parameters
self.current_PR_report = ""
self.new_PR_report = ""
self.path = ""
self.raw_excel = None
self.new_report = None
self.current_report = ""
# Configure the root window
self.title("PR Reader")
self.geometry('250x200')
# Label
self.label = ttk.Label(self, text="Merge Problem Reports!")
self.label.pack()
# Date Label and Entry
self.date_label = tk.Label(self, text="Enter Date (MM-DD-YYYY)")
self.date_label.pack()
self.date_label_entry = tk.Entry(self)
self.date_label_entry.pack()
# Run button
self.button = ttk.Button(self, text="Run", command=self.select_file)
self.button.pack()
# Button for closing
self.exit_button = tk.Button(self, text="Exit", command=self.destroy)
self.exit_button.pack(pady=20)
# Bring Dialog box to obtain Excel files paths
def select_file(self):
self.current_PR_report = filedialog.askopenfilename(
title="Select Current PR Report",
initialdir="some path",
filetypes=[("Excel Files", "*.xlsx")])
self.new_PR_report = filedialog.askopenfilename(
title="Select New PR Report",
initialdir="some path",
filetypes=[("Excel Files", "*.xlsx")])
# Load the spreadsheets
self.new_report = self.read_excel_report(self.new_PR_report)
self.current_report = self.read_excel_report(self.current_PR_report)
# Define function to load Excel data
def read_excel_report(self, path):
try:
# sheet_name=None indicates you want a dictionary of data frames, each item in the dictionary
# representing a different worksheet.
self.raw_excel = pd.read_excel(path, sheet_name=-1, engine='openpyxl')
self.raw_excel = self.raw_excel.fillna('')
print("data extracted")
return self.raw_excel
except Exception as e:
print(e)
if __name__ == "__main__":
app = App()
app.mainloop()
My main issue is that after running the script my other methods were not getting executed. I learned that I not only need to define the methods on my class but also need to call them inside of it. I this case, I had to call them inside the logic of the "select_file" method since it is the method called when the "Run" button gets called.

Is there a way to make a label show in every window for tkinter without having to write out every single line of code over and over

I am wondering if there is a way, in tkinter or using any other python module, to make it so you keep a label or any other element in every window made by just using something like a function that makes the label within the window? I've tried this:
#Modules
import tkinter as tkin
#The initializer window
class Test:
def __init__(self):
#Initializes the main window
self.start = tkin.Tk()
self.start.title('Test')
self.label_thing()
#Makes a label
def label_thing(self):
self.label1 = tkin.Label(text='Not Button')
self.label1.pack()
I don't know if I made any errors or if this isn't a thing you can do but I'd also like to refrain from having the label localized to this window and having to remake the code for every window.
Let us assume you have a button that creates windows, you would pass this window as an argument to the function that creates the label, so like:
import tkinter as tk # Fixed weird import naming
class Test:
def __init__(self):
self.start = tk.Tk()
self.start.title('Test')
self.label_thing(self.start) # Label on the main window
tk.Button(self.start,text='Click me',command=self.create_win).pack()
self.start.mainloop()
def label_thing(self, master):
self.label1 = tk.Label(master, text='Not Button')
self.label1.pack()
def create_win(self):
new = tk.Toplevel()
self.label_thing(new) # Label on the new window
if __name__ == '__main__':
Test()
As you can see, as long as you press the button, new windows are created, and all those windows have the label on them, dynamically.

how do i display a widget in a Frame written in another script Tkinter

i have a two python scripts with two classes. How do i display a widget which exists in the second script to the a frame in the first script for example
I have the main script where i have my frames in the first script. in the second script(Toplevel) i ran some functions to perform some tasks. after the tasks are performed, the results are written to the database.
while the tasks are running, i wanted a progress bar to be displayed in the Main so that after the tasks is finished then the progress bar can stop.
the challenge is the condition used to start and stop the progressbar occurs in the second script. but i want the progressbar to be displayed in the main
from tkinter import *
from tkinter import ttk
#import script2
class Main(object):
def __init__(self,master):
self.master = master
#main frames
self.mainFrame = Frame(self.master)
self.mainFrame.pack()
self.topFrame= Frame(self.mainFrame,width=200,height =150, bg="#f8f8f8",padx =20, relief =SUNKEN,
borderwidth=2)
self.topFrame.pack(side=TOP,fill = X)
self.btnseldate = Button(self.topFrame, text="open script2", font="arial 12 bold")
self.btnseldate.configure(compound=LEFT, command=self.sc)
self.btnseldate.pack()
def sc(self):
s = script2.class2(self.master,self.mainFrame)
def main():
root =Tk()
app = Main(root)
root.title("two variables")
root.geometry("200x200+350+200")
root.mainloop()
if __name__=="__main__":
main()
this is the second script
####In the second script titled "script2.py"
from tkinter import *
from tkinter import ttk
class class2(Toplevel):
def __init__(self,parent, mainFrame):
Toplevel.__init__(self)
self.geometry("200x200+350+200")
self.title("display")
#object1 = script1.Main()
#self.script1Frame = object1.topFrame()
##this button should be displayed just under the first button in script 1
self.progbar = ttk.Progressbar(self.topFrame, orient=HORIZONTAL, length=200)
self.progbar.pack()
self.progbar.config(mode='determinate')
self.progbar.start()
self.self.topFrame.pack()
#this is a dummy function just to describe what i have
def funcdotask():
#this function contains so many local variables that are only known in
#this script
pass
so the widget actually depends on a condition in the second script , thats why i didn't just place it in the first script.
i need ideas on even how i inherit variables from script2 to script 1 for example
or if there is a way i can get the results from the performed function in script 1(Main). it could help

Python tk entry box not active until window deselected

I have a small Tk-based application that uses a standard layout of a window, defined in init. For one of the submenu items, I need to temporarily create a small form, which I remove after it is successfully submitted. I do this on the fly with the code in start_make_canvas in the mcve below:
import random
import tkinter
from tkinter import *
from tkinter import messagebox
from PIL import ImageTk, Image
NONE=0
TAGGING=1
MAKECANVAS=4
TAGS=["some text","some more text"]
PICS=["c:/users/rob/desktop/camera.jpg","c:/users/rob/desktop/fridge.jpg"]
class Window(Frame):
def __init__(self,master):
Frame.__init__(self, master)
self.master=master
self.mode=NONE
self.init_window()
self.start_tagging()
def start_tagging(self):
if self.photo is not None:
self.photo.destroy()
self.photo=None
messagebox.showinfo("Start tagging")
self.mode=TAGGING
self.configure_buttons()
self.show_pic()
def init_window(self):
menubar=Menu(self.master)
menu=Menu(menubar,tearoff=0)
menu.add_command(label="Start tagging",command=self.start_tagging)
menu.add_command(label="Make canvas",command=self.start_make_canvas)
menubar.add_cascade(label="Tag",menu=menu)
self.master.config(menu=menubar)
self.pack(fill=BOTH,expand=1) #take full space of root window
self.photo=None
self.tag_trk={}
row=1
for tag in TAGS:
self.tag_trk[tag]=IntVar()
Checkbutton(self,text=tag,variable=self.tag_trk[tag]).place(x=500,y=10+20*row)
row+=1
self.tag_count=StringVar()
self.button1_label=StringVar()
self.btn1=Button(self,textvariable=self.button1_label,command=self.button1_click)
self.btn1.place(x=10,y=495)
self.max_score=StringVar()
def configure_buttons(self):
if self.mode==NONE:
self.button1_label.set("Tag")
elif self.mode==TAGGING:
self.button1_label.set("Next")
elif self.mode==MAKECANVAS:
self.button1_label.set("Make")
def button1_click(self):
if self.mode==TAGGING:
self.show_pic()
elif self.mode==MAKECANVAS:
# do some things here
for e in self.form: e.destroy()
self.mode=NONE
self.configure_buttons()
elif self.mode==NONE:
self.start_tagging()
def show_pic(self):
if self.photo is not None:
self.photo.destroy()
img=ImageTk.PhotoImage(Image.open(random.choice(PICS)))
self.photo=tkinter.Label(self,image=img,borderwidth=0)
self.photo.image=img
self.photo.place(x=15,y=5)
def start_make_canvas(self):
if self.photo is not None:
self.photo.destroy()
self.photo=None
self.mode=MAKECANVAS
self.form=[]
e=Label(self,text='Max score')
e.place(x=80,y=200)
self.form.append(e)
e=Entry(self,textvariable=self.max_score,width=20)
e.place(x=180,y=200)
self.form.append(e)
self.form[1].focus_set()
self.configure_buttons()
def target_tags():
global root
root=tkinter.Tk()
root.geometry("700x570")
root.protocol("WM_DELETE_WINDOW", on_closing)
app=Window(root)
root.mainloop()
def on_closing():
global root
root.destroy()
if __name__ == "__main__":
target_tags()
The problem occurs after selecting "Make Canvas" from the menu - the form creation works just fine, except that the newly created Entry elements are not active when first created: I cannot see an insertion cursor, and typed text does not go into the entry. When I select a different window and the reselect my application window, all is fine. Is there a method I need to call after I create the form for mainloop to recognize that there are new bits to be looking after?
Note: in creating the mcve, I found that the messagebox in start_tagging was necessary to recreate the problem. Without it, everything works from the get-go. With it, the checkboxes that are created initially work fine, but the new entry box doesn't (until the window is unselected/reselected).
For some reason, the Entry control looks as if it hasn't been fully initialized yet: I cannot even click on the control and input something into it, it doesn't react. It goes to normal if I Alt-Tab out of the app and back in. Using focus_force() helps as a workaround (it's justified in this case since it's done as a response to user's affirmative action).
This could as well be a bug in Tk, or some additional step is required after .place() that place manual page "forgot" to mention. (With ttk.Entry, it's just the same, so not a case of obsolete code.) You can ask # tcl-core#lists.sourceforge.net about this.

Update Tkinter panel over time

I have Tkinter panels that I want to update over time. The main file executes a method on an imported class to draw the Tkinter panel and I want it to execute 5 times every second.
Here is the main script which calls the method to create the panel:
# Script to control updating the display
from tkinter import *
from settings import Settings
from main_panel import Main
import time
# Creates window
root = Tk()
root.configure(background = 'black')
# Establish settings variable
settings = Settings()
# Create panel class with settings
Main = Main(settings)
# Call panel function to create itself
Main.create(root)
root.mainloop()
Here is the method which creates the Tkinter panels:
def create(self,root):
current = self.get_current_status()
self.draw_icons(root,current)
self.draw_days(root,current)
self.draw_temps(root,current)
self.draw_status(root,current)
Where do I do the 'root.after' call to have the panels update?
You've not provided enough code for a customized example, so here's a minimalist example of using root.after():
from tkinter import *
def update_counter():
counter = label_variable.get()
if counter > 0:
label_variable.set(counter - 1)
root.after(1000, update_counter)
root = Tk()
label_variable = IntVar()
label_variable.set(10)
Label(root, textvariable=label_variable, width=10).pack()
root.after(1000, update_counter)
root.mainloop()
Hopefully this will give you an idea of how to incorporate root.after() into your own code.

Categories

Resources