Scrollbar displaying but not scrolling the dynamically created combobox widgets - python

I have created a canvas, scrollbar on a label frame. Using a button, dynamically creating the combobox widgets on the canvas. But the scrollbar not scrolling through the dynamically created.
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry("1366x705+0+0")
ExtRole_Dest_LF = ttk.LabelFrame(root, text='ExternalRoles', width =600)
ExtRole_Dest_LF.place(relx=0.225, rely=0.113, relheight=0.376, relwidth=0.264)
canvas=Canvas(ExtRole_Dest_LF,bg='#FFFFFF', height = 110, width = 335, scrollregion=(0,0,500,800))
canvas.grid(column = 0, row = 0, sticky = 'news')
canvas.grid_propagate(0)
canvas.config(scrollregion=canvas.bbox("all"))
vbar=Scrollbar(ExtRole_Dest_LF,orient=VERTICAL, command=canvas.yview)
vbar.grid(row = 0, column = 1, sticky='ns')
canvas.configure(yscrollcommand=vbar.set)
global System_Dest_row
System_Dest_row = 1
def fn_SystemDest():
global System_Dest_row
System_Dest_col = 0
System_Dest_cb = ttk.Combobox(canvas, values=['a','s','d','g'], width=15)
System_Dest_cb.grid(row=System_Dest_row, column=System_Dest_col, padx=10, pady = 5)
deletebutton = Button(canvas, text="X")
deletebutton.grid(row=System_Dest_row, column=System_Dest_col + 1, padx=10, pady = 5)
System_Dest_row += 1
AddButton = Button(root, text = 'Add', command =fn_SystemDest )
AddButton.grid(column = 3,row = 3)
root.mainloop()
Can I make scrollbar to scroll through the dynamical combobox widgets
Can I grt solution in any otherway to scroll the combobox widgets on LabelFrame/Frame

You can't scroll things added to a canvas with grid. A canvas can only scroll items added to it with one of the create_ functions.

Related

create a scrollbar on entry with cavnas, python tkinter

I'm trying to put a scrollbar on entry that created on canvas..
I've tried this code
from tkinter import *
root = Tk()
root.geometry('400x600')
root.resizable(0,0)
page = Canvas(root, width=400, height=600, bd=0, highlightthickness=0,scrollregion=(0,0,500,500))
MyImage1 = PhotoImage(file='Study With4.png')
CanvasImage = page.create_image(0,0,image= MyImage1, anchor='nw')
entry =Text(page,height=29,width =46,wrap=WORD,bg='#F8F8F8')
scroll = Scrollbar(entry, orient=VERTICAL)
scroll.pack(side=RIGHT, fill=Y)
scroll.config(command=page.yview)
page.config(yscrollcommand=scroll.set)
page.create_window(200,285, window=entry)
page.pack()
mainloop()
but it doesn't work and I don't know where is the problem.
I've made minimal changes to your code, the biggest is creating a Frame called "label"
to contain Text and Scrollbar then inserting that into Canvas window.
Your scroll object was not defined properly with confusion around page and entry objects.
from tkinter import *
root = Tk()
root.geometry('500x600')
root.resizable(0,0)
page = Canvas(
root, width = 500, height = 600, bd = 0,
highlightthickness = 0,scrollregion = (0,0,1000,1000))
page.pack(side = LEFT, fill = BOTH, expand = True)
MyImage1 = PhotoImage(file='Study With4.png')
CanvasImage = page.create_image(0, 0, image = MyImage1, anchor = NW)
label = Frame(root)
label.pack(fill = BOTH, expand = True)
entry = Text(label, height = 29, width = 46, wrap = NONE, bg = '#F8F8F8')
entry.pack(side = LEFT, fill = BOTH, expand = True)
scroll = Scrollbar(label, orient = VERTICAL)
scroll.pack(side=RIGHT, fill=Y)
scroll.config(command = entry.yview)
entry.config(yscrollcommand = scroll.set)
page.pack()
page.create_window(25, 25, window = label, anchor = NW)
mainloop()

Update text widget in tkinter from function

The problem:
I am trying to update the same text widget box from a function that contains some text. Instead a whole new text window appears every time.
Here is my code:
from tkinter import *
import os
#Tkinter graphics
homepage = Tk()
homepage.title("My first GUI")
# set size of window
homepage.geometry('1200x400')
# Add image file
bg = PhotoImage(file = "maxresdefault.png")
# Show image using label
label1 = Label( homepage, image = bg)
label1.place(x = 0, y = 0)
label2 = Label( homepage, text = "Test App")
label2.pack()
# Create Frame
frame1 = Frame(homepage)
frame1.pack()
#button initatiors
def buttonupdate():
S = Scrollbar(homepage)
T = Text(homepage, height=100, width=30)
T.pack()
T.pack(side=RIGHT, fill= Y)
S.pack(side = RIGHT, fill = Y)
S.config(command=T.yview)
T.insert(END, "test")
T.config(yscrollcommand=S.set, state=DISABLED)
# Static buttons
tickets30button = Button(text = "This is button 1", command=buttonupdate)
tickets30button.place(x=0, y=26)
mcibutton = Button(text = "This is button 2")
mcibutton.place(x=0, y=52)
hdebutton = Button(text = "This is button 3")
hdebutton.place(x=0, y=78)
homepage.mainloop()
Here is the result if I click on the first button three times:
Let me know if you have any suggestions that I can try.
Thank you for your time,
I was able to update my text window instead of create a new one, upon each click of a button, thanks to #TheLizzard.
He mentioned to move the section of code that creates the text window outside of the function and keep the section of code that creates the text, inside the function.
Before:
#button initiators
def buttonupdate():
S = Scrollbar(homepage)
T = Text(homepage, height=100, width=30)
T.pack()
T.pack(side=RIGHT, fill= Y)
S.pack(side = RIGHT, fill = Y)
S.config(command=T.yview)
T.insert(END, "test")
T.config(yscrollcommand=S.set, state=DISABLED)
After: (UPDATED)
S = Scrollbar(homepage)
T = Text(homepage, height=100, width=30)
T.pack(side=RIGHT, fill= Y)
S.pack(side = RIGHT, fill = Y)
S.config(command=T.yview)
T.config(yscrollcommand=S.set, state=DISABLED)
#button initatiors
def myTicketstatusbutton():
T.delete(1.0,END)
T.insert(END, "test")

Creating and Getting values from tkinter check boxes using for loop

This code is a part of my project in which I have to manage the attendance of 50 (or more) students.
The thing I want is that all the checkboxes should initially be 'checked' (showing the present state) and when I uncheck random checkboxes (to mark the absent) and click the Submit button (yet to be created at the bottom of the window), I should get a list with 'entered date' as first element and the roll numbers i.e. 2018-MC-XX as other elements.
For example: ['01/08/2020', '2018-MC-7', '2018-MC-11', '2018-MC-23', '2018-MC-44']
Actually my plan is when I will get a list I will easily write it to a text file. Also, if there is another way of creating multiple scrollable checkboxes without packing them inside a canvas then please do tell!
from tkinter import *
from tkcalendar import DateEntry
root = Tk()
root.geometry('920x600+270+50')
root.minsize(920,600)
Attendance_frame = Frame(root) ### Consider it a Main Frame
Attendance_frame.pack()
attendaceBox = LabelFrame(Attendance_frame, text = 'Take Attendance', bd = 4, relief = GROOVE, labelanchor = 'n',font = 'Arial 10 bold', fg = 'navy blue', width = 850, height = 525) # A Label Frame inside the main frame
attendaceBox.pack_propagate(0)
attendaceBox.pack(pady = 15)
dateFrame = Frame(attendaceBox) # A small frame to accommodate date entry label & entry box
dateFrame.pack(anchor = 'w')
font = 'TkDefaultFont 10 bold'
date_label = Label(dateFrame, text = 'Enter Date : ', font = font).grid(row = 0, column = 0, sticky = 'w', padx = 10, pady = 10)
date_entry = DateEntry(dateFrame, date_pattern = 'dd/mm/yyyy', showweeknumbers = FALSE, showothermonthdays = FALSE)
date_entry.grid(row = 0, column = 1, sticky = 'w')
noteLabel = Label(attendaceBox, text = 'Note: Uncheck the boxes for absentees').pack(anchor = 'w', padx = 10, pady = 5)
canvas = Canvas(attendaceBox, borderwidth=0, background="#ffffff")
checkFrame = Frame(canvas, width = 100, height = 50)
vsb = Scrollbar(canvas, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.pack_propagate(0)
canvas.create_window((4,4), window=checkFrame, anchor="nw")
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
checkFrame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
for i in range(0,51): # A loop to create Labels of students roll numbers & names
c = Checkbutton(checkFrame, text = f"{'2018-MC-'+str(i+1)} Student {i+1}")
c.grid(row = i, column = 0, padx = 10, sticky = 'w')
mainloop()
First you need StringVar for each Checkbutton in order to get the state of the Checkbuttons later. Then you can use a list to hold the StringVars so that you can access them later. You can set the onvalue of the checkbuttons to the roll number associated to them.
Also you can use Text widget instead of Canvas+Frame. Below is an example:
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
text = tk.Text(frame, width=40, height=20)
text.pack(side=tk.LEFT, fill=tk.BOTH)
vars = []
for i in range(51):
rollnum = '2018-MC-'+str(i+1)
var = tk.StringVar(value=rollnum)
cb = tk.Checkbutton(text, text=rollnum, variable=var, onvalue=rollnum, offvalue='', bg='white')
text.window_create('end', window=cb)
text.insert('end', '\n')
vars.append(var)
vsb = tk.Scrollbar(frame, orient=tk.VERTICAL, command=text.yview)
vsb.pack(side=tk.RIGHT, fill=tk.Y)
text.config(yscrollcommand=vsb.set)
def submit():
# extract roll numbers for checked checkbuttons
result = [var.get() for var in vars if var.get()]
print(result)
tk.Button(root, text='Submit', command=submit).pack()
root.mainloop()

How to clear part of a tkinter Canvas and show something when submit is pressed?

I'm creating a simple madlib style game and I've come into a bit of a problem. I cannot get the canvas to clear and show the results.
The following code places an image as the background of a canvas. It then places labels and entry fields in 2 columns for all of the words to be inserted. There is a submit button at the bottom of the page. I can't figure out how to get it clear everything except the background image, so that it can display the story, with the users words inserted. If i place it in the callback(), it clears just the background and keeps everything else. I want the opposite.
from tkinter import *
from PIL import Image, ImageTk
canvas_width = 360
canvas_height = 525
file = r"C:\Users\kraak\Desktop\PyCharm Community Edition 2017.1.2\borderedpaper.GIF"
master = Tk()
canvas = Canvas(master, width=canvas_width, height=canvas_height)
old_img = PhotoImage(file=file)
new_img = old_img.subsample(3, 3)
canvas.create_image(-11, -10, anchor=NW, image=new_img)
canvas.create_window(0, 0, height=1, width=1, anchor=NW)
canvas.create_text(0, 0, text="Test")
e1 = Entry(canvas)
canvas.create_window(250, 60, window=e1, height=15, width=100)
label = Label(text="Enter an adjective.")
label.place(x=40, y=50)
e1.focus_set()
e2 = Entry(canvas)
canvas.create_window(250, 85, window=e2, height=15, width=100)
label = Label(text="Enter a nationality.")
label.place(x=40, y=75)
e2.focus_set()
def callback():
print("Pizza was invented by a " + (e1.get()) + " " + (e2.get()))
def answer():
button = Button(text="Submit.", command=callback)
button.place(x=150, y=460)
answer()
canvas.pack()
mainloop()
As Bryan Oakley suggested you can store the id's of the widgets you want to get rid of in a list to make it easier to destroy() them all in the callback() function. Here's showing the modification to your code that would do that—note the lines with a # ADDED comments.
from tkinter import *
from PIL import Image, ImageTk
canvas_width = 360
canvas_height = 525
file = r"C:\Users\kraak\Desktop\PyCharm Community Edition 2017.1.2\borderedpaper.GIF"
master = Tk()
canvas = Canvas(master, width=canvas_width, height=canvas_height)
canvas_entry_widgets = [] # ADDED
old_img = PhotoImage(file=file)
new_img = old_img.subsample(3, 3)
canvas.create_image(-11, -10, anchor=NW, image=new_img)
canvas.create_window(0, 0, height=1, width=1, anchor=NW)
canvas.create_text(0, 0, text="Test")
e1 = Entry(canvas)
canvas.create_window(250, 60, window=e1, height=15, width=100)
label = Label(text="Enter an adjective.")
label.place(x=40, y=50)
e1.focus_set()
canvas_entry_widgets.append(e1) # ADDED
e2 = Entry(canvas)
canvas.create_window(250, 85, window=e2, height=15, width=100)
label = Label(text="Enter a nationality.")
label.place(x=40, y=75)
e2.focus_set()
canvas_entry_widgets.append(e2) # ADDED
def callback():
print("Pizza was invented by a " + (e1.get()) + " " + (e2.get()))
# destroy the canvas entry widgets and clear the list # ADDED
while canvas_entry_widgets: # ADDED
widget = canvas_entry_widgets.pop() # ADDED
widget.destroy() # ADDED
def answer():
button = Button(text="Submit.", command=callback)
button.place(x=150, y=460)
answer()
canvas.pack()
mainloop()
Every widget has a destroy method which can be used to delete the widget. In your callback you can simply call this method for every widget:
def callback():
e1.destroy()
e2.destroy()
...
In your specific case, if you want to delete all the labels you will have to give them unique names. Or, to make this even easier, you can store all of your widgets and iterate over the list.

Python tkinter: access child widgets of a widget

I have a string 'currentMessage' and a Label to display it.
I have a Toplevel widget, which contains a Text widget to provide new value for 'currentMessage':
from tkinter import *
from tkinter import ttk
root = Tk()
mainFrame = ttk.Frame(root)
mainFrame.grid()
currentMessage = 'current Message'
ttk.Label(mainFrame, text = currentMessage).grid(padx = 10, pady = 10)
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.textBox.get(0.0, END)
def changeValues():
popup = Toplevel(mainFrame)
popup.grid()
textBox = Text(popup, width = 20, height = 5)
textBox.grid(column = 0, row = 0)
textBox.insert(END, 'new message here')
b = ttk.Button(popup, command = lambda: updateCurrentMessage(popup))
b.grid(column = 0, row = 1, padx = 5, pady = 5)
b['text'] = 'Update'
theButton = ttk.Button(mainFrame, command = changeValues, text = 'Click')
theButton.grid(padx = 10, pady = 10)
mainFrame.mainloop()
I tried to get the content of 'textBox' Text widget of the Toplevel by using this function:
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.textBox.get(0.0, END)
But I got an error
'Toplevel' object has no attribute 'textBox'
So how do I access content of the widget 'textBox', which is a child widget of 'popup' (this Toplevel widget is only created when function changeValues() is called)?
I think probably this is what you are looking for -- although I'm just guessing, because you are asking for a solution for a specific problem you think you have, however if I were you I would rethink what exactly do I want to do:
from tkinter import *
from tkinter import ttk
# Create Tk Interface root
root = Tk()
# Initialize mainFrame
mainFrame = ttk.Frame( root )
mainFrame.grid()
# Initialize label of mainframe
theLabel = ttk.Label( mainFrame, text='Current Message' )
theLabel.grid( padx=10, pady=10 )
def createPopup():
# Initialize popup window
popup = Toplevel( mainFrame )
popup.grid()
# Initialize text box of popup window
textBox = Text( popup, width=20, height=5 )
textBox.grid( column = 0, row = 0 )
textBox.insert( END, 'New Message Here' )
# Initialize button of popup window
button = ttk.Button( master = popup,
command = lambda: theLabel.config(text=textBox.get(0.0, END)),
text = 'Update')
button.grid( column=0, row=1, padx=5, pady=5 )
# Initialize button of main frame
theButton = ttk.Button( mainFrame, command=createPopup, text='Click' )
theButton.grid( padx=10, pady=10 )
# Enter event loop
mainFrame.mainloop()
There is a way indeed, like this:
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.nametowidget('textBox').get(0.0, END)
def changeValues():
popup = Toplevel(mainFrame)
popup.grid()
textBox = Text(popup, width = 20, height = 5, name = 'textBox')
textBox.grid(column = 0, row = 0)
textBox.insert(END, 'new message here')
b = ttk.Button(popup, command = lambda: updateCurrentMessage(popup))
b.grid(column = 0, row = 1, padx = 5, pady = 5)
b['text'] = 'Update'
You can choose whatever you want for the 'name'.

Categories