I have a problem. I dynamically created buttons using a class. Each button is stored in a list, so I can use them later by indexing them. I am having trouble placing/displaying the buttons, though. When I create one button, it shows up perfectly. When I create another one, for some reason it appears over the first one. Help to fix this would be appreciated. Thanks!
Here is the code:
import tkinter as tk
window = tk.Tk()
window.geometry('800x600')
placeX = 20
placeY = 20
bl = []
def direction(type_):
pass
class SideBar():
def __init__(self, name):
global window
global placeX
global placeY
global bl
self.name = name
self.placeX = placeX
self.placeY = placeY
self.bl = bl
self.bl.append(self.name)
print(self.bl)
def create_folder(self, index):
self.bl[index] = tk.Button(window, text = self.name, command = lambda: direction(self.name))
self.bl[index].config(height = 3, width = 6)
self.bl[index].place(x = self.placeX, y = self.placeY)
self.placeY += 100
Computer = SideBar('Computer')
Documents = SideBar('Documents')
Computer.create_folder(0)
Documents.create_folder(1)
window.mainloop()
I think the problem is somewhere in the create_folder function.
You probably meant to use a class variable as opposed to an instance attribute. A class variable holds the data shared among all instances of a class, in fact, it can have a value as long as there's a class definition. Whereas an instance attribute can have values specific to a singular instance of a class, typically in the format self.attribute.
The way you are trying to use self.placeY fits the typical use of class variable. Remove:
self.placeY = placeY
add:
class SideBar():
...
placeY = placeY #assign global placeY's value to Sidebar.placeY
...
finally, replace:
self.placeY += 100
with:
SideBar.placeY += 100
You are creating two different instances of a class. Both have their own local variable. Create one instance and use something like this:
import tkinter as tk
window = tk.Tk()
window.geometry('800x600')
placeX = 20
placeY = 20
bl = []
def direction(type_):
pass
class SideBar():
def __init__(self):
global window
global placeX
global placeY
global bl
self.name = []
self.placeX = placeX
self.placeY = placeY
self.bl = []
self.bl.append(self.name)
def create_folder(self, index, name):
self.name.append(name)
self.bl.append(tk.Button(window, text = self.name[-1], command = lambda: direction(self.name)))
self.bl[-1].config(height = 3, width = 6)
self.bl[-1].place(x = self.placeX, y = self.placeY)
self.placeY += 100
side_bar = SideBar()
#Documents = SideBar('Documents')
side_bar.create_folder(0, 'Computer')
side_bar.create_folder(1, 'Documents')
window.mainloop()
Related
how to remove the page1's widgets when the next button is pressed, so that only the page2's widgets is shown.
and vice versa if the back button is pressed on page 2, so the widgets don't overlap
from tkinter import *
class Buttons(Button):
def __init__(self,master,**kwargs):
super().__init__(master=master,**kwargs)
self.look = {"fg":"ghost white","bg":"DarkBlue"}
self.config(self.look)
def makeButton(self,name,texts,wide,rows,cols,com):
self.name = name
self.texts = texts
self.wide = wide
self.rows = rows
self.cols = cols
self.com = com
self.name = Buttons(root,text=self.texts,width=self.wide,command=self.com)
self.name.place(x=self.rows,y=self.cols)
class make(Buttons):
def __init__(self, mainFrame):
super().__init__(mainFrame)
self.main_frame = Frame(mainFrame, width=400, height=300)
self.main_frame.place()
self.page1()
def page1(self):
self.makeButton("name1","Page1-widgets1",15,125,30,None)
self.makeButton("name2","Page1-widgets2",15,125,80,None)
self.makeButton("name3","Next",15,125,130,self.page2)
self.makeButton("name4","Exit",15,125,180,exit)
def page2(self):
self.makeButton("name5","Page2-widgets1",15,135,40,None)
self.makeButton("name6","Page2-widgets2",15,135,90,None)
self.makeButton("name7","Page2-widgets3",15,135,140,None)
self.makeButton("name8","Back",15,135,210,self.page1)
def main():
global root
root = Tk()
root.geometry('400x300+50+50')
script = make(root)
root.mainloop()
if __name__ == '__main__':
main()
was gonna comment but not enough reps, so gonna ask my question here. I copied your script and produced same error too. Firstly its not clear what you want to achieve with script so comments between definitions would help a lot!. When continue button is pressed you want current page to be destroyed and new one to be created right ?
enter code here
def frame_elements_remove(self, elements):
self.elements = elements
for self.element in self.elements:
self.element.destroy()
To destroy button it has to object. When I try to check for type of element in list ;
self.frame_elements = []
with
def mainPage(self):
self.frame_elements_remove(self.frame_elements)
self.makeButton("Button1", "Continue", 10, 10, self.page1)
self.makeButton("Button2", "Exit", 10, 80, quit)
self.controler = 1
self.frame_elements = [self.makeButton]
#just put here print for type check
print(type(self.frame_elements[0]))
it returns;
<class 'method'>
So its seems, its not destroyable because it is not object but a method.
Hope it helps!
I'm using Tkinter's "validatecommand" function to validate inputs from a entry box. I want to pass my class object so that the validation-function can request information from the object. However, it seems that the validatecommand function turns everything I pass into strings. Because of this the validation-function now has __main__.foo object at 0x042981B0 but as string. How can I instead pass the original __main__.foo?
It currently looks like this (pseudo-code):
class foo(object):
def start(program):
self.stuff = 5 #stuff changes while the program is running
tkinter_stuff(program)
def tkinter_stuff(program):
Entry = tkinter.Entry(validatecommand = (window.register(validate_entry), '%P', program))
def validate_entry(entry, program): #checks if current stuff + the amount of staff that would be added over this entry box is <= 20
if int(entry) + program.get_stuff() <= 20:
return True
return False
program = foo() #there are other classes that create their own program and overwrite the one the entry uses, so I can't rely on this one
program.start(program)
actual code:
import tkinter
class foo(object):
def __init__(self):
self.stuff = 5 #stuff changes while the program is running
def start(self, program):
tkinter_stuff(program)
def get_stuff(self):
return self.stuff
def tkinter_stuff(program):
window = tkinter.Tk(className = 'window')
window.geometry('50x50')
print(program, type(program))
Entry = tkinter.Entry(window, width = 10, validate = 'key', validatecommand = (window.register(validate_entry), '%P', program))
Entry.place(x = 10, y = 10)
window.update()
def validate_entry(entry, program): #checks if current stuff + the amount of staff that would be added over this entry box is <= 20
print(program, type(program))
if int(entry) + program.get_stuff() <= 20:
return True
return False
program = foo() #there are other classes that create their own program and overwrite the one the entry uses, so I can't rely on this one
program.start(program)
Try this:
import tkinter as tk
class Entry(tk.Entry):
def __init__(self, master=None, args=tuple(), validatecommand=None, **kwargs):
if validatecommand is not None:
self.args = args
self.callers_function = validatecommand[0]
validatecommand = (root.register(self.validatecommand), *validatecommand[1:])
super().__init__(master, validatecommand=validatecommand, **kwargs)
def validatecommand(self, *args):
return self.callers_function(*args, *self.args)
class Foo:
def __init__(self):
pass
def validate_entry(entry, program):
print(type(entry), type(program))
return True
program = Foo()
root = tk.Tk()
# Make sure it's not `root.register(validate_entry)`:
entry = Entry(root, validate="key", validatecommand=(validate_entry, "%P"),
args=(program, ))
entry.pack()
root.mainloop()
I just made a wrapper class that will call the validatecommand with the args that were specified when creating the Entry.
The Point class has an instance variable point, which keeps tabs of each unique corner of the polygon. Problem is, when I hover the cursor over the second instance of the class, the point variable of the first instance changes. My code,
from tkinter import *
from tkinter import Tk,Canvas
import sys,os,string,time
class Point():
def __init__(self,root,canvas,pt,radius = 4):
self.canvas = canvas
self.root = root
self.point = self.canvas.create_oval(pt[0]-radius,pt[1]-radius,pt[0]+radius,pt[1]+radius,fill = "green", tag = "Point")
self.canvas.tag_bind("Point","<Enter>",self.enter)
self.canvas.tag_bind("Point","<Leave>",self.leave)
def enter(self,event):
print(self.point)
self.canvas.itemconfigure(CURRENT,fill="blue")
self.loc = 1
def leave(self,event):
self.canvas.itemconfigure(CURRENT,fill="green")
root = Tk()
root.title("Poly Draw")
canvas = Canvas(root,width = 256, height = 256, borderwidth = 1)
pt = [100,100]
point = Point(root,canvas,pt)
point2 = Point(root,canvas,[150,150])
print(point.point)
print(point2.point)
canvas.pack()
root.mainloop()
When you run the above piece of code you will see that the instance variable self.point is changing for the first instance when I hover over the second instance(I am printing self.point when cursor enters the widget). Noted that, before I run the mainloop() the instance variable is correct.
You're binding the tag "Point" sequentially, so only the last one is active.
I think you instead want to bind to the item id, not to the (non-unique) tag.
If so, change the binding instead to:
def __init__(self,root,canvas,pt,radius = 4):
self.canvas = canvas
self.root = root
self.point = self.canvas.create_oval(pt[0]-radius,pt[1]-radius,pt[0]+radius,pt[1]+radius,fill = "green", tag = "Point")
self.canvas.tag_bind(self.point,"<Enter>",self.enter)
self.canvas.tag_bind(self.point,"<Leave>",self.leave)
I'm currently working on a Synthesizer inside Python for a school project and currently have a really troublesome issue. I have a Stack of Checkboxes which mark when a note is played and on which pitch inside a sequencer. My Problem is that whenever I open two Oscillators inside my synthesizer and put in the Values inside the checkboxes, the checkboxes duplicate their value over the multiple windows, but don't do it for the actual sequence, as in I have two lists with correct values, but the values aren't properly displayed inside the window.
To Replicate the Problem hit "new Oscillator" then click on "Arpeggio", do this a second time and click any Checkbox on the Arppeggio Windows
I know this might be a confusing explanation, but I'm going to link the complete code in the bottom so you can try it out and might know what I'm talking about.
The problem occurs inside the "Arpeggio" Class
import numpy # used for Waveform Calculation
from functools import partial # used for Command Combining
from tkinter import * # used for GUI
from tkinter import ttk # used for GUI
from tkinter import filedialog # used for GUI
np = numpy # Simplifying Libraries
tk = ttk # Simplifying Libraries
fd = filedialog # Simplifying Libraries
root = Tk()
#StartupFunction
def StartUp():
print("")
print("Starting Startup")
app = App(root) # Initializing GUI
print("Finished Startup")
main()
("Exiting Startup")
#Main Program Function
def main():
print("Executing Main")
root.mainloop()
print("Finished Main")
return 0
class Oscillator():
pass
class App(tk.Frame):
OscillatorWindowList = []
OscillatorList = []
SoundInputArrayList = []
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
root.title("PySynth")
root.geometry("984x300")
root.resizable(False, True)
root.maxsize(984,720)
btnNewOscillator = Button(root,text="New Oscillator",command=self.NewOscillator,relief=RIDGE,bg="#2d2d2d",fg="white")
btnNewOscillator.place(x = 8, y = 8+128)
def NewOscillator(self):
print("AddingOscillator")
self.OscillatorList.append(Oscillator())
self.SoundInputArrayList.append(SoundInputArray(root,len(self.OscillatorList)-1,len(self.OscillatorList)-1))
print(self.OscillatorList)
self.OscillatorWindowList.append(OscillatorGUI(root,self.OscillatorList[len(self.OscillatorList)-1],len(self.OscillatorList)))
def EXIT(self):
root.destroy()
#$SoundInputArray
class SoundInputArray():
actv = []
CheckbuttonList = []
CheckButtonFreq = []
i=0
ButtonCount = 32
VolumeSlider = None
btnArpeggio = None
Arpeggio = None
hasArpeggio = False
LFO = None
ArpeggioList = [(0,0)]
def __init__(self,master,oscillatorCount,number):
btnArpeggio = Button(master,text="Arpeggio",command=self.OpenArpeggio,relief=RIDGE,bg="#2d2d2d",fg="white")
btnArpeggio.place(x = 8, y = (1+oscillatorCount)*48 +128 )
def OpenArpeggio(self):
if self.Arpeggio == None:
self.Arpeggio = Arpeggio()
def GetArpeggio(self):
return self.Arpeggio
#$Arpeggio
class Arpeggio():
SoundValueList = None
def __init__(self):
GUI = Toplevel(root)
GUI.title("Arpeggio")
GUI.geometry("480x320")
GUI.resizable(False, False)
GUI.configure(bg="#171717")
self.SoundValueList = np.arange(0,16)
self.DrawUI(GUI)
self.ClearList()
def DrawUI(self,frame):
Button(frame,text="display", command= self.PrintSound, width=11,bg="#171717",).place(x = 4, y = 4)
Button(frame,text="empty", command= self.ClearList).place(x = 96, y = 4)
y = 1
x = 1
checkbuttonList = []
for y in range(1,13):
for x in range(0,16):
updatecommand = partial(self.UpdateList,x,y)
checkbuttonList.append(Checkbutton(frame, variable=self.SoundValueList[x], onvalue=y, offvalue=0,command = updatecommand))
checkbuttonList[len(checkbuttonList)-1].place(x = x*24 + 96, y= y*24 + 8)
def ClearList(self):
for i in range(0,16):
self.SoundValueList[i] = 0
def UpdateList(self,x,value):
if (self.SoundValueList[x] == value):
self.SoundValueList[x] = 0
else:
self.SoundValueList[x] = value
self.PrintSound()
def PrintSound(self):
print(self.SoundValueList)
def GetList(self):
print(self.SoundValueList)
return self.SoundValueList
StartUp() # Initiate Program
I have a question, though I'm not sure what language to use. I'm a little confused about how to access the canvas defined in the main driver module from other modules without using globals. For instance, I have instantiated the canvas in driver.py, but am trying to draw from Level.py. For context, it is eventually going to read a text file and draw a map based on the data it extracts from it for an RPG-ish game. Here is my code:
Driver.py:
import tkinter
import Level
HEIGHT = 1024
WIDTH = 800
TILE_SIZE = 32
VERTICAL_TILES = HEIGHT//TILE_SIZE
HORIZONTAL_TILES = WIDTH//TILE_SIZE
root = tkinter.Tk()
root.title("RPG Land")
window = tkinter.Canvas(root,width= WIDTH, height=HEIGHT )
lev = Level.LevelMgr()
lev.loadLevel()
lev.drawLevel()
window.pack()
root.mainloop()
Annnd Level.py:
import tkinter
from driver import window
class LevelMgr:
def __init__(self):
self.levelData = []
self.visibleLevel = []
self.tileColors = {
0 : 'empty',
1 : 'wall',
2 : 'bush' }
def loadLevel(self):
fyle = open('levels/level1','r')
count = 0
for lyne in fyle:
self.levelData.append(lyne)
count += 1
def drawLevel(self):
currentY = 0
currentX = 0
for col in self.levelData:
currentY += 32
for t in col:
window.create_rectangle(currentX, currentY, 32, 32, fill="blue")
currentX += 32
Any advice on how to structure the program better would be also appreciated. When accessing other namespaces, what is the proper way to do so? Do I need to have "import Level" on driver.py, as well as "import driver" in Level.py? I'm a little confused as to the fundamental structure of such a program.
LevelMgr depends on window, so name it explicitly in __init__:
class LevelMgr:
def __init__(self, window):
self.window = window
def drawLevel(self):
...
for t in col:
self.window.create_rectangle(currentX, currentY, 32, 32, fill="blue")
Remove the import statement:
from driver import window
Then, in Driver.py:
lev = Level.LevelMgr(window)
Another possibility is to simply define window in Level.py instead of Driver.py.