Python / Tkinter grid_rowconfigure issue between versions - python-2.7

Working with some samples I encountered strange behaviour in Python 3.9.
grid_rowconfigure did not expand rows as i would expect it to.
Unfortunately looking for an already reported bug inside either Python3.9.2 or Tk8.6 lead nowhere.
Please find below the reproduction code.
#!/usr/bin/python
"""
Code Stripped for Stackoverflow.
"""
from sys import hexversion
if hexversion < 0x03000000:
# pylint: disable=import-error
# if we are in 0x02whaterver that works
import Tkinter as tk
import ttk
else:
import tkinter as tk
from tkinter import ttk
class ScrolledTree(ttk.Frame):
""" A Scrolled Treeview
"""
def __init__(self, *args, **kwargs):
# pylint: disable=unused-argument
if hexversion < 0x03000000:
ttk.Frame.__init__(self, *args, **kwargs)
else:
super().__init__()
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.tree = ttk.Treeview(self)
self.tree.grid(row=0, column=0, sticky=tk.NW+tk.SE)
ttk.Style().configure("TFrame", background="red")
ttk.Style().configure("ScrolledTree.TFrame", background="blue")
class ScrolledFilterTree(ttk.Frame):
""" A Scrolled Tree with Filters
"""
def __init__(self, *args, **kwargs):
# pylint: disable=unused-argument
if hexversion < 0x03000000:
ttk.Frame.__init__(self, *args, **kwargs)
else:
super().__init__()
ttk.Label(self, text="asdf").grid(row=0, column=0)
self.tree = ScrolledTree(self)
self.tree.grid(row=1, column=0, sticky=tk.NW+tk.SE)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(0, weight=1)
ttk.Style().configure("TFrame", background="red")
ttk.Style().configure("ScrolledTree.TFrame", background="blue")
def test_show():
root = tk.Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.title("Show - ScrolledTree")
app = ScrolledTree(root)
app.grid(row=0, column=0, sticky=tk.NW+tk.SE)
root.mainloop()
root = tk.Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.title("Show - ScrolledFilterTree")
app = ScrolledFilterTree(root)
app.grid(row=0, column=0, sticky=tk.NW+tk.SE)
root.mainloop()
#end test_show
if __name__ == "__main__":
test_show()
What does look strange?
ScrolledText classes (Python2.7 / Tk8.5) on the left (Python 3.9 / Tk8.6) on the right are both fine.
Using them in ScrolledFilteredText results in:
Is there any explanation why these look different?
Or - to be more precice - behave different on rescaling of the root window?
Update 1
OS: Windows 10 20H2 ( 19042.985 ) x64
Python: 2.7.16 x64 (v2.7.16:413a49145e, Mar 4 2019, 01:37:19)
Python: 3.9.2 x64 (tags/v3.9.2:1a79785, Feb 19 2021, 13:44:55)
Tk/Tcl: Stock versions from the python distributions.
Any help is very much appreciated.

Since you did not pass *args and **kwargs to super().__init__(), i.e. did not pass the parent argument, ScrolledTree will be put in root window instead of ScrolledFilterTree frame in Python 3.
Add back *args and **kwargs to super().__init__() will fix the issue.

Related

I am trying to have fixed frame for my GUI Application. The window should not change its size

I am trying to develop a GUI using Tkinter and Python2.7 by following a few tutorials and their documentation. Right now i am stuck on how to keep my frame a constant size (Because i want to have a nice background).
I have tried to use geometry() function from their documentation but always returns with an error - geometry attribute not found
import Tkinter as tk
import ttk
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
# Initialize Tkinter
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "ScraperBot")
tk.Tk.geometry('250x250')
# Make a container
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
# Define Frames
self.frames = {}
for myFrame in (HomePage, PageOne, PageTwo):
frame = myFrame(container, self)
self.frames[myFrame] = frame
frame.grid(row=0, column=0, sticky="nsew")
# Which frame do we wanna display?
self.show_frame(HomePage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class HomePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# Make a Label
label = ttk.Label(self, text="HomePage", font=LARGE_FONT)
label.pack(pady=10, padx=10) # Padding on top and bottom
# Add a Button to navigate from one page to another
button1 = tk.Button(self, text="Go to Page_1", fg="red",
command= lambda: controller.show_frame(PageOne))
button1.pack()
# Add a Button to navigate from one page to another
button6 = tk.Button(self, text="Go to Page_2", fg="green",
command= lambda: controller.show_frame(PageTwo))
button6.pack()
def button_Function(parameter):
print(parameter)
app = Application()
app.mainloop()

Python Tkinter- method for printing not working after being called from another script

I have a class called app.py within that class there's a method called print_raw_records_screen here's the part of the class and the method
app.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Tkinter import *
from ttk import *
import os
import mftsession
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.filename = ""
self.initUI()
#defining a function that that receives the raw records from the mftsession.py and print it to the screen
#this script will be called by the mftsession.py in
#process_mftfile()
def print_raw_records_screen(self,records):
self.area.delete(1.0, "end")
self.area.insert('1.0',records)
def initUI(self):
self.parent.title("Mtf Analyzer")
#initializing and configuring menubar
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="Open file", command=self.fileOpen)
fileMenu.add_command(label="Exit", command=self.onExit)
menubar.add_cascade(label="File", menu=fileMenu)
#specify grid row and column spaces
self.pack(fill=BOTH, expand=True)
self.columnconfigure(1, weight=1)
self.columnconfigure(3, pad=7)
self.rowconfigure(3, weight=1)
self.rowconfigure(5, pad=7)
lbl = Label(self, text="File Name")
lbl.grid(row=1, column=0, sticky=W, pady=4, padx=5)
self.filename_area = Entry(self)
self.filename_area.grid(row=1, column=1, columnspan=5, padx=5, sticky=E+W+S+N)
analize_button = Button(self, text="Analize", command=self.processFile)
analize_button.grid(row=1, column=6, padx=5)
self.area = Text(self)
self.area.grid(row=2, column=1, columnspan=2, rowspan=4,
padx=5, sticky=E+W+S+N)
#configure the raw output view
def onExit(self):
self.quit()
#this function selects and opens the file to analize
def fileOpen(self):
from tkFileDialog import askopenfilename
Tk().withdraw()
self.filename = askopenfilename()
#populate the filename field
self.set( self.filename)
#do the processing of the file obtained. Populate the file NAME entry or
#send the filename to the analyzeMFT.py
def processFile(self):
arguments = "analyzeMFT.py -f "+self.filename+" -d --bodyfull -l -o "+self.filename+".csv"
os.system(arguments)
mftsession.MftSession.process_mft_file(self)
#get and set methods for the entry field
def get(self):
return self.filename_area.get()
def set(self, value):
self.filename_area.delete(0, END)
self.filename_area.insert(0,value)
def main():
root = Tk()
root.geometry("450x350+500+500")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
This method is called by another external script like this
mftsession.py
import csv
import json
import os
import sys
from optparse import OptionParser
import mft
import app
from Tkinter import *
from ttk import *
class MftSession:
"""Class to describe an entire MFT processing session"""
#staticmethod
def fmt_excel(date_str):
return '="{}"'.format(date_str)
#staticmethod
def fmt_norm(date_str):
return date_str
def __init__(self):
self.mft = {}
self.fullmft = {}
self.folders = {}
self.debug = False
self.mftsize = 0
def process_mft_file(self):
root = Tk()
appi = app.Example(root)
self.sizecheck()
self.build_filepaths()
# reset the file reading
self.num_records = 0
self.file_mft.seek(0)
raw_record = self.file_mft.read(1024)
if self.options.output is not None:
self.file_csv.writerow(mft.mft_to_csv(None, True, self.options))
while raw_record != "":
record = mft.parse_record(raw_record, self.options)
if self.options.debug:
print record
appi.print_raw_records_screen(raw_record ) #THIS FUNCTION WHILE INVOKED THIS WAY IS NOT WORKING
..........
The script analyzeMFT.py called by the app.py through the subrouting
#!/usr/bin/python
import sys
from os import path
def main():
session = mftsession.MftSession()
session.mft_options()
session.open_files()
session.process_mft_file()
if __name__ == '__main__':
if __package__ is None:
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
import mftsession
main()
else:
import mftsession
main()
When called this way it prints nothing to the window, but when i invoke it for testing within its own class it prints the test
def print_raw_records_screen(self,records):
self.area.delete(1.0, "end")
self.area.insert('1.0', records)
and invoke like print_raw_records_screen("any test"): in the app.py
An image can tell alot. and summarize
What am i doing wrong? I sense it's the instantiation am doing wrong. I need diretions please
From what I can see here you have a few issues causing problems.
you are importing app.py on mftsession.py instead importing mftsession.py on app.py.
You are trying to use appi.print_raw_records_screen(raw_record) on a completely different instance of the Example() class with appi = app.Example(root) remove that part all together.
It is bad practice to importing inside a function. Import at the start of each py file.
There is so many things going on in your code I had to create a Minimal, Complete, and Verifiable example example of my own to illustrate the relation between files.
Here is a simple example of how the 2 files can interact and the way I think you are trying to do things.
Here I have created a main file called app.py:
from Tkinter import *
# You might need to import the py file with the package name as well.
# Change the package name to the package your python files are located in.
import PACKAGE_NAME.file_b
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.filename = ""
analize_button = Button(self.parent, text="Analize", command=self.processFile)
analize_button.pack()
self.area = Text(self.parent, height = 2, width = 40)
self.area.pack()
def print_raw_records_screen(self,records):
self.area.delete(1.0, "end")
self.area.insert('1.0',records)
def processFile(self):
PACKAGE_NAME.file_b.process_mft_file(self)
if __name__ == "__main__":
root = Tk()
app = Example(root)
root.mainloop()
Here I have created a file called file_b.py
from Tkinter import *
def process_mft_file(self):
record = "Some values assigned to a variable"
self.print_raw_records_screen(record)
The results look something like this:

Printing the output of a script to a window in python

I am trying to create a GUI for the analyzeMFT python program. So far this is what i have
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Tkinter import *
from ttk import *
import analyzeMFT
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.filename = ""
self.initUI()
def initUI(self):
self.parent.title("Mtf Analyzer")
#initializing and configuring menubar
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="Open file", command=self.fileOpen)
fileMenu.add_command(label="Exit", command=self.onExit)
menubar.add_cascade(label="File", menu=fileMenu)
#specify grid row and column spaces
self.pack(fill=BOTH, expand=True)
self.columnconfigure(1, weight=1)
self.columnconfigure(3, pad=7)
self.rowconfigure(3, weight=1)
self.rowconfigure(5, pad=7)
lbl = Label(self, text="File Name")
lbl.grid(row=1, column=0, sticky=W, pady=4, padx=5)
filename_area = Entry(self)
filename_area.grid(row=1, column=1, columnspan=3,
padx=5)
analize_button = Button(self, text="Analize", command=self.processFile)
analize_button.grid(row=1, column=4, padx=5)
area = Text(self)
area.grid(row=2, column=1, columnspan=2, rowspan=4,
padx=5, sticky=E+W+S+N)
#configure the raw output view
def onExit(self):
self.quit()
#this function selects and opens the file to analize
def fileOpen(self):
from tkFileDialog import askopenfilename
Tk().withdraw()
self.filename = askopenfilename()
#populate the filename field
#do the processing of the file obtained. Populate the file NAME entry or
#send the filename to the analyzeMFT.py
def processFile(self):
filei = "-f "+self.filename
arguments = [filei, '-o '+self.filename+' .csv']
analyzeMFT.main(arguments)
#get and set methods for the entry field
def get(self):
return self.filename_area.get()
def set(self, value):
filename_area.set(value)
def main():
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
This code creates a gui that i can chose a file to analyze using the analyzeMFT program. There are two problems.
1.analyzeMFT is meant to be run on command line. I am not able to pass the file name I get from the GUI to the analyzeMFT.py Here are the contents of analyzeMFT.py
#!/usr/bin/python
try:
from analyzemft import mftsession
except:
from .analyzemft import mftsession
def main():
session = mftsession.MftSession()
session.mft_options()
session.open_files()
session.process_mft_file()
if __name__ == "__main__":
main()
Next, when i run the analyzerMFT in cmd and enable debug mode, it prints every detail on the screen. How can I direct that out to a window i have shown below
I want it somehow to look like this
Sorry if the explanation is very long. I've been working on it for days.
Instead of using print like you would normally use to display results to the console you can use insert() on your text box.
EDIT:
First change:
area = Text(self)
area.grid(row=2, column=1, columnspan=2, rowspan=4,
padx=5, sticky=E+W+S+N)
To:
self.area = Text(self)
self.area.grid(row=2, column=1, columnspan=2, rowspan=4,
padx=5, sticky=E+W+S+N)
This way your text box is set up as a class attribute that we can use.
Then use:
self.area.delete(1.0, "end") # to clear text box
self.area.insert("end", your_output variable or string) # to insert content

Replacing a figure on Tkinter Python

I am using Tkinter and would like to have the option to clear my graph and draw a new one in its place. I'm not sure what to put in my callback to do this. The best I can do so far is to replace the graph with a white box. The new graph is drawn underneath (which isn't ideal) and also appears as a blank white box for some reason. Apologies for asking such a basic question but I'm new to python and couldn't find an example using subplots on here.
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import Tkinter as tk
from Tkinter import *
import ttk
class Helmholtz_App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Helmholtz Coils Data")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (PageOne,PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(PageOne)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
fig1 = Figure(figsize=(5,5), dpi=100)
a = fig1.add_subplot(111)
def callback():
volt=[[0,1,2,3,4],[0,1,2,3,4]]
a.plot(volt[1],volt[0],'bo')
canvas = FigureCanvasTkAgg(fig1, self)
canvas.show()
canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
toolbar = NavigationToolbar2TkAgg(canvas, self)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
def ClearCallback():
#problem line
#~~~~~~~~~~~
fig1.clf()
# ~~~~~~~~~~
clearbutton.destroy()
clearbutton=ttk.Button(self, text="Clear", command=ClearCallback)
clearbutton.pack()
b=ttk.Button(self, text="Plot Data", command=callback)
b.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
app = Helmholtz_App()
app.mainloop()
The code is based on this tutorial 1
I want the code to work so the "take data" button creates a graph, and the "clear" button removes that graph. Then when the "take data" button is pressed again it will put a new graph in the same place with none of the data from the old graph present.
Here is the

Passing an SQLAlchemy Session to a TKinter controller

I am currently learning how to work with the tkinter gui and I am trying to integrate with SQLAlchemy. The first hurdle was figuring out how to move between frames without destroying/closing the window, luckily I found this question: Switch between two frames in tkinter. So I used a lot of the provided code and created a simple application that is trying to have someone log in.
Now for the part I can't figure out. I have a function _check_credentials(self) deep inside the structure of windows that is trying to query the db. I have successfully created the table and queried it elsewhere.
I need to pass the session from the main.py module to the Login class and finally to the _check_credentials(self) method.
main.py -> Authorzation() -> Login() -> login_btn = tk.Button() -> _check_credentials()
Additional info:
I have included all of my code. Right now it runs, but crashes when the login button is pressed.
I have attempted to pass the session directly, ie. Authorzation(Session) but that crashes immediately.
Also, I am trying to follow the guidelines laid out in the sqlalchemy docs http://docs.sqlalchemy.org/en/latest/orm/session.html, FAQ, "When do I construct a Session, when do I commit it, and when do I close it?".
main.py:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from module1 import Authorzation
import Tkinter as tk
Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
app = Authorzation()
app.mainloop()
module1.py:
import Tkinter as tk
import user
TITLE_FONT = ("Helvetica", 18, "bold")
class Authorzation(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Login, Register):
frame = F(container, self)
self.frames[F] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Login)
def show_frame(self, c):
'''Show a frame for the given class'''
frame = self.frames[c]
frame.tkraise()
class Login(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Title", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
#Create the Username field
label_username = tk.Label(self, text="username")
label_username.pack()
self.username = tk.StringVar()
tk.Entry(self, textvariable=self.username).pack()
#Create the password field
label_password = tk.Label(self, text="password")
label_password.pack()
self.password = tk.StringVar()
tk.Entry(self, textvariable=self.password).pack()
login_btn = tk.Button(self, text="Login",
command=self._check_credentials)
login_btn.pack(pady=5)
reg_btn = tk.Button(self, text="Registration",
command=lambda: controller.show_frame(Register))
reg_btn.pack(pady=10)
def _check_credentials(self):
session_user = session.query(user.User)\
.filter_by(
username=self.username
).first()
if session_user:
return session_user.check_pwd(), session_user
else:
print("Sorry we could not find your username")
return False, None
self.controller.show_frame(Login)
class Register(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="This is page 1", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(Login))
button.pack()
After doing research on *args, and **kwargs. I realized I had not tried putting the session first in the __init__() function. Here is how I then passed the session to the controller.
main.py:
app = Authorzation(session)
module1.py
class Authorzation(tk.Tk):
def __init__(self, session, *args, **kwargs):
self.session = session
Once the session was in the controller it was easy to reference it from the Login() frame.
module1.py:
class Login(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
def _check_credentials(self):
session = self.controller.session
Note: this solution is also nice because it alleviates the need to pass the session through the button.