Backporting Python 3 tkinter & ttk code to Python 2.7 - python-2.7

I'm following a Sentdex tutorial, found here, specifically this part about creating a GUI in Python. However the tutorial is in python 3 and I'm using 2.7.
Importing Tkinter is fine, however when I come to importing ttk and then inheriting it into the class, a problem arises. In python 2.7 ttk is a separate module, which means is not in the Tkinter module.
import Tkinter as tk
import ttk
LARGE_FONT= ("Verdana", 12)
class SeaOfBTCApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Seal of BTC")
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 (StartPage, PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
The problem appears to be when the classes inherit Tk, when I do:
button = ttk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
It doesn't use ttk, the buttons still look the same (like when it was tk.Button). How can I make the buttons look like the ttk buttons.
Full code:
import Tkinter as tk
from ttk import *
LARGE_FONT= ("Verdana", 12)
class SeaOfBTC(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Sea of BTC")
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 (StartPage, PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = ttk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
button2 = ttk.Button(self, text="Visit Page 2",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = ttk.Button(self, text="Page Two",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = ttk.Button(self, text="Page One",
command=lambda: controller.show_frame(PageOne))
button2.pack()
app = SeaOfBTC()
app.mainloop()

If you use ttk.Button, it absolutely will use the ttk button. It can't possibly do anything else because you're explicitly saying to use the Button class from the ttk module.
Note: Depending on what platform you're on, the tk and ttk buttons may look identical.
Other than the way you do the imports, there's virtually no difference between tkinter in python 2.x and 3.x.

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()

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

wx.TextCtrl is blank for very long strings

I'm trying to display the string representation of a list of many float items in a wx.TextCtrl using the SetValue() method.
As soon as the length of the string to be displayed reaches 6151 characters the TextCtrl goes blank and does not display the string. I can still copy portions of the text control as normal and paste them somewhere (such as a text editor) but the characters in the text control itself don't appear on the screen.
Why isn't the text control's value displayed in the text control?
How do I make it display the string if it's longer than 6150 characters?
This happens when setting the text control's value using the SetValue method and when typing in the text control.
Changing the max length for the text control didn't help.
Environment:
Windows 10 (64 bit)
Python 2.7.10
wxPython 3.0
Example code:
import wx
import os
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(200,-1))
self.control = wx.TextCtrl(self)
self.control.SetMaxLength(10000) #doesn't help
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.control, 1, wx.EXPAND)
self.SetSizer(self.sizer)
self.SetAutoLayout(1)
self.sizer.Fit(self)
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()
It looks like a bug. According to the this, it should max out at 64K since Windows 98 unless the operating system you have has some kind of odd limit. You can actually increase the number of characters displayed by using one of the wx.TE_RICH style flags.
I was able to replicate your issue on Windows 7 with Python 2.7 and wxPython 3.0.2 using the following code:
import wx
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
super(MyPanel, self).__init__(parent)
self.text = wx.TextCtrl(self, value="y"*7000)
btn = wx.Button(self, label='Line Length')
btn.Bind(wx.EVT_BUTTON, self.onLength)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.text, 0, wx.EXPAND|wx.ALL, 5)
sizer.Add(btn, 0, wx.CENTER|wx.ALL, 5)
self.SetSizer(sizer)
#----------------------------------------------------------------------
def onLength(self, event):
""""""
print len(self.text.GetValue())
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
super(MyFrame, self).__init__(parent=None, title='Test')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
If I add the wx.TE_RICH flag and call Layout(), I can make it work though:
import wx
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
super(MyPanel, self).__init__(parent)
self.text = wx.TextCtrl(self, value="y"*7000, style=wx.TE_RICH)
btn = wx.Button(self, label='Line Length')
btn.Bind(wx.EVT_BUTTON, self.onLength)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.text, 0, wx.EXPAND|wx.ALL, 5)
sizer.Add(btn, 0, wx.CENTER|wx.ALL, 5)
self.SetSizer(sizer)
self.Layout()
#----------------------------------------------------------------------
def onLength(self, event):
""""""
print len(self.text.GetValue())
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
super(MyFrame, self).__init__(parent=None, title='Test')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
You should report this bug here: http://trac.wxwidgets.org/

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.

Tkinter widgets not appearing

I was testing an app I was writing but I just get a blank window and no widgets.
from Tkinter import*
class App(Frame):
def _init_(self, master):
frame = Frame(master)
frane.pack()
self.instruction = Label(frame, text = 'Password:')
self.instruction.pack()
self.button = Button(frame, text = 'Enter', command = self.reveal)
self.button.pack()
root = Tk()
root.title('Password')
root.geometry('350x250')
App(root)
root.mainloop()
You have a couple of typos. The first is in the name of the constructor method:
def _init_(self, master):
Should read:
def __init__(self, master):
Note the double underscore - see the docs for Python objects.
The second is inside your constructor:
frane.pack()
You're also missing a declaration for a method named 'reveal' in your App class:
self.button = Button(frame, text="Enter", command=self.reveal)
The working example reads:
from Tkinter import *
class App(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.pack()
frame = Frame()
frame.pack()
self.instruction = Label(frame, text="Password:")
self.instruction.pack()
self.button = Button(frame, text="Enter", command=self.reveal)
self.button.pack()
def reveal(self):
# Do something.
pass
root = Tk()
root.title("Password")
root.geometry("350x250")
App(root)
root.mainloop()
See also: The Tkinter documentation.