I'm having a question related to functions in a class. My goal is to write a program with a Visualization class, which handles all the GUI related stuff with tkinter and another class called SensorHandling, which will mainly test sensors whether they work or not and return the result, which will then be displayed in Visualization. I'm having problems with my GUI class.
I have two different functions inside the class Visualization, namely StartPage and TestingHead_Page. I pass root in order for them to be able to modify my application directly. Now, I want to display the TestingHead_Page with my button bTestingHead, but I get an error that TestingHead_Page is not defined.
How do I go about this problem?
Criticism is always appreciated as I want to learn.
import tkinter as tk
from tkinter import ttk
import serial
import platform
import time
LARGEFONT = ("Verdana", 35)
class Visualization:
def StartPage(self, root):
lTitle= ttk.Label(root, text ="Title", font = LARGEFONT)
lTitle.grid(row = 0, column = 1, sticky = "N", padx = 10, pady = 10)
lTitle.config(font = ('Calibri', 32, 'bold'))
lTesting = ttk.Label(root, text = "Testing", font = ('calibri', 20))
lTesting.grid(row = 1, column = 0, padx = 10, pady = 10)
bTestingHead = ttk.Button(root, text = "Testing Head", command = self.TestingHead_Page(root))
bTestingHead.grid(row = 2 , column = 0, padx = 20, pady = 20)
exit = tk.PhotoImage(file = "exit_icon.png")
bExit = ttk.Button(root, text = "exit", image = exit, command = root.destroy)
bExit.image = exit
bExit.place(relx = 1.0, rely = 0.0, anchor = 'ne')
def TestingHead_Page(self, root):
lTitle = ttk.Label(root, text = "Testing Head")
lTitle.grid(row = 0, column = 0, sticky = "W", padx = 10, pady = 10)
bStartPage = ttk.Button(root, text = "Start Page", command = "")
bStartPage.grid(row = 0, column = 1, sticky = "E", padx = 10, pady = 10)
exit = tk.PhotoImage(file = "exit_icon.png")
bExit = ttk.Button(root, text = "exit", image = exit, command = root.destroy)
bExit.image = exit
bExit.place(relx = 1.0, rely = 0.0, anchor = 'ne')
if __name__ == "__main__":
root = tk.Tk()
guiref = StartPage(self, root)
tk.mainloop()
#class SensorHandling():
In python, you need to pass the current instance, referenced through the self keyword, explicitly in the class's methods:
def StartPage(self, root):
...
def TestingHead_Page(self, root):
...
While inside the class, you would call them like this:
self.StartPage(root)
self.TestingHead_Page(root)
Read up on classes in Python.
Also, from the code, it seems like TestingHead_Page should return some value since you try to capture that in command = TestingHead_Page(root), so you need to fix that as well.
Related
Application: Building a notes application (as an intro to GUI development in Python) which includes feature of a scrollbar to scroll through a textbox
Problem: I can't actually seem to scroll down through the textbox. I don't seem to get the grayed rectangle which lets me control the scrollbar and scroll up/down through the textbox
#importing necessary packages
from tkinter import *
from tkinter import font
from tkinter import ttk
#set up main window
root = Tk()
root.title("Notes")
root.geometry("400x650")
#functions
#functions to change all widget button's backgrounds when user hovers over it and leaves it
def enter_button(e):
e.widget.config(background = "#D4D4D4")
#SystemButtonFace is default colour
def leave_button(e):
e.widget.config(background = "SystemButtonFace")
#clear text in text-box
def clear():
#delete all text from text_box
text_box.delete(1.0,END)
def bold_it():
#create font
try:
bold_font = font.Font(text_box, text_box.cget("font"))
bold_font.configure(weight = "bold")
#creating tag called "bold" which bolds textll upon condition
text_box.tag_configure("bold", font = bold_font)
#creating a bold tag which highlights first character
bold_tag = text_box.tag_names("sel.first")
#condition for checking to see if tag is applied or not
#in the first highlighted character
#if tag is applied, remove the bold from first-highlighted text
#- last highlighted text
#"bold" needs to be matched in the tag
if "bold" in bold_tag:
text_box.tag_remove("bold","sel.first","sel.last")
else:
text_box.tag_add("bold","sel.first", "sel.last")
except TclError:
pass
def italics_it():
try:
#create a font
italics_font = font.Font(text_box, text_box.cget("font"))
italics_font.configure(slant = "italic")
#create a tag called "italic"
text_box.tag_configure("italics", font = italics_font)
italics_tag = text_box.tag_names("sel.first")
#condition to see whether tag has been applies or not
if "italics" in italics_tag:
text_box.tag_remove("italics", "sel.first","sel.last")
else:
text_box.tag_add("italics", "sel.first", "sel.last")
except TclError:
pass
#frames
top_frame = LabelFrame(root, padx = 30, pady = 10)
button_frame = LabelFrame(root, padx = 30, pady = 10)
text_frame = LabelFrame(root, padx = 10, pady = 10)
bottom_frame = LabelFrame(root, borderwidth = 0, highlightthickness = 5)
top_frame.grid(row = 0 , column = 0)
button_frame.grid(row = 1, column = 0, pady = 10)
text_frame.grid(row = 2, column = 0, pady = 1)
bottom_frame.grid(row = 3, column = 0, pady = 3)
#labels, textboxes, buttons
#top_frame content
Notes_label = Label(top_frame, text = "Notes", fg = "black", font = 1, padx = 141)
Notes_label.grid(row = 0 , column = 0)
save_button = Button(top_frame, text = "save")
#padx increases distance between buttons
#button_frame content
#bold button
#the ideal is that if u press ctrl + b, the bold_button is pressed by itself
#rn, it's gonna be a highlight technique
bold_button = Button(button_frame, text = "B", padx = 4, pady = 2, command = bold_it)
bold_button.grid(row = 0, column = 0)
#italicsize button
italics_button = Button(button_frame, text = "I", padx = 4, pady = 2, command = italics_it)
italics_button.grid(row = 0, column = 2, padx = 15)
#text_box frame button
text_box = Text(text_frame, width = 45, height = 27)
text_box.grid(row = 0, column = 0)
#text_box frame content
main_scrollbar = ttk.Scrollbar(text_frame, orient = "vertical", command = text_box.yview)
main_scrollbar.grid(row = 0, column = 1)
text_box["yscrollcommand"] = main_scrollbar.set
clear_button = Button(bottom_frame, text = "clear", padx = 2, pady = 2, command = clear)
clear_button.grid(row = 0, column = 0, padx = 15, pady = 10)
save_button = Button(bottom_frame, text = "save note", padx = 2, pady = 2)
save_button.grid(row = 0, column =1, padx = 15, pady = 10)
#binding all buttons for changing colours when user hovers over it and leaves it
bold_button.bind("<Enter>", enter_button)
bold_button.bind("<Leave>", leave_button)
italics_button.bind("<Enter>", enter_button)
italics_button.bind("<Leave>", leave_button)
clear_button.bind("<Enter>", enter_button)
clear_button.bind("<Leave>", leave_button)
save_button.bind("<Enter>", enter_button)
save_button.bind("<Leave>", leave_button)
# main program loop
root.mainloop()
here's an image of the problem image of problem
I would also be very grateful if one could explain the concept of scrollbar.set and like yview and why they are both needed for the scrollbar to work. Tutorials and videos don't seem to explain the concept, but just implement it
In line 145. You're missing sticky
main_scrollbar.grid(row = 0, column = 1, sticky=NS)
Output:
I am working with python tkinter and I have a dialog box window that pops up. I have two buttons in the box:
from tksheet import Sheet
from tkinter import *
import os
import sys
import mapMaker2
root=Tk()
root.title('Map Tool')
root.geometry("750x750")
sheetframe = Frame(root)
sheetframe.grid(row = 0, column = 0,)
buttonEditlabel = Button(sheetframe, text='Edit Labels', width=12, command=lambda: [openEditWindow()], bg='#cacccf',fg='black')
buttonEditlabel.grid(row = 0, sticky=W, column = 0, pady = (25,5), padx = (50,0))
def openEditWindow():
top = Toplevel(root)
top.geometry("260x195")
top.title('Edit Axes Labels')
frm = Frame(top, borderwidth=0, relief='ridge')
frm.grid(row = 0, column = 0, pady = (20,0),padx=(20,0))
b_cancel = Button(frm, text='Close', width=10)
b_cancel['command'] = top.destroy
b_cancel.grid(column = 0, row = 6, pady = (15,0),padx=(0,0))
b_save = Button(frm, text='Save', width=10)
b_save['command'] = lambda: editLabels()
b_save.grid(column = 1, row = 6, sticky = E, pady = (15,0),padx=(0,0))
def editLabels():
pass
mainloop()
Cancel button closes the window with top.destroy command. I would like the Save button to also close the window after running the editLabels() function first. I have tried:
b_save['command'] = [lambda: editLabels(), top.destroy]
but this doesn't work.
Here is one way you can do it. Create a function to destroy the top window.
def kill_main():
top.destroy()
top.update()
Then call the function wherever you want. You don't have to add kill_main() to the button itself. Just put it inside the next function you are opening so that it will close the Top windows first and then run the rest of the editlabels() function. Hopefully, it makes sense to you.
def editLabels():
kill_main()
pass
So this is my first Python GUI project utilizing tkinter. I come from a background in R.
I decided after a review of the documentation to create a class to handle the bulk of the work. The problem appears with my incrementer functions fwd() and bck(). If I do not call these functions in the following chunk of code:
class App:
def __init__(self, master):
....
self.total = 2
self.fwd()
self.bck()
The output of the entire code is an empty tkinter frame.
On the other hand, if I do call them, the fwd() function works as one would expect, but every time I click the back button (command = bck()), a new and identical GUI will be attached directly to the bottom of my current GUI. If I click the back button again, another GUI will pop up behind the current GUI.
from tkinter import *
from tkinter import font
from tkinter import filedialog
class App: #I'm not typing what goes in this class, this way I can avoid issues with App(Frame), etc. DUCKTYPE!
def __init__(self, master):
self.frame = Frame(master)
self.frame.pack()
self.master = master
master.title("PyCCI Caste")
self.total = 2
self.fwd() #Need to call these at the beginning otherwise the window is minimized??? No idea why.
self.bck() #The back button creates a duplicate window...
## +Incrementer
def fwd(self):
self.total += 1
print(self.total)
## -Incrementer THIS CREATES A SECOND PANED WINDOW, WHY?!
def bck(self):
self.total += -1
if self.total < 3:
self.total = 2
print(self.total)
#Body
self.k1 = PanedWindow(self.frame, #Note: if this is not self.frame, the error: 'App' object has no attribute 'tk' is thrown
height=500,
width=750,
orient = VERTICAL)
self.k1.pack(fill=BOTH, expand = 1)
self.titlefont = font.Font(size = 12,
weight = 'bold')
self.boldfont = font.Font(size=8,
weight = 'bold')
self.textfont = font.Font(family = 'Arial',
size = 10)
#Title
self.title = PanedWindow(self.k1)
self.k1.add(self.title, padx = 10, pady = 10)
Label(self.title, text = "Chronic Critically Ill Patient GUI",
font = self.titlefont,
fg="darkslateblue").pack()
#Top row open csv window & button
self.k2 = PanedWindow(self.k1)
self.k1.add(self.k2)
self.openbutton = Button(self.k2,
text = "Open CSV")#, command = openfile())
self.openbutton.pack(side = LEFT,
padx = 30)
#Panes below buttons
self.k3 = PanedWindow(self.k1)
self.k1.add(self.k3)
self.leftpane = PanedWindow(self.k3)
self.k3.add(self.leftpane,
width = 400,
padx = 30,
pady = 25,
stretch = "first")
self.separator = PanedWindow(self.k3,
relief = SUNKEN)
self.k3.add(self.separator,
width=2,
padx=1,
pady=20)
self.rightpane = PanedWindow(self.k3)
self.k3.add(self.rightpane,
width = 220,
padx = 10,
pady = 25,
stretch = "never")
#Left pane patient note text frame doo-diddly
self.ptframe = LabelFrame(self.leftpane,
text = "Medical Record",
font = self.boldfont,
padx = 0,
pady=0,
borderwidth = 0)
self.ptframe.pack()
Label(self.ptframe,
text = "patient # of ##").pack()
#Incrementer buttons
self.buttonframe = Frame(self.ptframe)
self.buttonframe.pack()
self.buttonframe.place(relx=0.97, anchor = NE)
#Back Button
self.button1 = Button(self.buttonframe, text = 'Back', width = 6, command = self.bck)
self.button1.grid(row = 0, column = 0, padx = 2, pady = 2)
#Next Button
self.button2 = Button(self.buttonframe, text = 'Next', width = 6, command = self.fwd)
self.button2.grid(row = 0, column = 2, padx = 2, pady = 2)
#Scrollbar!
self.ptscroll = Scrollbar(self.ptframe)
self.ptscroll.pack(side = RIGHT, fill = Y)
self.pttext = Text(self.ptframe,
height=300,
width=400,
wrap=WORD,
font=self.textfont,
spacing1=2,
spacing2=2,
spacing3=3,
padx=15,
pady=15)
self.pttext.pack()
self.ptscroll.config(command=self.pttext.yview)
self.pttext.config(yscrollcommand=self.ptscroll.set)
#Checkbuttons
self.checkframe = LabelFrame(self.rightpane, text="Indicators",
font=self.boldfont,
padx = 10,
pady = 10,
borderwidth=0)
self.checkframe.pack()
self.check1 = Checkbutton(self.checkframe, text="Non-Adherence")
self.check1.grid(row = 1,
column = 0,
sticky = W)
root = Tk()
app = App(root) ## apply the class "App" to Tk()
### Menu stuff does not need to be part of the class
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Open CSV")#, command=openfile)
menubar.add_cascade(label="File", menu=filemenu)
helpmenu = Menu(menubar, tearoff=0)
helpmenu.add_command(label="About")#, command=about)
menubar.add_cascade(label="Help", menu=helpmenu)
root.config(menu=menubar)
root.mainloop()
What do you folks think? If I'm missing any pertinent information here, please let me know. The difficulty I'm having is that I don't know what I don't know about Python/Tkinter yet.
Thanks, I really appreciate any insight and direction.
Solved (thanks Bryan Oakley & TigerhawkT3): Due to Python's use of indentation as part of its syntax, I had created a function bck() which, when called, includes the code for the entirety of the rest of the GUI. To solve this problem after it was pointed out, I drew heavily from:
Python def function: How do you specify the end of the function?
You appear you have a simple indentation error. It seems like you intend for bck to have four lines of code, but because almost all of the remaining code is indented the same, it is all considered to be part of bck.
I want to create two windows.
Behaviour of windows:
Window1 has a label and a button. When I click on that button, 2nd window has to open. 2nd window have a label.
Problem:
Label in 2nd window is not appearing.
Code:
def window1():
root = tkinter.Tk()
root.geometry("200x200")
root.title("Window1")
var = tkinter.StringVar()
tkinter.Label(root, textvariable = var, bg = "red").grid(row = 0, column = 0)
var.set("This is window1")
tkinter.Button(root, text = "Button1", command = OnBut).grid(row = 0, column = 1)
root.mainloop()
def OnBut():
window2()
def window2():
root = tkinter.Tk()
root.title("Window2")
root.geometry("250x250")
var = tkinter.StringVar()
tkinter.Label(root, textvariable = var, bg = "blue").grid(row = 1, column = 0, padx = 3, pady = 3)
tkinter.Button(root, text = "Button", command = OnBut).grid(row = 0, column = 1, padx =3, pady = 3)
var.set("This is window2") #not appearing <-- problem
root.mainloop()
window1()
when I call window2 seperately, its working fine. Why label not printing in 2nd window, by clicking on button?
You don't really need a real function for your command in this case. This is what lambdas are made for -- callbacks!
Remove your onBut function (which is the problem anyway, since root isn't defined there) and replace your command in each button with:
command = lambda: window2(root)
Currently, when you call onBut, it tries to do:
window2(root)
# HELP I DON'T KNOW WHAT root IS!!
This throws a NameError on my copy. Your code may vary.
Since you're editing willy nilly, let me just write you some working code.
import tkinter
def run():
root = tkinter.Tk()
root.title("Window1")
s_var = tkinter.StringVar()
tkinter.Label(root, textvariable = s_var).pack()
tkinter.Button(root, text = "Button", command = lambda: makewindow(root)).pack()
s_var.set("Window #1")
def makewindow(root):
top = tkinter.Toplevel(root)
top.title("Window2")
s_var = tkinter.StringVar()
tkinter.Label(top, textvariable = s_var).pack()
tkinter.Button(top, text = "Button", command = lambda: makewindow(root)).pack()
s_var.set("Window #2")
if __name__ == "__main__":
run()
I'm working on the GUI for a simple quiz app using Tkinter in Python 2.7.
Thus far, I have begun to set up my frame. I've put a scrollbar inside of a Text widget named results_txtbx to scroll up and down a list noting the player's performance on each question. I've been using grid since it's easier for me to manage.
from Tkinter import *
class Q_and_A:
def __init__(self, master):
frame = Frame(master)
Label(master).grid(row = 4)
results_txtbx = Text(master)
results_scrbr = Scrollbar(results_txtbx)
results_scrbr.grid(sticky = NS + E)
results_txtbx.config(width = 20, height = 4, wrap = NONE, yscrollcommand = results_scrbr.set)
results_txtbx.grid(row = 3, column = 1, padx = 12, sticky = W)
root = Tk()
root.wm_title("Question and Answer")
root.resizable(0, 0)
app = Q_and_A(root)
root.mainloop()
What happens is that when it runs, results_txtbx resizes to fit the scrollbar. Is there any way to make it keep its original size using grid?
You don't want to use a text widget as the master for a scrollbar. Like any other widget, if you pack or grid the scrollbar in the text widget, the text widget will shrink or expand to fit the scrollbar. That is the crux of your problem.
Instead, create a separate frame (which you're already doing), and use that frame as the parent for both the text widget and the scrollbars. If you want the appearance that the scrollbars are inside, set the borderwidth of the text widget to zero, and then give the containing frame a small border.
As a final usability hint, I recommend not making the window non-resizable. Your users probably know better what size of window they want than you do. Don't take that control away from your users.
Here's (roughly) how I would implement your code:
I would use import Tkinter as tk rather than from Tkinter import * since global imports are generally a bad idea.
I would make Q_and_A a subclass of tk.Frame so that it can be treated as a widget.
I would make the whole window resizable
I would separate widget creation from widget layout, so all my layout options are in one place. This makes it easier to write and maintain, IMO.
As mentioned in my answer, I would put the text and scrollbar widgets inside a frame
Here's the final result:
import Tkinter as tk
class Q_and_A(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master, borderwidth=1, relief="sunken")
self.label = tk.Label(self)
self.results_txtbx = tk.Text(self, width=20, height=4, wrap="none",
borderwidth=0, highlightthickness=0)
self.results_scrbr = tk.Scrollbar(self, orient="vertical",
command=self.results_txtbx.yview)
self.results_txtbx.configure(yscrollcommand=self.results_scrbr.set)
self.label.grid(row=1, columnspan=2)
self.results_scrbr.grid(row=0, column=1, sticky="ns")
self.results_txtbx.grid(row=0, column=0, sticky="nsew")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
root = tk.Tk()
root.wm_title("Question And Answer")
app = Q_and_A(root)
app.pack(side="top", fill="both", expand=True)
root.mainloop()
Set results_scrbr.grid(row = 3, column = 2) next to results_txtbx.grid(row = 3,column = 1, padx = 4), sticky is not needed because window is not resizable, and i lowered the padx so scrollbar is closer to text.
Also to make the results_txtbx vertically scrollable, add results_scrbr.config(command=results_txtbx.yview)
Here is a working code...
from Tkinter import *
class Q_and_A:
def __init__(self, master):
frame = Frame(master)
Label(master).grid(row = 4)
results_txtbx = Text(master)
results_scrbr = Scrollbar(master)
results_scrbr.grid(row = 3, column = 2)
results_scrbr.config(command=results_txtbx.yview)
results_txtbx.config(width = 20, height = 4,
wrap = NONE, yscrollcommand = results_scrbr.set)
results_txtbx.grid(row = 3, column = 1, padx = 4)
root = Tk()
root.wm_title("Question and Answer")
root.resizable(0, 0)
app = Q_and_A(root)
root.mainloop()
My implemented solution:
I needed to add more widgets to the app, so I bound the Scrollbar and Text widgets to another label and put that in the proper column the code (trimmed for readability) is below:
import Tkinter as tk
class Q_and_A(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.label = tk.Label(self)
#Set up menu strip
self.main_menu = tk.Menu(self)
self.file_menu = tk.Menu(self.main_menu, tearoff = 0)
self.file_menu.add_command(label = "Exit", command = self.quit)
self.main_menu.add_cascade(label = "File", menu = self.file_menu)
self.master.config(menu = self.main_menu)
#Set up labels
self.question_lbl = tk.Label(self, text = "Question #: ", padx = 12, pady = 6)
self.question_lbl.grid(row = 0, sticky = "w")
tk.Label(self, text = "Hint: ").grid(row = 1, sticky = "w", padx = 12, pady = 6)
tk.Label(self, text = "Answer: ").grid(row = 2, sticky = "w", padx = 12, pady = 6)
tk.Label(self, text = "Results: ").grid(row = 3, sticky = "nw", padx = 12, pady = 6)
tk.Label(self).grid(row = 4)
#Set up textboxes
self.question_txtbx = tk.Entry(self)
self.question_txtbx.config(width = 60)
self.question_txtbx.grid(row = 0, column = 1, padx = 12, columnspan = 3, sticky = "w")
self.help_txtbx = tk.Entry(self)
self.help_txtbx.config(width = 40)
self.help_txtbx.grid(row = 1, column = 1, columnspan = 2, padx = 12, sticky = "w")
self.answer_txtbx = tk.Entry(self)
self.answer_txtbx.config(width = 40)
self.answer_txtbx.grid(row = 2, column = 1, columnspan = 2, padx = 12, sticky = "w")
self.results_label = tk.Label(self)
self.results_txtbx = tk.Text(self.results_label, width = 10, height = 4, wrap = "none", borderwidth = 1, highlightthickness = 1)
self.results_scrbr = tk.Scrollbar(self.results_label, orient = "vertical", command = self.results_txtbx.yview)
self.results_txtbx.configure(yscrollcommand = self.results_scrbr.set)
self.label.grid(row = 1)
self.results_label.grid(row = 3, column = 1, padx = 11, sticky = "w")
self.results_scrbr.grid(row = 0, column = 1, sticky = "nse")
self.results_txtbx.grid(row = 0, column = 0, sticky = "w")
root = tk.Tk()
root.wm_title("Question and Answer")
#A note: The window is non-resizable due to project specifications.
root.resizable(0, 0)
app = Q_and_A(root)
app.pack(side = "top", fill = "both")
root.mainloop()
I'll keep storage in nested labels as a reference for myself for when I need to group things close together, unless there's some reason it should be avoided. Worked very well here. Thanks to Bryan for the advice.