tkinter use imported function - python

I have the following basic app using tkinter, which has two buttons. With the first button I can open the folder including the files I want to run an analysis on and the second button then runs the analysis.
from tkinter import filedialog
from tkinter import *
from pathlib import Path
from run_analysis import create_tmp_file
class MyApp():
def __init__(self,master):
frame = Frame(master)
frame.pack()
self.button_filedialog = Button(frame, text="Öffnen", fg="red",command=self.open_filedialog)
self.button_analyse = Button(frame, text="Starte Analyse", fg="green", command=self.make_analysis)
## Unpack buttons
self.button_filedialog.pack()
self.button_analyse.pack()
def open_filedialog(self):
start_path = Path.cwd()
self.data_path = filedialog.askdirectory(initialdir=start_path)
def make_analysis(self):
create_tmp_file(self.data_path,1,0.12)
root = Tk()
app = MyApp(root)
root.mainloop()
The code runs fine. However, it is actually not what I want.
I want to call my imported function create_tmp_file directly in the second button. However, if I do replace the line
self.button_analyse = Button(frame, text="Starte Analyse", fg="green", command=self.make_analysis)
with
self.button_analyse = Button(frame, text="Starte Analyse", fg="green", command=create_tmp_file(self.data_path,1,0.12))
The code doesn't work and I receive the following error message:
AttributeError: 'MyApp' object has no attribute 'data_path'
What am I doing wrong?
Thanks!

What's happening here is pretty straightforward. You're setting an attribute on the class - in this case the data_path attribute... - inside the method. But, that only happens when the method is actually invoked.
Setting the command is just a reference to it, so until it's actually called that attribute doesn't exist.
This is clearly problematic when you're giving a reference to a method - which sets the attribute but hasn't been called - then immediately invoking the method which assumes it's existence.

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.

tkinter python program structure

I'm new to python programming, and I have a bit of trouble with the program structure:
When I make my GUI in the main python part then the code works:
import tkinter as tk
root = tk.Tk()
root.overrideredirect(True)
root.geometry("800x480")
def cb_Gebruiker():
btnUser["text"]= "changed"
btnUser = tk.Button(root, text="User",command = cb_Gebruiker)
btnUser.place(x=1,y=1,width="300",height="73")
root.mainloop()
When I make my GUI in a function, the btn variable is local, so this doesn't work
def MakeBtn():
btnUser = tk.Button(root, text="User",command = cb_Gebruiker)
btnUser.place(x=1,y=1,width="300",height="73")
def cb_Gebruiker():
btnUser["text"]= "changed"
MakeBtn()
root.mainloop()
Now I have a program that's rather large and I want my GUI in a separate file, but then I can't access my GUI components...
And I can't seem to be able to find a tutorial on how to structure a program (python has to many possibilities: script, module, object oriented,..)
How do I solve this?
You will need a lambda to delay the call to the command with a parameter.
def MakeBtn():
btnUser = tk.Button(root, text="User", command = lambda: cb_Gebruiker(btnUser))
btnUser.place(x=1, y=1, width="300", height="73")
def cb_Gebruiker(btnUser):
btnUser["text"] = "changed"
MakeBtn()
root.mainloop()

How do I pass a value to another file, which is itself imported in current file?

I have two files, one containing the tkinter code and another containing a function. I have a button in the tkinter window and an Entry field. I am trying to execute the function when clicking the button, but it needs the text from the Entry field to work. I get an error when trying to import anything from the tkinter file:
tkinter_file.py:
import File
window = Tk()
def input():
s = entry1.get()
return s
entry1 = Entry(window)
button1 = Button(window, text='GO', command=File.function)
File.py:
from tkinter import *
import tkinter_file
def function():
req_url = 'http://someurl.com/{}'.format(tkinter_file.input)
requests get url etc. etc.
I seem to be getting an error as soon as I import the tkinter_file into File.py, or even just the function input:
File "/etc/etc/tkinter_file.py", line 75, in <module>
button1 = Button(window, text='GO', command=File.function)
AttributeError: module 'File' has no attribute 'function'
I'm thinking that req_url not having the value s straight away is the problem, as well as maybe importing the 2 files into each other, but how do you overcome this?
If you have two modules, say a.py and b.py, you can't import module b in a and then import module a in b, because that creates a cyclic dependency, which can't clearly be solved!
A solution would be to pass as parameter to File.function what you need for that function to run properly, i.e. the contents of the entry1.
button1 = Button(window, text='GO', command=lambda: File.function(entry1.get()))

Python call function defined in class

So I am making a game and I am using Tkinter and Python 2.7, and I want to store map data in files. I am trying to make a menu to open the file and set to a variable. I am having trouble defining a function to use on the Tkinter Button.
The Tkinter window opens but when I click the button it gives an error.
Code:
#readfiles.py
class maps(object):
def __init__(self):
self.data
def mapset(var):
data = var
fselect = Tk()
...
buttons = Frame(fselect).pack()
Label(fselect, text="Select maps in folder:")
for i in listdir('./maps/'):
if i[-4:] == ".pvm":
Button(buttons, text=i[:-4], command=lambda i=i: mapset(open('./maps/'+i, 'r').read())).pack()
NameError: global name 'mapset' is not defined
Also, how can I access the data variable in maps (so it would be maps.data, right) in another file?
#main.py
from readfiles import *
print maps.data
AttributeError: type object 'maps' has no attribute 'data'
Let me know if you need any additional information.
Also, print "Happy New Years!
Here's a simple GUI class that stores the data from a map file in the self.data attribute.
To test this code I created a set of simple text files using this Bash command:
for i in {0..5}; do echo >"maps/map${i}.pvm" "Map $i data"; done
And here's the Python 3 code. To run it on Python 2, just change the Tkinter import statement to import Tkinter as tk, and add from __future__ import print_function before the other import statements.
I've added a "Print data" that lets us test that we have actually loaded the data correctly.
import tkinter as tk
import os
class Gui(object):
def __init__(self, mapspath):
self.data = None
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
tk.Label(frame, text="Select maps in folder:").pack()
for fbase in os.listdir(mapspath):
if fbase.endswith('.pvm'):
fname = os.path.join(mapspath, fbase)
#print(fname)
tk.Button(frame, text=fbase[:-4],
command=lambda fn=fname:self.read_map(fn)).pack()
tk.Button(frame, text="Print data", command=self.print_map).pack()
root.mainloop()
def read_map(self, fname):
with open(fname) as f:
self.data = f.read()
def print_map(self):
print("current map:", self.data)
Gui("./maps")
Unless these maps are huge it would probably be more convenient to store all of them at once. You could store them in a list, but it'd probably be better to store them in a dictionary, using the base name (minus the '.pvm' extension) as the key.

Python 2.7 Tkinter open webbrowser on click

from Tkinter import *
import webbrowser
root = Tk()
frame = Frame(root)
frame.pack()
url = 'http://www.sampleurl.com'
def OpenUrl(url):
webbrowser.open_new(url)
button = Button(frame, text="CLICK", command=OpenUrl(url))
button.pack()
root.mainloop()
My goal is to open a URL when I click the button in the GUI widget. However, I am not sure how to do this.
Python opens two new windows when I run the script without clicking
anything. Additionally, nothing happens when I click the button.
You should use
button = Button(root, text="CLCK", command=lambda aurl=url:OpenUrl(aurl))
this is the correct way of sending a callback when arguments are required.
From here:
A common beginner’s mistake is to call the callback function when
constructing the widget. That is, instead of giving just the
function’s name (e.g. “callback”), the programmer adds parentheses and
argument values to the function:
If you do this, Python will call the callback function before creating
the widget, and pass the function’s return value to Tkinter. Tkinter
then attempts to convert the return value to a string, and tells Tk to
call a function with that name when the button is activated. This is
probably not what you wanted.
For simple cases like this, you can use a lambda expression as a link
between Tkinter and the callback function:
Alternatively, you don't have to pass the URL as an argument of the command. Obviously your OpenUrl method would be stuck opening that one URL in this case, but it would work.
from Tkinter import *
import webbrowser
url = 'http://www.sampleurl.com'
root = Tk()
frame = Frame(root)
frame.pack()
def OpenUrl():
webbrowser.open_new(url)
button = Button(frame, text="CLICK", command=OpenUrl)
button.pack()
root.mainloop()

Categories

Resources