Pass selection Listbox topwindow to main window - python

This is my first real Python project. I am currently developing a GUI in Tkinter that allows the user to select Tasks and CVs to automatically compile documents using standard predefined task and CV texts from a database.
I have created two "Add" buttons in the main window to add Tasks and CVs that show a popup Listbox that allow the user to select the Tasks and CVs they want to have included in the commercial proposal. I have managed to create the popup window as a separate Class and it stores the selected Tasks in a list, but now I need to pass the list with the selected items to the Listbox in the main window when the user clicks the Select button in the popup window, but I cannot get my head around on how to do that.
I have researched on different fora and watched a variety of Youtube videos, but all focus on entry popups or some sort.
This is the code for the main window:
from tkinter import *
from Add import *
# make main window
root = Tk()
theLabel = Label(root, text="ProposalBuilder")
theLabel.grid(row=0)
# make frames
taskFrame = Frame(root)
taskFrame.grid(row=1, column=0)
CVFrame = Frame(root)
CVFrame.grid(row=1, column=1)
buildFrame = Frame(root)
buildFrame.grid(row=2, columnspan=2)
# add labels to frames
taskLabel = Label(taskFrame, text="Tasks")
taskLabel.pack()
CVLabel = Label(CVFrame, text="CVs")
CVLabel.pack()
# add listboxes to frames
scrollTask = Scrollbar(taskFrame, orient=VERTICAL)
listTask = Listbox(taskFrame, selectmode=MULTIPLE, yscrollcommand=scrollTask.set)
scrollTask.config(command=listTask.yview)
scrollTask.pack(side=RIGHT, fill=Y)
listTask.pack()
scrollCV = Scrollbar(CVFrame, orient=VERTICAL)
listCV = Listbox(CVFrame, selectmode=MULTIPLE, yscrollcommand=scrollCV.set)
scrollCV.config(command=listCV.yview)
scrollCV.pack(side=RIGHT, fill=Y)
listCV.pack()
# add commands to buttons
def addTask():
taskBox = Add('C:\\Users\\204703\\ProposalBuilder\\Database')
sel_test = taskBox.selection
def addCV():
CVBox = Add('C:\\Users\\204703\\ProposalBuilder\\Database')
# add buttons to frames
buttonAddTask = Button(taskFrame, text="Add", command=addTask)
buttonAddTask.pack(fill=X)
buttonDelTask = Button(taskFrame, text="Delete")
buttonDelTask.pack(fill=X)
buttonUpTask = Button(taskFrame, text="Up")
buttonUpTask.pack(fill=X)
buttonDownTask = Button(taskFrame, text="Down")
buttonDownTask.pack(fill=X)
buttonAddCV = Button(CVFrame, text="Add", command=addCV)
buttonAddCV.pack(fill=X)
buttonDelCV = Button(CVFrame, text="Delete")
buttonDelCV.pack(fill=X)
buttonUpCV = Button(CVFrame, text="Up")
buttonUpCV.pack(fill=X)
buttonDownCV = Button(CVFrame, text="Down")
buttonDownCV.pack(fill=X)
buttonBuild = Button(buildFrame, text="Build Proposal")
buttonBuild.pack(side=RIGHT)
root.mainloop()
This is the code for the separate class I created for the popup window:
from tkinter import*
from os import *
class Add:
def __init__(self, path):
# the slected tasks
self.selection = []
# make a frame
top = Toplevel()
# get file names from the directory (path) and save in list
self.path = path
self.dirList = listdir(self.path)
# add listbox to frames and populate with file names
self.scrollList = Scrollbar(top, orient=VERTICAL)
self.listbox = Listbox(top, selectmode=MULTIPLE, yscrollcommand=self.scrollList.set)
self.scrollList.config(command=self.listbox.yview)
self.scrollList.pack(side=RIGHT, fill=Y)
for item in self.dirList:
self.listbox.insert(END,item)
self.listbox.pack()
# add buttons to frame
self.selectButton = Button(top, text="Select", command=self.select)
self.selectButton.pack()
self.quitButton = Button(top, text="Quit", command=top.destroy)
self.quitButton.pack()
# identify selected rows and return a list with the selection
def select(self):
selectedRows = self.listbox.curselection()
for item in selectedRows:
self.selection.append(self.dirList[item])
print(self.selection)
return self.selection

Question: pass the list with the selected items to the Listbox in the main window
You need a reference of the main window Listbox.
I show, how to using listTask
Extend your __init__ to accept the reference target_listbox
class Add:
def __init__(self, target_listbox, path):
self.target_listbox = target_listbox
Insert the selected items into .target_listbox
Note: Your, return self.selection is useless, a Button.command can't process returns.
def select(self):
selectedRows = self.listbox.curselection()
for item in selectedRows:
self.target_listbox.insert(tk.END, self.dirList[item])
Pass the reference listTask to Add(...
taskBox = Add(listTask, ...)

Related

how to change the color of PanedWindow upon hovering over it, for multiple PanedWindow in tkinter?

I am trying to make PanedWindow change color when I hover mouse over it in tkinter.
now this works for a single iteration.
but when i try to do it for multiple panedwindows it only changes color of the last window.
import tkinter as tk
root = tk.Tk()
for i in range(10):
m1 = tk.PanedWindow(root, bd=4, relief="flat", bg="blue")
m1.pack()
def on_enter(e):
m1.config(background='OrangeRed3', relief="flat")
def on_leave(e):
m1.config(background='SystemButtonFace', relief="flat")
# Create a Button
button1 = tk.Button(m1, text=f"{i}")
button1.pack(pady=20)
# Bind the Enter and Leave Events to the Button
m1.bind('<Enter>', on_enter)
m1.bind('<Leave>', on_leave)
m1.add(button1)
tk.mainloop()
Since at each iteration of the loop all variables are overwritten, the functions are bound to the last created element. It is necessary to pass the desired element to the function. It is even better to collect everything created in dictionaries, so that in the future you can easily change them.
import tkinter as tk
from functools import partial
ms = {}
btns = {}
root = tk.Tk()
def on_enter(m, e):
m.config(background='OrangeRed3', relief="flat")
def on_leave(m, e):
m.config(background='SystemButtonFace', relief="flat")
for i in range(10):
ms[i] = tk.PanedWindow(root, bd=4, relief="flat", bg="blue")
ms[i].pack()
# Create a Button
btns[i] = tk.Button(ms[i], text=f"{i}")
btns[i].pack(pady=20)
# Bind the Enter and Leave Events to the Button
ms[i].bind('<Enter>', partial(on_enter, ms[i]))
ms[i].bind('<Leave>', partial(on_leave, ms[i]))
ms[i].add(btns[i])
tk.mainloop()

Dynamic dictionary entry tkinter

I'm trying to make a basic GUI in tkinter but I want it to be dynamic.
The end goal is to have a nested dictionary populated by this GUI and that the user can add as many items as he/she want.
I managed to enter a full single dictionary with some simple (and pretty repetetive) code:
for example:
Edit
from Tkinter import *
top = Tk()
L1 = Label(top, text="User Name")
L1.pack( side = LEFT)
E1 = Entry(top, bd =5)
E1.pack(side = RIGHT)
top.mainloop()
Now im looking for a way for the GUI to have a button (like a + sign) that will allow the user to open another single or a set of entries each time he/she presses the button and populate the dictionary accordingly.
Any Ideas?
You basically want to have a Label & Entry object. You can collect these objects in a collection type such as list or dict.
The example below does that using a list:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class LabelEntry(tk.Frame):
def __init__(self, master, text="User Name", *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self._label = tk.Label(self, text=text)
self._entry = tk.Entry(self, bd=5)
self._label.pack(side='left', fill='both', expand=True)
self._entry.pack(side='left', fill='both', expand=True)
def add_a_le(master, widgets_list):
widgets_list.append(LabelEntry(master))
widgets_list[-1].pack()
def main():
root = tk.Tk()
my_label_entries = list()
add_btn = tk.Button(root, text="Add")
add_btn['command'] = lambda m=root, ws=my_label_entries: add_a_le(m, ws)
add_btn.pack(side='bottom')
tk.mainloop()
if __name__ == '__main__':
main()

Tkinter buttons bellow grid to close and save to file [duplicate]

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 6 months ago.
I'm trying to create a grid of buttons that change colour with Tkinter.
from tkinter import *
class App():
def __init__(self, root):
self.root = root
buttonQ = Button(self.root, text = "Quit", command = endProgam())
buttonS = Button(self.root, text = "Save", command = saveToFile())
def Function(self):
self.grid = []
for i in range(5):
row = []
for j in range(5):
row.append(Button(self.root,width=6,height=3,command=lambda i=i, j=j: self.Click1(i, j),background='gray'))
row[-1].grid(row=i,column=j)
self.grid.append(row)
def Click1(self, i, j):
orig_color = self.grid[i][j].cget('bg')
#print(orig_color)
if orig_color=="red":
self.grid[i][j]["bg"]="gray"
else:
self.grid[i][j]["bg"]="red"
#self.grid[i][j]["bg"]="red"
#self.grid[i][j].configure(background="blue")
def endProgam(self):
# top.quit()
top.destroy()
def saveToFile(self):
# save matrix to file
top.destroy()
root = Tk()
app = App(root)
app.Function()
root.mainloop()
My problem is that I cannot add 2 buttons below the grid, one to quit and one to save into a file values based on the button colours (0-grey and 1-red as a matrix) and then quit.
File "--", line 37, in <module>
app = App(root)
File "--", line 6, in __init__
buttonQ = Button(self.root, text = "Quit", command = endProgam())
TypeError: endProgam() missing 1 required positional argument: 'self'
It's my first time coding in Python with Tkinter, so please be gentle :)
First, your indentation levels for your Class are off. The methods need to be indented another level or you'll get a TypeError for each method.
Second, for buttonQ and buttonS, make sure you are referencing the instance of the class, i.e.:
buttonQ = Button(self.root, text = "Quit", command = endProgam)
buttonS = Button(self.root, text = "Save", command = saveToFile)
should be:
buttonQ = Button(self.root, text = "Quit", command = self.endProgam)
buttonS = Button(self.root, text = "Save", command = self.saveToFile)
(Note the use of self)
As far as actually placing the buttons, I would recommend creating an additional frame to manage the layouts separately. You can create and place these just like widgets and it helps make managing the layouts much simpler.
For example:
class App():
def __init__(self, root):
self.root = root
self.TopFrame = Frame(root) # Create a top frame to place the original grid
self.BottomFrame = Frame(root) # Create a frame for the additional buttons
self.TopFrame.grid(row=0) # Place the Frame itself
self.BottomFrame.grid(row=6) # Place the new Frame directly below the first
# Changed to an instance variable to reference in Function method
buttonQ = Button(self.BottomFrame, text="Quit", command=self.endProgam)
buttonS = Button(self.BottomFrame, text="Save", command=self.saveToFile)
buttonS.grid(row=0, column=0, padx=10)
buttonQ.grid(row=0, column=1, padx=10)
def Function(self):
self.grid = []
for i in range(5):
row = []
for j in range(5):
row.append(Button(self.TopFrame,width=6,height=3,command=lambda i=i, j=j: self.Click1(i, j),background='gray'))
row[-1].grid(row=i,column=j)
self.grid.append(row)
Notice the new TopFrame and BottomFrame. The grid buttons are now sitting on the TopFrame while the BottomFrame contains the two new button widgets.
You'll find that placing separate layout objects in its own frame will make managing more complex layouts much simpler.

Tkinter: How can I save text entered in the Text Widget into a new window?

Hi I am new to programming in Tkinter. I have written this program, which is in the making. I made a class called IngredientAdder() and within that class under the method def steps_box I have a variable self.entry2, which calls upon the Text Widget. When I run the program, the text box works fine. However, under my method def save_recipie I wrote for it to print words.get('1.0', 'end') into the terminal window when I press the button in the init.gui method (words.get corresponds with the text entered by the user in the text box called self.entry2). However, when I run the program and enter text into text box and press the save button, nothing is printed into my terminal window. How can I modify my code so that the user entered text in the text box is printed into my terminal window? Help?
If you could possibly add comments in your code, it would be very helpful!! Thanks.
import Tkinter
class Cookbook(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.title("Cookbook")
self.geometry("500x500+0+22")
self.button = []
for r in range(1):
for c in range(1):
b = Button(self).grid(row=r,column=c)
self.button.append(b)
class Button(Tkinter.Button):
def __init__(self,parent):
b = Tkinter.Button.__init__(self, parent, text="Add A New Recipie", height=8, width=15, command=self.make_window)
def make_window(self):
popwindow = IngredientAdder()
popwindow.title_box()
popwindow.ingredients_box()
popwindow.steps_box()
popwindow.init_gui()
class IngredientAdder(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.title("Recipie")
self.geometry("555x500")
def title_box(self):
#Frame for the Title Label and the Title Entry Box
self.frame1 = Tkinter.Frame(self, height=50, width=550, relief=Tkinter.SUNKEN)
self.frame1.pack(anchor=Tkinter.NW,side=Tkinter.TOP)
self.frame1.pack_propagate(False)
#putting in a Title LABEL and ENTRY BOX
Tkinter.Label(self.frame1,text="Title:").pack(anchor=Tkinter.NW,side=Tkinter.LEFT)
self.entry1 = Tkinter.Entry(self.frame1,width=550)
self.entry1.pack(anchor=Tkinter.NW,side=Tkinter.TOP)
def ingredients_box(self):
#Frame for the Ingredients Label and the Ingredients Entry Boxes & Steps label and Steps Textbox
self.frame2 = Tkinter.Frame(self, height=412,width=550, relief=Tkinter.SUNKEN)
self.frame2.pack(anchor=Tkinter.NW,side=Tkinter.TOP)
self.frame2.pack_propagate(False)
# put an Ingredients label at the top of the window and anchor it there
ingredients_label = Tkinter.Label(self.frame2,text="Ingredients:").pack(anchor=Tkinter.NW,side=Tkinter.LEFT) #.grid(row=100, column=0)
def steps_box(self):
self.entry2 = Tkinter.Text(self.frame2,width=40,height=33,font="helvetica 12",padx=5,pady=5).pack(anchor=Tkinter.NW,side=Tkinter.RIGHT)
#putting in an entry box and Steps label for the steps of the recepie
steps_label = Tkinter.Label(self.frame2,text="Steps:").pack(anchor=Tkinter.NW,side=Tkinter.RIGHT) #.grid(row=100,column=1)
def title_save(self):
self.title_entries.append(self.entry1)
def text_box(self):
self.text_entries.append(self.entry2)
# function to add new ingredients
def add_ingredient_entry(self):
entry = Tkinter.Entry(self.frame2)
entry.pack(anchor=Tkinter.NW,side=Tkinter.TOP)
self.ingredient_entries.append(entry)
# get contents of all entry boxes
def save_recipie(self):
print("Title:")
for words in self.title_entries:
print words.get()
print("Ingredients:")
for ingredient in self.ingredient_entries:
print ingredient.get()
print("Steps:")
for text in self.text_entries:
print text.get('1.0', 'end')
print "[Recipie saved]"
# build initial widgets
def init_gui(self):
# title saved in this array, hopefully...
self.title_entries = []
# this is a list of ingredients entry boxes
self.ingredient_entries = []
#this saves the list in this array, hopefully..
self.text_entries = []
#Making a frame at the bottom to put both buttons in line
self.test4 = Tkinter.Frame(self,height=10, relief=Tkinter.SUNKEN)
self.test4.pack(side=Tkinter.BOTTOM)
# Put these two buttons at the bottom of the window and anchor them there
Tkinter.Button(self.test4,text="Save recipe",command=self.save_recipie, width=15).pack(anchor=Tkinter.SE,side=Tkinter.RIGHT)
Tkinter.Button(self.test4,text="Add ingredient",command=self.add_ingredient_entry, width=15).pack(anchor=Tkinter.NW,side=Tkinter.LEFT)
# new ingredients will be added between the label and the buttons
self.add_ingredient_entry()
top = Cookbook()
top.mainloop()
The code
def steps_box(self):
self.entry2 = Tkinter.Text(self.frame2,width=40,height=33,font="helvetica 12",padx=5,pady=5).pack(anchor=Tkinter.NW,side=Tkinter.RIGHT)
register None into self.entry2. You should put
def steps_box(self):
self.entry2 = Tkinter.Text(self.frame2, width=40, height=33, font="helvetica 12", padx=5, pady=5)
self.entry2.pack(anchor=Tkinter.NW, side=Tkinter.RIGHT)
However, you didn't register self.entry2 into self.text_entries because you don't call your method text_box.

Tkinter Frame adding itself to main window

I have a program that is suppose to create a new Toplevel widget and then add a frame to it. Both the toplevel and frame classes are already created separately so when they are called my window fills automatically. But when the button is clicked the Toplevel window pops up but the frame adds itself to the main window and not the new Toplevel
Main window code
class MainWindow(Tkinter.Tk):
def __init__(self,version):
Tkinter.Tk.__init__(self) # creates the main window
self.title('Awana Control ' + version) # names the main window
topframe = Tkinter.Frame(self)
topframe.grid(column=0,row=0)
openNewFamilyWindowButton = Tkinter.Button(topframe, text='Add a new family',command=partial(newWindow,'NewFamilyWindow'))
openNewFamilyWindowButton.grid(row=0, column=0)
#openEditStudentWindowButton = Tkinter.Button(topframe, text='edit current repository', command=partial(newWindow,'studentFinderWindow'))
#openEditStudentWindowButton.grid(row=0, column=1)
openTotalsWindowButton = Tkinter.Button(topframe, text='Total Shipping Orders', command=partial(newWindow,'totalsWindow'))
openTotalsWindowButton.grid(row=0, column=1)
openTotalsWindowButton = Tkinter.Button(topframe, text='Lists By Grade', command=partial(newWindow,'gradeList'))
openTotalsWindowButton.grid(row=0, column=2)
#bottomframe = Tkinter.Frame(self)
#bottomframe.grid(column=0,row=1)
right here is when it adds the student
finder window class to the main
window. This class is responsible for
opening the new toplevel windows
StudentFinderWindow().grid(column=0,row=1)
self.mainloop()
here is the StudentFinderWindow class
class StudentFinderWindow(Tkinter.Frame):
def __init__(self):
Tkinter.Frame.__init__(self) # Create Window
##### window attributes
#self.title('Edit Families') #sets window title
##### puts stuff into the window
# text
editStudentInfoLabel = Tkinter.Label(self,text='Select the family from the list below or search for one in the search box provided')
editStudentInfoLabel.grid(row=0, column=0)
# entry box
self.searchRepositoryEntry = Tkinter.Entry(self)
self.searchRepositoryEntry.grid(row=1, column=0)
# list box
self.searchResults = Tkinter.Listbox(self,selectmode='SINGLE')
self.searchResults.grid(row=2, column=0)
# create a vertical scrollbar to the right of the listbox
#yscroll = Tkinter.Scrollbar(self, command=self.searchResults.yview, orient=Tkinter.VERTICAL)
#yscroll.grid(row=0, column=1, sticky=Tkinter.N+Tkinter.S)
#self.searchResults.configure(yscrollcommand=yscroll.set)
# search results initial updater
self.getStudentList()
for student in self.studentList:
self.searchResults.insert(Tkinter.END, student)
##### event handler
self.searchResults.bind('<Double-Button-1>',self.editStudentWindowInit )
self.searchRepositoryEntry.bind('<KeyRelease>', self.updateSearch)
def updateSearch(self, event):
parameters = self.searchRepositoryEntry.get()
parameters = parameters.lower()
length = len(parameters)
self.searchResults.delete(0, Tkinter.END)
for i in self.studentList:
if i[0:length].lower() == parameters:
self.searchResults.insert(Tkinter.END, i)
def getStudentList(self):
global fileDirectory # gets the directory that all the files are in
fileList = os.listdir(fileDirectory) # makes a list of files from the directory
self.studentList = [] # makes a new list
for file in fileList: # for loop that adds each item from the file list to the student list
if file[-3:] == 'txt':
self.studentList.append(file[:-4])
This is the function that opens the new window which is suppose to fill itself with a frame
def editStudentWindowInit(self,mevent):
index = self.searchResults.curselection()
student = self.searchResults.get(index)
editStudentWindow = EditStudentWindow(student)
Here is the class that is opened
class EditStudentWindow(Tkinter.Toplevel):
def __init__(self,student):
Tkinter.Toplevel.__init__(self)
self.title('Edit Family Info')
Tkinter.Button(self,text='Edit Info',command=partial(self.fillInfo,student)).grid(column=0,row=0)
Tkinter.Button(self,text='Edit Orders',command=partial(self.fillOrder,student)).grid(column=1,row=0)
self.fillInfo(student)
def fillInfo(self,student):
try:
self.order.grid_forget()
except:
pass
self.info = EditFamilyWindow(student)
self.info.grid(column=0,row=1)
def fillOrder(self,student):
try:
self.info.grid_forget()
except:
pass
self.order = EditShippingWindow(student)
self.order.grid(column=0,row=1)
and here is the init of the frame class
class EditFamilyWindow(Tkinter.Frame):
def __init__(self,student):
Tkinter.Frame.__init__(self)
The bottom line is, your frame must be created as a descendant of a specific toplevel. You can't reparent a frame from one toplevel to another. So, for your code to work you must create the toplevel first, and then create your frame and tell it which toplevel to be created in.

Categories