So I have this code
class createNewFile:
def createNewFile(createFileEntry):
string = StringVar()
createFileEntry = Entry( CROW, textvariable = string)
createFileEntry.pack()
createFileEntry.focus_set()
def saveNewFile(message):
filename = createFileEntry.get()
extention = filename
target = open (extention, 'a')
message = tkMessageBox.showinfo('What happened','Recived ' +getFileEntry+' as filename')
newFileButton = createNewFile()
newFileButton.createNewFile()
newFileButton.saveNewFile
newFileIcon = PhotoImage(file='icons/newFile.png')
createFileBtn = Button(toolbar, image = newFileIcon, command = newFileButton.createNewFile,relief='solid',background='white', border=0)
createFileBtn.pack(side=LEFT, padx=0,pady=0)
saveFileIcon = PhotoImage(file='icons/saveFile.png')
saveFileEntry = Button(toolbar,image = saveFileIcon, command = newFileButton.saveNewFile,relief='solid',background='white', border=0)
saveFileEntry.pack(side=LEFT, padx=0,pady=0)
and the idea is for the new file button to create a entry space to get the name and extension of the file, and the save button to write it.
I read here in stack overflow how to set up the class for you to be able to call a variable from one function to another, but it didn't work. What am I doing wrong?
Here in the code shown above you are only creating local instances of the variables, create class variables if you want to use across classes, try using this: -
class createNewFile:
def createNewFile(self):
string = StringVar()
self.createFileEntry = Entry( CROW, textvariable = string)
self.createFileEntry.pack()
self.createFileEntry.focus_set()
def saveNewFile(self, message):
filename = self.createFileEntry.get()
extention = filename
target = open (extention, 'a')
message = tkMessageBox.showinfo('What happened','Recived ' +getFileEntry+' as filename')
Also, I am unable to find getFileEntry in the source code you have provided. anything that you want to save in the class instance, save or edit it using self.variable_name. I hope I have answered your question.
Related
I am trying to create a framework to create a label, text box and button as an object, I can extend it easily.
Idea:
Original
after extend
and it could extend to have 3, 4, 5 or more if it have more file, just declare in dictionary list, it will extend automatically.
The code I have:
def getpath(entry_box, type):
# path or file
if type == 'path':
entry_box.set(filedial.askdirectory())
elif type == 'file':
entry_box.set(filedial.askopenfilename())
MainWin = tk.Tk() # Create main windows
MainWin.title('Get file and path') # App name
# Create root container to hold Frames
mainFrame = ttk.Frame(MainWin)
mainFrame.grid(column=1, row=1)
# define the object to create in dictionary, format:
# {object name:[title name, default path, button name, file or folder path]}
obj2create ={'file1':['ABC Location: ', r'C:/', 'Get ABC','file'],
'file2': ['DEF Location:', r'C:/1/', 'Get DEF', 'file']}
ttl_obj = 0
for key in obj2create:
ttl_obj +=1
vir = obj2create[key]
# Module for get file:
obj_name = key
title = vir[0]
default_path = vir[1]
btn_name = vir[2]
get_type = vir[3]
# Create main container
pa_frame = ttk.Frame(mainFrame)
pa_frame.grid(column=1, row=10*ttl_obj, sticky=tk.W)
pa_frame.config(width = 100)
# Row 1: Label
frame_name = obj_name + '_name'
print(frame_name)
frame_name = ttk.Label(pa_frame, text= title).grid(column=1, row=10*ttl_obj,sticky=tk.W)
# Row 2: path and button
# assign type
path_loc = obj_name + '_path'
path_loc = tk.StringVar()
path_loc.set(default_path)
# put in frame
fileLocPath = obj_name + '_loc_path'
fileLocPath = ttk.Entry(pa_frame, width=70, textvariable=path_loc)
fileLocPath.grid(column=1, row=30*ttl_obj) # Assign position
# Get file button
# define button display text and assign the command / function
bt_get_file_path = obj_name + '_btn'
bt_get_file_path = ttk.Button(pa_frame, text= btn_name,
command=lambda: getpath(path_loc, get_type))
# Position Button in second row, second column (zero-based)
bt_get_file_path.grid(column=2, row=30*ttl_obj)
# Auto popup when open
MainWin.mainloop() # Let the window keep running until close
Issue:
Default file path appear in second text box only.
All the button location point to second box.
I also not sure how could I get the value in different box, the "path_loc = obj_name + '_path'" not able to get the correct object.
How should I make it work? Or the way I use is wrong?
Tkinter widgets are no different than any other python objects with respect to creating them in a loop. Your problem seems to be that you don't know how to create a unique variable for an unknown number of widgets.
The important thing to know is that you don't need a unique variable for each widget. Instead, you can use a list or dictionary, either of which can be easily extended at runtime.
Saving widget references in a dictionary
Here is a solution using a dictionary:
entries = {}
for key in obj2create:
...
entries[key] = ttk.Entry(...)
...
...
print("the value for file1 is", entries["file1"].get()
Creating a custom compound widget
If you're wanting to create sets of widgets that are to be treated as a group, it may be better to create a class. In effect, you're creating your own custom widget. This type of class is often called a "compound widget" or "megawidget".
For example:
class FileWidget(ttk.Frame):
def __init__(self, parent, name):
ttk.Frame.__init__(self, parent)
self.label = ttk.Label(self, text="%s Location" % name)
self.fileLocPath = ttk.Entry(self)
self.bt_get_file_path = ttk.Button(self, text=name, command=self.get_path)
...
def get_path(self):
return self.fileLocPath.get()
You can then create each row in your GUI like this:
widgets = {}
for key in obj2create:
widgets[key] = FileWidget(mainFrame, key)
widgets[key].pack(side="top", fill="x")
Later, you can get back the values like this:
for key in obj2create:
widget = widgets[key]
print("%s: %s" % (key, widget.get_path())
I have been trying to use tkinter CheckButton widget to edit items in a list - each item of the list is a new checkbutton. I want a save method to save the data to a text file, and a load method to load the info from the text file and mark the checkbutton boxes depending on the items in the list.
Here is my code so far, but the list doesn't seem to change when I check the buttons and update the list/file
Here is my code, I need to know why the list isn't updating when I check the boxes:
import tkinter.messagebox as box
modulesMain = Tk()
moduleChecks = []
def SaveChanges():
# Clear the text file
modules = open("modules.txt", "w") #Write mode to overwrite the whole file
modules.write("") # Put in blank text
modules.close()
modules = open("modules.txt", "a") # Append mode to append the file
for item in moduleChecks:
modules.write(item + "\n")
print(moduleChecks)
appButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[0]).grid()
searchButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[1]).grid()
Save = Button(modulesMain, text = "Save Changes", command = SaveChanges).grid()
The variable for each of your checkboxes needs to be an IntVar. Your moduleCheck list is currently un-initialised so when you try to access the elements inside it, you'll get an error.
In the below code (modified from yours), I've initialised the moduleCheck to contain two IntVars.
When you press the save button, it will print to the console the current state of the check boxes.
import tkinter as tk
modulesMain = tk.Tk()
moduleChecks = [tk.IntVar() for i in range(2)]
def SaveChanges():
for idx,item in enumerate(moduleChecks):
print(f"{idx} = {item.get()}")
appCheck = tk.Checkbutton(modulesMain, text = "App", variable = moduleChecks[0])
appCheck.grid()
searchCheck = tk.Checkbutton(modulesMain, text = "Check", variable = moduleChecks[1])
searchCheck.grid()
saveButton = tk.Button(modulesMain, text = "Save Changes", command = SaveChanges)
saveButton.grid()
modulesMain.mainloop()
Here is a basic solution.
import tkinter as tk
root = tk.Tk()
#list of options. doubles as checkbox label text
#appending to this list is all that is necessary to create more options
opts = ['App', 'Search']
#create an StringVar for every option
#this way there doesn't have to be type conversions for reading/writing files
vars = [tk.StringVar(value='0') for _ in opts]
#create checkboxes for every option
for opt, var in zip(opts, vars):
tk.Checkbutton(root, text=opt, variable=var, onvalue='1', offvalue='0').grid(sticky='w')
#write the checkbox values to file
def save_options_state():
with open("modules.txt", "w") as file:
file.write(''.join([v.get() for v in vars]))
#set checkbox values from file
def load_options_state():
with open("modules.txt", "r") as file:
for n, var in zip(list(file.read().strip()), vars):
var.set(n)
#save/load buttons
tk.Button(root, text="save", command=save_options_state).grid()
tk.Button(root, text="load", command=load_options_state).grid(row=2, column=1)
root.mainloop()
Here's a runnable example which I think shows how to do everything you've asked about. It assumes the modules.txt file contains data in the following format, where each line consists of a module's name and whether it's checked or not.
Mod1,0
Mod2,0
Mod3,0
The data in the file is used to create a list of Checkbuttons — one for each module — and initially sets to its current status as indicated in the file.
Here's the sample code:
from tkinter import *
import tkinter.messagebox as box
dataFilename = "modules.txt"
moduleChecks = []
def ReadModules():
with open(dataFilename, "r") as file:
for line in (line.strip() for line in file):
moduleName, moduleState = line.split(',')
moduleChecks.append((moduleName, int(moduleState)))
print('read:', moduleChecks)
def SaveChanges():
print('writing:', moduleChecks)
with open(dataFilename, "w") as file:
for checkButton in moduleCheckbuttons:
moduleName = checkButton.cget('text')
moduleState = checkButton.var.get()
file.write(','.join((moduleName, str(moduleState))) + '\n')
ReadModules()
modulesMain = Tk()
moduleCheckbuttons = []
# Create a Checkbutton for each module based on moduleChecks values.
for moduleName, moduleState in moduleChecks:
intVar = IntVar(value=moduleState)
checkButton = Checkbutton(modulesMain, text=moduleName, variable=intVar)
checkButton.var = intVar # Attach reference to variable.
checkButton.grid()
moduleCheckbuttons.append(checkButton)
saveButton = Button(modulesMain, text="Save Changes", command=SaveChanges)
saveButton.grid()
modulesMain.mainloop()
I am trying to display a list of users saved in a text file on a line by line basis and have it updated as people launch and close my program respectively. I can't figure out how to update the labels as in create new ones and delete ones no longer present in the text file as I can't .set() or .config() them as far as I know since the actual text on the labels doesn't have to change.
Here is my code so far.
def list_users(self):
with open("usercheck.txt", "r") as ulst:
self.usr_list = []
for line in ulst:
self.usr_list.append(line)
def online(self):
for self.name in self.usr_list:
self.onlbl = tk.Label(self, text = self.name,bg = "#42f480")
self.onlbl.grid(row = self.onlcnt,column = 5,padx = 0)
self.onlcnt +=1
Running the online function with after just creates duplicates of the same label and does not update the amount of labels. self.onlcnt is 0
The reason might be that you are using the self keyword in your loop variable, which causes the variable to stay constant: don’t.
def online(self):
for name in self.usr_list:
self.onlbl = tk.Label(self, text = name,bg = "#42f480")
self.onlbl.grid(row = self.onlcnt,column = 5,padx = 0)
self.onlcnt +=1
Also you might want to store the Labels in a list, so you can access them later:
def online(self):
try:
self.labels
except AttributeError:
self.labels = []
self.onlcnt = 0
for name in self.usr_list:
onlbl = tk.Label(self, text = name,bg = "#42f480")
onlbl.grid(row = self.onlcnt,column = 5,padx = 0)
self.labels.append(onlbl)
self.onlcnt +=1
root.after(5000, self.online) #run it again
I'm trying to generate multiple ComboBoxes with values from a "config.ini" file, the config.ini file data is:
priority1 = Normal:farty-blobble-fx.wav:2
priority8 = Reclamacao:buzzy-blop.wav:3
priority3 = Critico:farty-blobble-fx.wav:5
priority2 = Urgente:echo-blip-thing.wav:4
and the goal is turning the sound files names to the select values in the comboboxes.
My code to generate the comboboxes is:
content_data = []
for name, value in parser.items(section_name):
if name=="name":
self.note.add(self.tab2, text = value)
else:
data_prior = value.split(":")
self.PRIOR_LABEL = Label(self.tab2, text=data_prior[0])
self.PRIOR_LABEL.grid(row=data_prior[2],column=0,pady=(10, 2),padx=(40,0))
self.PRIOR_SOUNDS = None
self.PRIOR_SOUNDS = None
self.box_value = StringVar()
self.PRIOR_SOUNDS = Combobox(self.tab2, textvariable=self.box_value,state='readonly',width=35)
self.PRIOR_SOUNDS['values'] = getSoundsName()
self.PRIOR_SOUNDS.current(int(getSoundsName().index(data_prior[1])))
self.PRIOR_SOUNDS.grid(row=data_prior[2],column=1,pady=(10, 2),padx=(30,0))
self.PLAY = Button(self.tab2)
self.PLAY["width"] = 5
self.PLAY["text"] = "Play"
self.PLAY["command"] = lambda:playSound(self.PRIOR_SOUNDS.get())
self.PLAY.grid(row=data_prior[2], column=3,pady=(10,2),padx=(5,0))
And i was unable to show the current values of the "config.ini" file in the comboboxes.
Thank you in advance.
The problem is that you're creating more than one combobox, yet you keep overwriting the variables in each iteration of the loop. At the end of the loop, self.PRIOR_SOUNDS will always point to the last combobox that you created. The same is true for self.box_value, self.PLAY, etc.
The simplest solution is to use an array or dictionary to store all of your variables. A dictionary lets you reference each widget or variable by name; using a list lets you reference them by their ordinal position.
A solution using a dictionary would look something like this:
self.combo_var = {}
self.combo = {}
for name, value in parser.items(section_name):
...
self.combo_var[name] = StringVar()
self.combo[name] = Combobox(..., textvariable = self.combo_var[name])
...
I am using Tkinter to help me build a FTP client, in this client I am trying to get the selected information from a tk listbox. So I have a button that starts the download but what ever the reason is it pops up with the error "
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1410, in __call__
return self.func(*args)
File "/Volumes/LEGO FLASH/ftp.py", line 23, in Download
filename = stuff
NameError: global name 'stuff' is not defined"
Below I have the code for you to look at:
# Import the FTP object from ftplib
from ftplib import FTP
from Tkinter import *
import os
app = Tk()
app.title("FTP")
app.geometry("300x300")
lines = []
#[lines.replace(",", "\n")for lines in lines]
#lines = lines.replace(',','\n')
def handleDownload(block):
file.write(block)
print ".",
def append_line(line):
lines.append(line)
#This is where I am caught------->
def Download():
filename = stuff
file = open(filename, 'wb')
ftp.retrbinary('RETR ' + filename, handleDownload)
ftp.close()
def login():
try:
ftp.login(username.get(),password.get())
except:
error = Label(app, text = "Invalid USERNAME OR PASSWORD")
label2 = Label(app, text = "Welcome to Steam Engine").pack()
username.forget()
password.forget()
button.forget()
app.geometry("800x500")
download = Button(app, text = "Download!!!!!", command = Download)
download.pack(side = "left", pady = "5")
scrollBar.pack(fill = Y, side = "right", padx = "2")
#ftp.cwd('The_Store')
stuff = Listbox(app, height = "700", width = "500")
ftp.retrlines('NLST', append_line)
for i in lines:
stuff.insert(END, i)
stuff.pack(padx = "10", pady = "10")
stuff.config(yscrollcommand = scrollBar.set)
scrollBar.config(command = stuff.yview)
ftp = FTP('sciphigames.com')
label = Label(app, text = "Login").pack(pady = "10")
scrollBar = Scrollbar(app)
username = StringVar(None)
username = Entry(app, text = "Username: ")
username.pack(pady = "2")
password = StringVar(None)
password = Entry(app, text = "Password: ")
password.pack(pady = "2")
button = Button(app, text = "Login!", command = login)
button.pack(pady = "10")
app.mainloop()
Any help would be appreciated!
Thanks!
#This is where I am caught------->
def Download():
filename = stuff
what is stuff here ?? it is not a global variable, it seems to be a parameter of login, but not of Download method.
If you want to do stuff a global variable (probably not the better choice), use the global statement.
# Import the FTP object from ftplib
from ftplib import FTP
from Tkinter import *
import os
# define the global stuff
global stuff
...
def Download():
global stuff
filename = stuff
...
A better way to handle this would be to create an object around all of this ::
class NetworkApp(object):
def login(self):
# here put all the previous code of login
# here we change the callback to self.Download
download = Button(app, text = "Download!!!!!", command = self.Download)
# here we're creating a stuff member
self.stuff = Listbox(app, height = "700", width = "500")
def Download(self):
filename = self.stuff # here we use the stuff member
file = open(filename, 'wb')
ftp.retrbinary('RETR ' + filename, handleDownload)
ftp.close()
#...
net_app = NetworkApp()
button = Button(app, text = "Login!", command = net_app.login)
I am not entirely sure what the purpose is with your stuff variable, but the problems you are experiencing probably stem from the way you are using it.
First, you are using it as argument to login (which, by the way should take no arguments). You assign to this variable from the login function, and refer to another variable with the same name in your Download function.
Again, not being sure what I understand what you want to do with the stuff variable, I would try something like
.....
app.geometry("300x300")
stuff = None # <<<<----
lines = []
.....
#This is where I am caught------->
def Download():
global stuff # <<<<----
filename = stuff
......
ftp.close()
def login():
global stuff # <<<<----
......
stuff = Listbox(app, height = "700", width = "500")
ftp.retrlines('NLST', append_line)
for i in lines:
stuff.insert(END, i)
stuff.pack(padx = "10", pady = "10")
stuff.config(yscrollcommand = scrollBar.set)
scrollBar.config(command = stuff.yview)
......
All you need to do is put "global" in front of the variable
global var1