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!
Related
I'm trying to create an automated way of adding elements to my UI interface and deleting them when needed.
The functionality is there, on button press I create an instance of the translateUI class and with the other button I can remove it.
The issue lies within the def setBoolOn(self, *args).
btnList.append(self.delBtnFetch) is creating a second button in the UI and I cannot figure out why. Clicking the 'Add' button once generates 2 buttons, even though the function only creates one.
When commenting out the btnList.append, it only creates a single button, but the rest of the code breaks, when trying to remove it.
import maya.cmds as cmds
import maya.mel as mel
class UI(object):
#Constructor
def __init__(self):
#global counter, classList, sequence
self.btnList = []
global counter
counter = 1
classList.clear()
btnList.clear()
#define name, size and title
self.window = 'Animation_Window'
self.title = 'Animation Automator'
self.size = (400,560)
#close window if open
if cmds.window(self.window, exists=True) :
cmds.deleteUI(self.window, window=True)
#create new window
self.window = cmds.window(self.window, title=self.title, widthHeight=self.size, sizeable=False)
#specify layout for window
cmds.columnLayout(adjustableColumn=True)
#test text
cmds.text('Text in class')
#add button
self.btnChckBoxOn = cmds.button(label='Add', c=self.setBoolOn)
#remove button
self.btnChckBoxOff = cmds.button(label='Remove', c=self.setBoolOff)
#display window
cmds.showWindow()
############################
##Add button functionality##
def setBoolOn(self, *args):
#button check
btnChckOn = cmds.button(self.btnChckBoxOn, query=True)
global counter
self.classList = []
#add 1 to the counter
if counter >= 0:
counter = counter + 1
#create, based on sequence, the translateUI class
self.sequence = translateUI(counter)
#add the sequence class to a list
classList.append(self.sequence)
#get the button from translate
self.delBtnFetch = translateUI(0).delBtn
#add the delBtn to a list
btnList.append(self.delBtnFetch)
print(btnList)
###############################
##remove button functionality##
def setBoolOff(self, *args):
#button check
btnChckOff = cmds.button(self.btnChckBoxOff, query=True)
global counter, classList
#substract one off the counter
if counter >= 1:
counter = counter - 1
#get the last class in the list
lastSequence = classList[-1]
#set the last class instance to null
lastSequence = Null()
#get the last button in the list
lastButton = btnList[-1]
#remove the last button from list
btnList.remove(lastButton)
#delete last button from the UI
cmds.deleteUI(lastButton)
else :
print('You cannot remove all Sequences')
#initiate window class
myWindow = UI()
#######################
##Additional UI class##
#######################
class translateUI():
def __init__(self, counter):
global delBtn
self.delBtn = cmds.button(l='Animate', c=self.AnimatePress)
def AnimatePress(self, *args):
btnAnimate = cmds.button(self.delBtn, query=True)
print('Animation Pressed')
##############
##VOID class##
class Null():
pass
I am not that insanely experienced with Python yet and am at a loss here, any help is appreciated!
The issue is in your setBoolOn() method, the translateUI class is initialized twice as you can see here
# >> first translate UI instance was created
self.sequence = translateUI(counter)
self.classList.append(self.sequence)
# >> second translate UI instance was created
self.delBtnFetch = translateUI(0).delBtn
now each time it gets initiated, it will create a button for you, as that's how you declared it.
class translateUI():
def __init__(self, counter):
global delBtn
# >> you're creating a button during the initialization
self.delBtn = cmds.button(l='Animate', c=self.AnimatePress)
I didn't use your code but I guess what you're trying to do is something like this:
from maya import cmds
import sys
# I'm trying to create an automated way of adding elements to my UI interface and deleting them when needed.
def userInput():
user_input = cmds.promptDialog(title="Control Info", message="name,label", style="text",
button=['OK', 'Cancel'], defaultButton='OK',
cancelButton='Cancel', dismissString='Aborted'
)
if user_input == 'OK':
ranges = cmds.promptDialog(query=True, text=True)
else:
print('closed')
# force to stop the script
sys.exit()
return ranges.split(",")
def setParentLayout(*args):
""" Set the parent one level up in the current active window"""
cmds.setParent('..')
print ("MOVED the parent to: {0}".format(cmds.setParent(q=True)))
def addButtons(*args):
""" Add a button to the current parent """
# get control type and name
description = userInput()
# a dynamic dictionnary to extend for more buttons
ctrl_dict = {"button": cmds.button, "checkBox": cmds.checkBox}
# create the control
ctrl_cmd = ctrl_dict[description[0]]
the_ctrl = ctrl_cmd()
# If it's not a layout, use the label
if not cmds.layout(the_ctrl, q=True, ex=True):
ctrl_cmd(the_ctrl, e=True, l=description[1])
print("ADDED A BUTTON: {0}".format(the_ctrl))
# update the list
controls.append(the_ctrl)
def addLayout(*args):
""" Add a layout to the current parent """
lay = cmds.columnLayout()
controls.append(lay)
print("ADDED A LAYOUT: {0}".format(controls))
def removeButtons(*args):
""" remove one by one anything that the user created"""
for button_name in controls.__reversed__():
if cmds.control(button_name, q=True, ex=True):
cmds.deleteUI(button_name)
print("DELETED CONTROL: {0}".format(button_name))
# update my list
controls.remove(button_name)
break
# A simple window for testing the methods
controls = []
windowed = 'myWindow'
if cmds.window(windowed, q=True, exists=True):
cmds.deleteUI(windowed)
cmds.windowPref(windowed, remove=True)
cmds.window(windowed, title='Updater UI', w=297, h=113)
cmds.showWindow(windowed)
cmds.frameLayout("main_layout", lv=False, mh=7)
# create the updater button
edge = 7
layout2 = cmds.rowColumnLayout(nc=2, adj=1, columnOffset=[(1, "left", edge), (2, "right", edge)],
columnSpacing=(2, 3),
parent=windowed)
add_btn = cmds.button(l="Add more buttons", c=addButtons, p=layout2)
remove_btn = cmds.button(l="Remove buttons", c=removeButtons, p=layout2)
# change parent to the top level layout of the window
cmds.setParent('|')
layout1 = cmds.columnLayout(adj=1, columnOffset=("both", edge), rs=5, parent=windowed)
cmds.button(l="Add Layout", c=addLayout, p=layout1)
cmds.button(l="Set parent Layout", c=setParentLayout, p=layout1)
# change parent to the top level layout of the window
cmds.setParent('|')
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 have been working on my first GUI in tkinter - I am using Windows. My goal right now is to have buttons that accomplish these goals:
The buttons are highlighted when moused over.
The button remains highlighted if clicked.
Only one button can be "selected" (click-highlighted) at a time.
I initially thought that I had accomplished this! But I realize now that my work is not complete.
Here is what I am seeing:
I mouse over button A. It becomes highlighted! (GOOD)
I click on button A. It stays highlighted! (GOOD)
I mouse over button B. It becomes highlighted! (GOOD)
I click on button B. It stays highlighted! The highlight from A is removed! (GOOD)
I mouse over button A. It does not highlight. (BAD)
I am calling the default_coloring class function on button A when I click on button B. However, this appears to turn off the highlighting functions of button A, and the button no longer functions correctly according to the three rules I listed at the top.
How do I ensure that the buttons continue to function normally, even after the command is called? Am I approaching this the wrong way?
import tkinter as tk
blue = '#0000BB'
white = '#FFFFFF'
class HoverButton(tk.Button):
def __init__(self, master, position = None, **kw):
tk.Button.__init__(self,master=master,**kw)
self.defaultBackground = self["background"]
self.defaultForeground = self["foreground"]
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
self.bind("<Button-1>", self.hover_click)
self.state = 0
self.position = position
def on_enter(self, e):
if self.state == 0:
self['background'] = self['activebackground']
self['foreground'] = self['activeforeground']
def on_leave(self, e):
if self.state == 2:
self.state = 0
if self.state == 0:
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
def hover_click(self, e):
self.state += 1
self.state = self.state % 3
if self.state == 2:
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
def default_coloring(self):
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
class AddOnFrame(tk.Frame):
def __init__(self, master):
self.selectedbutton = None
super().__init__(master)
games = ['A','B','C']
self.objs = list()
self['bg'] = blue
for i in range(3):
self.objs.append(HoverButton(self,position = i, text = games[i].upper(), activebackground = white,activeforeground = blue,fg = white, bg = blue, borderwidth=0, relief = 'flat', highlightbackground = white))
self.objs[i]['command'] = lambda c=i: self._hover_button_clicked(self.objs[c])
self.objs[i].grid(row = i, column = 0, sticky = tk.W + tk.E)
self.blanklabel = tk.Label(self, text = '', background = white)
self.blanklabel.grid(row = 0, column = 1,rowspan = 10, sticky = tk.N + tk.E + tk.W + tk.S)
self.grid_columnconfigure(1, weight=1, minsize=10)
self.grid_columnconfigure(2, weight=1, minsize=500)
self.grid_columnconfigure(3, weight=1, minsize=500)
self.grid_columnconfigure(4, weight=1, minsize=500)
self.pack(expand = True)
def _hover_button_clicked(self, HoverButton):
self.lastbutton = self.selectedbutton
if self.lastbutton != None:
self.objs[self.lastbutton].default_coloring()
self.selectedbutton = HoverButton.position
window = tk.Tk()
window.geometry('1750x950')
window['bg'] = blue
window.title('Testing')
lf = AddOnFrame(window)
lf['bg'] = blue
window.mainloop()
I think I found the main source of the problem. When another button is clicked, you restore color of the last clicked button, but you do not reset its state. Change your default_coloring function to:
def default_coloring(self):
self.state = 0
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
But you should also prevent default_coloring if same button is pressed again:
def _hover_button_clicked(self, HoverButton):
self.lastbutton = self.selectedbutton
if (self.lastbutton != None) and (self.lastbutton != HoverButton.position):
self.objs[self.lastbutton].default_coloring()
self.selectedbutton = HoverButton.position
After cursory inspection, this sequence seems to be the problem:
When a button is clicked, the AddOnFrame._hover_button_clicked
method is invoked.
AddOnFrame.selectedbutton is initially None, which means the
if-statement in AddOnFrame._hover_button_clicked will not be
executed the first time. This is why the buttons seem to work the
first time you click them, but not after that.
However, the next time it is invoked (the next time a button is
pressed), AddOnFrame.selectedbutton is not None, and will never
be None again, meaning that from now on, every click will result in
a call to that HoverButton's default_coloring method.
default_coloring is invoked as soon as a button is clicked, which
results in a quick flash from the active color to the default color,
and the button does not stay highlighted.
The quick fix:
Basically, don't do the default_coloring stuff. It seems to be hurting you more than it's helping. Not really sure why you're doing it in the first place (all that stuff with setting the command, the lambda, the whole _hover_button_clicked method) since the buttons seem to be setting their colors back to the default just fine when on_leave or hover_click are invoked. You can fix your problem by changing the body of your HoverButton.default_coloring function to this:
def default_coloring(self):
return
The real fix would be some restructuring of your code.
EDIT I'm offering this to help you simplify things:
import tkinter as tk
colors = {
"white": "#FFFFFF",
"blue": "#0000BB"
}
class HoverButton(tk.Button):
def __init__(self, *args, **kwargs):
tk.Button.__init__(self, *args, **kwargs)
self.is_selected = False
self.is_highlighted = False
self["borderwidth"] = 0
self["relief"] = tk.FLAT
self["font"] = ("United Sans Cd Bk", 30)
self["activeforeground"] = colors["blue"]
self["activebackground"] = colors["white"]
self["highlightbackground"] = colors["white"]
self.recolor()
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
self.bind("<Button-1>", self.on_click)
def recolor(self):
self["background"] = [colors["blue"], colors["white"]][self.is_highlighted]
self["foreground"] = [colors["white"], colors["blue"]][self.is_highlighted]
def on_enter(self, *args):
self.is_highlighted = True
self.recolor()
def on_leave(self, *args):
if self.is_selected:
return
self.is_highlighted = False
self.recolor()
def on_click(self, *args):
self.is_selected = not self.is_selected
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Window")
self.geometry("256x256")
self.resizable(width=False, height=False)
self["background"] = colors["blue"]
button_labels = ["A", "B", "C"]
self.buttons = []
for row, button_label in enumerate(button_labels):
button = HoverButton(text=button_label)
button.grid(row=row, column=0, sticky=tk.W)
self.buttons.append(button)
def main():
application = Application()
application.mainloop()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
I wanted to program a simple graphical version of Tic Tac Toe in Python after having previously made a text based one with a friend. For this I used tkinter. I have been able to get rid of all mistakes in the first window where you choose which symbol you want. But in the second window when you are supposed to place it, when I press one of the buttons it tells me that "pyimage2" doesn't exist which seems to be a common error
I have already checked out some other threads where I was told that I should use TopLevel instead, since there can only be one instance of Tk(). But I already am using it, and when I try using two instances by using destroy() on the first, the error remains. I have also switched from simple PhotoImage to PIL, but even that is of no help
from tkinter import *
from random import *
from PIL import Image, ImageTk
global root
global SpielerSymbol
SpielerSymbol = "Defaultshit"
def combine_funcs(*funcs):
def combined_func(*args, **kwargs):
for f in funcs:
f(*args, **kwargs)
class SelectionScreen:
def __init__(self,pRoot):
Imageo = Image.open("o.png")
o = ImageTk.PhotoImage(Imageo)
Imagex = Image.open("x.png")
x = ImageTk.PhotoImage(Imagex)
Screen = Toplevel(pRoot)
self.TextField = Label(Screen, text="Please choose a symbol.")
self.TextField.grid(row=1,column=1)
self.ButtonX = self.SelectionButton(x,Screen)
self.ButtonX.Choice.grid(row = 2, column = 1)
self.ButtonO = self.SelectionButton(o,Screen)
self.ButtonO.Choice.grid(row = 2, column = 2)
Screen.mainloop()
class SelectionButton:
def __init__(self, pImage, pScreen):
self.Choice = Button(pScreen, image = pImage, command = lambda: combine_funcs(setSpielerSymbol(str(pImage)), pScreen.destroy(), pScreen.quit()))
def setSpielerSymbol(pZeichen):
global SpielerSymbol
SpielerSymbol = pZeichen
class Game:
def __init__(self, pRoot):
global SpielerSymbol
ImageFeldx = Image.open("Feldx.png")
Feldx = ImageTk.PhotoImage(ImageFeldx)
ImageFeldo = Image.open("Feldo.png")
Feldo = ImageTk.PhotoImage(ImageFeldo)
ImageFeld = Image.open("Feld.png")
Feld = ImageTk.PhotoImage(ImageFeld)
Window = Toplevel(pRoot)
Feld1 = [self.Feld(Feld,Window,1,1), self.Feld(Feld,Window,1,2), self.Feld(Feld,Window,1,3),
self.Feld(Feld,Window,2,1), self.Feld(Feld,Window,2,2), self.Feld(Feld,Window,2,3),
self.Feld(Feld,Window,3,1), self.Feld(Feld,Window,3,2), self.Feld(Feld,Window,3,3)]
Window.mainloop()
class Feld:
def __init__(self, pImage, pWindow, pRow, pColumn):
self.Feld1 = Button(pWindow, image = pImage, command =lambda: combine_funcs(self.setFeldImage(self), Window.quit()) )
self.Feld1.grid(row=pRow,column=pColumn)
def setFeldImage(self, pFeld1):
pFeld1.Feld1.config(image=SpielerSymbol)
def main():
root = Tk()
root.withdraw()
SelectionScreen1 = SelectionScreen(root)
print("Das ist das Werk von Feiglingen")
Game1 = Game(root)
main()
The output should be two windows, first the one where you choose a symbol, that one should work fine, and the second should be a tic tac toe field where clicking on a button should display the symbol you've chosen. And what I instead get is the error message image "pyimage1" doesn't exist
Also sorry for the ugly code, I am still a beginner, especially at Python
So I have the following code creating a grid of buttons using tkinter:
class Application(Frame):UP = 'Up'
DOWN = 'Down'
LEFT = 'Left'
RIGHT = 'Right'
END = "E"
SEND = "S"
STAR = "*"
POUND = "#"
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.arrowButtons = []
self.arrowButtons += [Button(self, text=self.LEFT[0], command=self.buttonPressFactory(self.LEFT) )]
self.arrowButtons += [Button(self, text=self.UP[0], command=self.buttonPressFactory(self.UP) )]
self.arrowButtons += [Button(self, text=self.RIGHT[0], command=self.buttonPressFactory(self.RIGHT) )]
self.arrowButtons += [Button(self, text=self.DOWN[0], command=self.buttonPressFactory(self.DOWN) )]
self.send = Button(self, text=self.SEND, command=self.buttonPressFactory(self.SEND) )
self.end = Button(self, text=self.END, command=self.buttonPressFactory(self.END) )
self.send.grid(row=2,column=1)
self.end.grid(row=2,column=3)
self.numButtons = []
for i in range(0,10):#make the number buttons
self.numButtons.append(Button(self, text=str(i), command=self.buttonPressFactory(str(i))))
self.starButton = Button(self, text=self.STAR, command=self.buttonPressFactory(self.STAR) )
self.hashButton = Button(self, text=self.POUND, command=self.buttonPressFactory(self.POUND) )
self.arrowButtons[0].grid(row=1,column=1)
self.arrowButtons[1].grid(row=1,column=2)
self.arrowButtons[2].grid(row=1,column=3)
self.arrowButtons[3].grid(row=2,column=2)
self.send.grid(row=2,column=1)
self.end.grid(row=2,column=3)
self.numButtons[1].grid(row=3,column=1)
self.numButtons[2].grid(row=3,column=2)
self.numButtons[3].grid(row=3,column=3)
self.numButtons[4].grid(row=4,column=1)
self.numButtons[5].grid(row=4,column=2)
self.numButtons[6].grid(row=4,column=3)
self.numButtons[7].grid(row=5,column=1)
self.numButtons[8].grid(row=5,column=2)
self.numButtons[9].grid(row=5,column=3)
self.starButton.grid(row=6,column=1)
self.numButtons[0].grid(row=6,column=2)
self.hashButton.grid(row=6,column=3)
def press(self, x):
print(x)
def buttonPressFactory(self, button):
def buttonPress(*args):
self.press(button)
root.bind("<"+button+">", buttonPress)
return buttonPress
if __name__ == '__main__':
root = Tk()
app = Application(root)
app.master.title("stackoverflow is great")
app.mainloop()
The problem started when I added the line in buttonPressFactory that starts "root.bind("
When I added that line, suddenly clicking any button seems to call press(1) then call press(whatever). The keybinds work correctly except for 1,2,3,4, and 5 (on the numpad or numrow). If you remove that line, everything works ok, but of course then you can't use the keybinds. I am new to tkinter, so I might have made an obvious mistake, though my intuition tells me I might have made a syntax mistake.
How can I add keybindings to the code without breaking it?
'<1>' is mouse button 1, '1' is the literal character (relevant docs). So don't use brackets on literal keys.
Here is the fixed buttonPressFactory:
def buttonPressFactory(self, button):
def buttonPress(*args):
self.press(button)
eventname = '<' + button + '>' if len(button) > 1 else button
root.bind(eventname, buttonPress)
return buttonPress
Regarding your statement
I am new to tkinter, so I might have made an obvious mistake, though
my intuition tells me this is actually a python mistake.
A good rule of thumb is that it is almost always your mistake. That's true for everyone.