Scrollbar on top of listbox issue - python-3.x

I'm trying to attach a scrollbar element to the side of the listbox element.
When I try to put scrollbar to the side of the listbox, the scrollbar jumps to top of the listbox. I tried to grid it and pack it. None of them seems to work.
It looks like this:
The code:
from tkinter import *
class MyApp(Frame):
def __init__(self, root):
root.title('My app')
super().__init__(root)
self.grid()
self.createWindow()
def createWindow(self):
self.listbox = Listbox(self)
self.listbox.grid(row=0, column=0)
self.scrollbar = Scrollbar(self.listbox)
self.scrollbar.grid(row=0, column=1)
if __name__ == '__main__':
ma = MyApp(Tk())
mainloop()

The thing that I was trying to accomplish can't be done in Tkinter. I tried to use Wx module and it works great.

Related

tkinter catch value from lambda function

I'm trying to wrap my head around this problem.
Say I have a code like this:
def get_input(data_A, data_B):
all_data = [data_A.get(),dataB.get()]
return(all_data)
def the_gui():
root = Tk()
data_A = Entry(root)
data_B = Entry(root)
button = Button(root, text='Submit', command=lambda: get_input(data_A, data_B))
mainloop()
My goal is to get the value of data_A and data_B once I clicked the submit button.
I tried to use global variable and everything, but I kept failing to catch the value.
The only thing that works is when I put the whole get_input() function inside the_gui() function. However, I don't think that's a good practice to implement.
Any suggestions?
Here is a simple example of how you could write this to get the results you are looking for.
When using global is that all your root window and related fields are in a function. So you would have to define global in both function and this is not what you want to do.
Typically you will want to write the root window in the global namespace and not in a function or write it into a class so you can avoid global's all-together.
button = Button(...) may not be doing what you think it is. This does not return a value from the command once clicked. Tkinter buttons do not care about anything being returned. So you have to record that value elsewhere.
I am not sure how you code is working as you do not use geometry managers and mainloop() should be attached to the root window so I have added those in as well.
Example 1:
import tkinter as tk
def get_input():
global a_and_b
a_and_b = [data_a.get(), data_b.get()]
# If you want to keep a running record of all values submitted
# then you can do this instead:
# a_and_b.append([data_a.get(), data_b.get()])
def print_a_b():
print(a_and_b)
root = tk.Tk()
a_and_b = []
data_a = tk.Entry(root)
data_b = tk.Entry(root)
data_a.pack()
data_b.pack()
tk.Button(root, text='Submit', command=get_input).pack()
tk.Button(root, text='Print A/B List', command=print_a_b).pack()
root.mainloop()
Example 2 using OOP:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.a_and_b = []
self.data_a = tk.Entry(self)
self.data_b = tk.Entry(self)
self.data_a.pack()
self.data_b.pack()
tk.Button(self, text='Submit', command=self.get_input).pack()
tk.Button(self, text='Print A/B List', command=self.print_a_b).pack()
def get_input(self):
self.a_and_b = [self.data_a.get(), self.data_b.get()]
def print_a_b(self):
print(self.a_and_b)
if __name__ == '__main__':
App().mainloop()

Python Tkinter Check if Frame exists

I am trying to do the following:
Create a Tkinter App with a 'File' menu.
The File Menu has 2 options, Add and View.
The Add option adds a Frame and then adds a Label widget (Label 1) in the frame.
If I then select the View option form the file menu, it should print out whether or not a Frame widget already exists.
Following is my attempt at it, but I receive the error
AttributeError: 'Test' object has no attribute 'tk'
when I select the View option, can someone please help point out what I am missing here?
from tkinter import Tk, Menu, Label, Frame
class Test():
def __init__(self):
self.gui = Tk()
self.gui.geometry("600x400")
menu = Menu(self.gui)
new_item1 = Menu(menu)
menu.add_cascade(label='File', menu=new_item1)
new_item1.add_command(label='Add', command=self.addlbl)
new_item1.add_command(label='View', command=self.viewlbl)
self.gui.config(menu=menu)
self.gui.mainloop()
def addlbl(self):
f=Frame()
f.pack()
lbl1 = Label(f, text="Label 1").grid(row=0, column=0)
def viewlbl(self):
print(Frame.winfo_exists(self))
T=Test()
I replicated your problem. I got the code below to work using Python3.4 on Linux. f needs to become self.f. I named it self.frame. This enables the frame to be accessed outside of the method it is created in.
from tkinter import Tk, Menu, Label, Frame
class Test():
def __init__(self):
self.gui = Tk()
self.gui.geometry("600x400")
menu = Menu(self.gui)
new_item1 = Menu(menu)
menu.add_cascade(label='File', menu=new_item1)
new_item1.add_command(label='Add', command=self.addlbl)
new_item1.add_command(label='View', command=self.viewlbl)
self.gui.config(menu=menu)
self.gui.mainloop()
def addlbl(self):
self.frame = Frame(self.gui)
self.frame.pack()
lbl1 = Label(self.frame, text="Label 1")
lbl1.grid(row=0, column=0)
def viewlbl(self):
print('frame exists {}'.format(self.frame.winfo_exists()))
T=Test()

Python3 class function definition confusion about NameError

I'm new to programming and this is my first post on the site. I'm sure I'm making a dumb mistake, but I'd really appreciate a push in the right direction. I'm trying to make a calculator, and want to make a function that produces a Button object for numbers. When I try to run this I get the error:
'NameError: name 'num_but_gen' is not defined'
Here is the code:
from tkinter import *
WINDOW_HEIGHT = 300
WINDOW_WIDTH = 325
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def num_but_gen(self, disp, xloc=0, yloc=0, wid=0, hei=0):
self.Button(text='{}'.format(disp),height=hei, width=wid)
self.place(x=xloc, y=yloc)
def init_window(self):
self.master.title('Calculator')
self.pack(fill=BOTH, expand=1)
Button1 = num_but_gen('1', xloc=0, yloc=200, wid=40, hei=40)
root = Tk()
app = Window(root)
root.geometry("{}x{}".format(WINDOW_WIDTH,WINDOW_HEIGHT))
root.mainloop()
Any help would be greatly appreciated! Also bonus points to anyone with suggestions on how to better phrase my question titles in future posts.
jasonharper is right, you need to add self in front of num_but_gen, but there are other problems in your code.
In num_but_gen:
your window class does not have a Button attribute, so you need to remove self. in front of Button
it is not the Window instance but the button that you want to place
you don't need to use text='{}'.format(disp), text=disp does the same.
In init_window:
you store the result of num_but_gen in a variable, but this function returns nothing so that's useless (and capitalized names should not be used for variables, but for class names only)
the width option of a button displaying text is in letters, not in pixels and its height option is in text lines, so wid=40, hei=40 will create a very big button. If you want to set the button size in pixels, you can do it through the place method instead.
Here is the corresponding code:
import tkinter as tk
WINDOW_HEIGHT = 300
WINDOW_WIDTH = 325
class Window(tk.Frame):
def __init__(self, master = None):
tk.Frame.__init__(self, master)
self.master = master
self.init_window()
def num_but_gen(self, disp, xloc=0, yloc=0, wid=0, hei=0):
button = tk.Button(self, text=disp)
button.place(x=xloc, y=yloc, height=hei, width=wid)
def init_window(self):
self.master.title('Calculator')
self.pack(fill=tk.BOTH, expand=1)
self.num_but_gen('1', xloc=0, yloc=200, wid=40, hei=40)
root = tk.Tk()
app = Window(root)
root.geometry("{}x{}".format(WINDOW_WIDTH,WINDOW_HEIGHT))
root.mainloop()

Hiding canvas, part 2

Gotten further along but now I can't get rid of canvaspopup, even with pack_forget(). Everything else seems to be working okay, granted I'm testing it with a very dumbed down version of the full program so I can post code here without posting 1000+ lines of code.
import tkinter as tk
from tkinter import *
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.keys = dict.fromkeys(('Left', 'Right', 'Up', 'Down', 'Prior', 'Next', 'Shift_L', 'Alt'))
self.frame = tk.Frame(self,bg='gray', width=1366, height=714)
self.frame.pack()
self.canvas = tk.Canvas(self, background="black", width=714, height=714)
self.canvas.pack(fill='both', expand=True)
self.canvas.place(x=652,y=0)
self.canvas.bind("<Alt_L><w>", self.mgram)
self.canvas.bind("<Left>", self.left)
self.canvas.focus_set()
def left(self,event):
print('canvas left')
def popleft(self,event):
print('popup left')
def popescape(self,event):
self.canvas.focus_set()
self.canvaspopup.pack_forget()
def mgram(self,event):
self.canvaspopup = Canvas(self, width=800, height=614)
self.canvaspopup.pack(fill='both', expand=True)
self.canvaspopup.place(x=284,y=52)
self.png = Label(self.canvaspopup)
self.png.pack()
self.popupimage1 = PhotoImage(file='example.png')
self.canvaspopup.bind("<Left>", self.popleft)
self.canvaspopup.bind("<Escape>", self.popescape)
self.canvaspopup.focus_set()
self.png.config(image=self.popupimage1)
s = Canvas(self.canvaspopup, width=800, height=14)
s.pack_propagate(0)
s.place(x=0,y=600)
v = Label(s, fg='black',borderwidth=0,anchor='center',text = 'Image of: ')
v.pack()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
How do I make the popup disappear, supposedly in my previous question Bryan said I could use pack_forget() but that isn't working here. Everything else seems like it working correctly. Still not sure why I couldn't get focus_set to work earlier this morning at home. Not sure what I've changed now???
In an earlier question you asked how to hide a window managed with pack, and I said pack_forget. In this code you're using place so the command would be place_forget.
You call pack but immediately call place right after. Only one geometry manager (pack, place, grid) can manage a widget, and it's always the last one that you use on that widget.

Tkinter Assign Different Functions To Each Ranged Button

the problem is its only works for one global function.. how to assign each button for a different function
from tkinter import*
class mainWindow:
def __init__(self,master):
mainFrame=Frame(master)
mainFrame.pack()
self.cv=Canvas(scrollregion=(0,0,200,1200),width=200,height=1200,bg='green')
self.scb=Scrollbar(command=self.cv.yview)
self.cv.pack(side=LEFT)
self.scb.pack(side=RIGHT,fill=Y)
self.cv.configure(yscrollcommand=self.scb.set)
#frame on canvas to pack title frame
self.cvFrame=Frame(self.cv)
self.cvFrame_window=self.cv.create_window(5,5,anchor=NW,window=self.cvFrame)
self.tFrame=Frame(self.cvFrame)
self.tFrame.pack(in_=self.cvFrame)
self.t=['Site Preparation','WBLFF','Frame','Roof Construction','Roof Finishes',]
self.tr=[0,1,2,3,4,5]
#range button created inside one frame
for i in range(5):
self.tButton=Button(self.tFrame,text=self.t[i],bg='purple',fg='white',width=20,relief=GROOVE)
self.tButton.grid(in_=self.tFrame,row=self.tr[i])
for i in range(0,1):
self.tButton.bind('<Button-1>',self.subT1)
#open up new subtitle under main title
def subT1(self,event):
self.s=['Site Preparation','Site Clearence','Earth Works']
self.sc=['dark violet','yellow','yellow']
self.sf=['white','black','black']
self.sr=[0,1,2]
self.sFrame=Frame(self.tFrame)
self.sFrame.grid(in_=self.tFrame,row=0)
for x in range(3):
self.sBtn=Button(self.sFrame,text=self.s[x],bg=self.sc[x],fg=self.sf[x],width=20,relief=GROOVE)
self.sBtn.grid(in_=self.sFrame,row=self.sr[x])
self.sBtn.bind('<Button-1>',self.destF)
#detroy frame back to main title
def destF(self,event):
self.sFrame.destroy()
#root window
root=Tk()
root.title('Built Up Rates')
root.geometry('220x600+0+0')
A=mainWindow(root)
root.mainloop()
Open Up The Program
Open up the subtitle by clicking the button
the problem is its only works for one global function.. how to assign each button for a different function
Firstly: you can use Button(command=function_name)
in place of bind('< Button-1 >',function_name)
Create list of functions
functions = [self.subT1, self.other_function]
for i in range(2):
Button(command=functions[i])
def subT1(self): # without `event` or with `event=None`
from tkinter import*
class mainWindow:
def __init__(self,master):
mainFrame=Frame(master)
mainFrame.pack()
self.cv=Canvas(scrollregion=(0,0,200,1200),width=200,height=1200,bg='green')
self.scb=Scrollbar(command=self.cv.yview)
self.cv.pack(side=LEFT)
self.scb.pack(side=RIGHT,fill=Y)
self.cv.configure(yscrollcommand=self.scb.set)
#Frame to Canvas
self.cvFrame=Frame(master)
self.cvFrame_window=self.cv.create_window(5,5,anchor=NW,window=self.cvFrame)
self.t=['Site Preparation','WBLFF','Frame','Roof Construction','Roof Finishes',]
self.tr=[0,1,2,3,4,5]
#range button created inside one frame
for i in range(5):
self.tButton=Button(self.cvFrame,text=self.t[i],bg='purple',fg='white',width=20,relief=GROOVE)
self.tButton.grid(in_=self.cvFrame,row=self.tr[i])
if i in range(0,1):
self.tButton.bind('<Button-1>',self.subT1)
if i in range(1,2):
self.tButton.bind('<Button-1>',self.subT2)
def subT1(self,event):
self.s=['Site Preparation','Site Clearence','Earth Works']
self.sc=['dark violet','yellow','yellow']
self.sf=['white','black','black']
self.sFrame=Frame(self.cvFrame)
self.sFrame.grid(in_=self.cvFrame,row=0)
for x in range(3):
self.sBtn=Button(self.sFrame,text=self.s[x],bg=self.sc[x],fg=self.sf[x],width=20,relief=GROOVE)
self.sBtn.grid(in_=self.sFrame,row=x)
self.sBtn.bind('<Button-1>',self.destF)
def subT2(self,event):
self.s=['WBLFF','Pile Cap','Column Stump','Ground Beam','Ground Slab']
self.sc=['dark violet','yellow','yellow','yellow','yellow']
self.sf=['white','black','black','black','black']
self.sFrame=Frame(self.cvFrame)
self.sFrame.grid(in_=self.cvFrame,row=1)
for x in range(5):
self.sBtn=Button(self.sFrame,text=self.s[x],bg=self.sc[x],fg=self.sf[x],width=20,relief=GROOVE)
self.sBtn.grid(in_=self.sFrame,row=x)
self.sBtn.bind('<Button-1>',self.destF)
#detroy frame back to main title
def destF(self,event):
self.sFrame.destroy()
#root window
root=Tk()
root.title('Built Up Rates')
root.geometry('220x600+0+0')
A=mainWindow(root)
root.mainloop()
got it :) by using 'if' statements... works well in ranged object and assign the parameters is there any other way to make it much more simpler like inheritance or global definition perhaps?

Resources