Why canvas appears above widget in tkinter? - python

I am using tkinter in python3. I created a die class as a subclass of the canvas widget. Then, I created a subclass of the original die class that allows me to freeze the die and prevent it from being rolled. Finally, I created a custom widget as a subclass of frame, which contains both the freezable die and a toggle freeze button. However, even though I grid the die canvas before I grid the button, the button still appears above the canvas. (It did not work to try to control the rows manually) Here is my code:
from tkinter import *
import random
class GUIDie(Canvas):
'''6-sided Die class for GUI'''
def __init__(self,master,valueList=[1,2,3,4,5,6],colorList=['black']*6):
'''GUIDie(master,[valueList,colorList]) -> GUIDie
creates a GUI 6-sided die
valueList is the list of values (1,2,3,4,5,6 by default)
colorList is the list of colors (all black by default)'''
# create a 60x60 white canvas with a 5-pixel grooved border
Canvas.__init__(self,master,width=60,height=60,bg='white',\
bd=5,relief=GROOVE)
# store the valuelist and colorlist
self.valueList = valueList
self.colorList = colorList
# initialize the top value
self.top = 1
def get_top(self):
'''GUIDie.get_top() -> int
returns the value on the die'''
return self.valueList[self.top-1]
def roll(self):
'''GUIDie.roll()
rolls the die'''
self.top = random.randrange(1,7)
self.draw()
def draw(self):
'''GUIDie.draw()
draws the pips on the die'''
# clear old pips first
self.erase()
# location of which pips should be drawn
pipList = [[(1,1)],
[(0,0),(2,2)],
[(0,0),(1,1),(2,2)],
[(0,0),(0,2),(2,0),(2,2)],
[(0,0),(0,2),(1,1),(2,0),(2,2)],
[(0,0),(0,2),(1,0),(1,2),(2,0),(2,2)]]
for location in pipList[self.top-1]:
self.draw_pip(location,self.colorList[self.top-1])
def draw_pip(self,location,color):
'''GUIDie.draw_pip(location,color)
draws a pip at (row,col) given by location, with given color'''
(centerx,centery) = (17+20*location[1],17+20*location[0]) # center
self.create_oval(centerx-5,centery-5,centerx+5,centery+5,fill=color)
def erase(self):
'''GUIDie.erase()
erases all the pips'''
pipList = self.find_all()
for pip in pipList:
self.delete(pip)
class GUIFreezeableDie(GUIDie):
'''a GUIDie that can be "frozen" so that it can't be rolled'''
def __init__(self,master,valueList=[1,2,3,4,5,6],colorList=['black']*6):
'''GUIFreezeableDie(master,[valueList,colorList]) -> GUIFreezeableDie
creates a GUI 6-sided freeze-able die
valueList is the list of values (1,2,3,4,5,6 by default)
colorList is the list of colors (all black by default)'''
# you add code here
GUIDie.__init__(self, master, valueList, colorList)
self.isFrozen = False
def is_frozen(self):
'''GUIFreezeableDie.is_frozen() -> bool
returns True if the die is frozen, False otherwise'''
return self.isFrozen
def toggle_freeze(self):
'''GUIFreezeableDie.toggle_freeze()
toggles the frozen status'''
if self.is_frozen():
self.isFrozen = False
self.configure(background = "white")
else:
self.isFrozen = True
self.configure(background = "grey")
def roll(self):
'''GuiFreezeableDie.roll()
overloads GUIDie.roll() to not allow a roll if frozen'''
if not self.is_frozen():
GUIDie.roll(self)
class GUIDieSet(Frame):
def __init__(self,master,valueList=[1,2,3,4,5,6],colorList=['black']*6):
'''GUIFreezeableDie(master,[valueList,colorList]) -> GUIFreezeableDie
creates a GUI 6-sided freeze-able die
valueList is the list of values (1,2,3,4,5,6 by default)
colorList is the list of colors (all black by default)'''
Frame.__init__(self,master)
self.grid()
self.die = GUIFreezeableDie(master, valueList, colorList)
self.die.grid()
self.toggleFreeze = Button(self,text='Freeze',command=self.die.toggle_freeze)
self.toggleFreeze.grid(row=1)
def roll(self):
self.die.roll()
class FreezeTest(Frame):
'''a small application to test the freezeable die'''
def __init__(self,master):
Frame.__init__(self,master)
self.grid()
self.die = GUIDieSet(self)
self.die.grid()
# test application
root = Tk()
test = TestFrame(root)
root.mainloop()

In the class GUIDieSet(), remove the line self.grid() in __init()__ and it seems to work.

Related

Tkinter widgets created in an Update function run by tk.after function do not create untill the aforementioned Update ends

I intend to make a Py code which creates a tkinter dot that turns on a key press and deletes on a key press of couple keys.
The dot already is functional but i need it switch on and off on certain keypresses/mouse clicks which means i need an outside tkinter.mainloop() Update function.
The Update function with a while in it to constantly check if conditions to turn it off/on are present. But the Tkinter widget Somehow gets applied to the screen Only when the function nds. Like widget could be created but it will only take effect when function ends. And i need to turn it off/on dynamically.
I have tried to use a tkinter.after() with additional one at the end of called function only to find out an error of Recursion depth. What i expected to happen was that the function would be called over and over again, instead it runs that function like a while loop. I also have tried Asyncio.run() but it would result not making it visible till the function ends at least once. And I need to change it dynamically.
from tkinter import *
from tkinter import Canvas
from winsound import Beep
from time import sleep
import asyncio
import keyboard
import mouse
root = Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
class tk_Dot():
def __init__(self,x=-1,y=-1,radius=4,color="red"):
self.x = x
if x == -1:
self.x = width/2-radius//2
print(self.x)
self.y = y
if y == -1:
self.y = height/2+radius//2
print(self.y)
self.radius=radius
self.color = color
self.lines = []
self.count = 1
def line(self,i):
return canvas.create_line(self.x, self.y-i, self.x+self.radius, self.y-i, fill=self.color)
def create(self):
self.lines = []
for i in range(0,self.radius):
self.lines.append(self.line(i))
def delete(self):
for i in range(0,self.radius):
canvas.delete(self.lines[i])
canvas.dtag(self.lines[i])
opacity_of_tk_window = 1 # From 0 to 1 0 meaning completely transparent 1 meaning everything created in canvas will give the color it was given
root.attributes('-alpha',opacity_of_tk_window)
# Invisible Tkinter window label
root.overrideredirect(True)
# Makes Transparent background
transparent_color = '#f0f0f0'
root.wm_attributes('-transparent', transparent_color)
canvas = Canvas()
# Rectangle filled with color that is specified above as the transparent color so practically making transparent background
canvas.create_rectangle(0, 0, width, height, fill=transparent_color)
canvas.pack(fill=BOTH, expand=1)
radius = 2
radius = 1+radius\*2
# Create a dot class
game_dot = tk_Dot(width/2-radius//2+1,height/2+1+radius//2,radius,"Red")
# Create a Dot at the middle of the calorant crosshair
# game_dot.create()
# Delete the dot
# game_dot.delete()
def Update():
game_dot.create()
print("Dot should be visible by now")
print("Is it?")
sleep(5) #sec
print("Oh yeah after the function ends.") # the problem
def Delete():
game_dot.delete()
root.geometry('%dx%d+%d+%d' % (width, height, -2,-2))
# Tkinter window always on top
root.attributes('-topmost',True)
root.after(1000,Update())
root.mainloop()

Maya Python append a button into a list creates 2 UI elements

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('|')

Monopoly python game using canvas player movement error

I am creating an online monopoly game using canvas and tkinter and having trouble trying to make the "player1.png" .ie my character to move across the board. Please help!
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Monopoly Physics Edition")
self.pack(fill=BOTH, expand=1)
canvas = Canvas(self)
global player1
load= Image.open("player1.png")
player1 = ImageTk.PhotoImage(load)
img = Label(image=player1)
img.place(x=834, y=60)
def main():
canvas = Tk()
ex = Example()
canvas.geometry("1150x820+800+700")
if __name__ == '__main__':
main()
Ok so I programmed in the basics here:
import tkinter as tk
from PIL import Image, ImageTk
class Player:
def __init__(self, canvas, sprite, pos):
self.posx, self.posy = pos
self.canvas = canvas
self.sprite = sprite
self.tk_image = None
self.canvas_id = None
def display(self):
# This will display the train card on the board
self.load_sprite()
# If we already displayed the object hide it first
if self.canvas_id is not None:
self.hide_from_canvas()
# We need to create a new canvas object and get its id
# so that later we can move/remove it from the canvas
self.canvas_id = self.canvas.create_image(self.posx, self.posy, image=self.tk_image)
def hide_from_canvas(self):
# This will remove the sprite from the canvas
self.canvas.delete(self.canvas_id)
def load_sprite(self):
# If we already loaded the sprite just don't do anything
if self.tk_image is not None:
return None
# Later you can determine the filename from `self.name`
filename = "img_small.png"
pillow_image = Image.open(filename)
# You must keep a reference to this `tk_image`
# Otherwise the image would show up on the board
self.tk_image = ImageTk.PhotoImage(pillow_image)
def move(self, change_x, change_y):
# Move the object by change_x, change_y
self.canvas.move(self.canvas_id, change_x, change_y)
class Property:
def __init__(self, canvas, name, cost, pos):
self.posx, self.posy = pos
self.canvas = canvas
self.name = name
self.cost = cost
self.tk_image = None
self.canvas_id = None
def display(self):
# This will display the train card on the board
self.load_sprite()
# If we already displayed the object hide it first
if self.canvas_id is not None:
self.hide_from_canvas()
# We need to create a new canvas object and get its id
# so that later we can move/remove it from the canvas
self.canvas_id = self.canvas.create_image(self.posx, self.posy, image=self.tk_image)
def hide_from_canvas(self):
# This will remove the sprite from the canvas
self.canvas.delete(self.canvas_id)
def load_sprite(self):
# If we already loaded the sprite just don't do anything
if self.tk_image is not None:
return None
# Later you can determine the filename from `self.name`
filename = "img_small.png"
pillow_image = Image.open(filename)
# You must keep a reference to this `tk_image`
# Otherwise the image would show up on the board
self.tk_image = ImageTk.PhotoImage(pillow_image)
def move(self, change_x, change_y):
# Move the object by change_x, change_y
self.canvas.move(self.canvas_id, change_x, change_y)
class App(tk.Canvas):
def __init__(self, master, **kwargs):
# Create the canvas
super().__init__(master, **kwargs)
# lets show all of the monopoly properties on the screen
self.init()
# I will also bind to key presses (You might want to use them later)
master.bind("<Key>", self.on_key_press)
# I will also bind to mouse presses
super().bind("<Button-1>", self.on_mouse_press)
def on_key_press(self, event):
print("You pressed this character:", repr(event.char))
print("For non character keys (like Escape):", repr(event.keysym))
# For now I will move the player sprite by 10 pixels to the right
# when "d" or the right key is pressed
if (event.char == "d") or (event.keysym == "Right"):
# Move the first player of list
self.players[0].move(10, 0)
def on_mouse_press(self, event):
print("You clicked with the mouse at this position:", (event.x, event.y))
def init(self):
# A list that will hold all of the properties:
self.properties = []
# A list that will hold all of the players playing
self.players = []
# I will only create 1 property (You can add the rest)
# I will create 1 of the trains where the cost is 200 and
# its position is (50, 50). Note (0, 0) is the top left
# corner of the canvas
train1 = Property(self, "Train", 200, (50, 50))
train1.display()
self.properties.append(train1)
# I will also create a player at position (100, 100)
player1 = Player(self, "player_sprite1", (100, 100))
player1.display()
self.players.append(player1)
# Create the window
root = tk.Tk()
# Make it so that the user can't resize the window
# Otherwise you will have a lot of problems with your images not being in
# the correct places
root.resizable(False, False)
# Create the App, I also passed in `width=200, height=200` those are in pixels
main_app = App(root, width=200, height=200)
# Show the App on the screen:
main_app.pack()
# Enter tkinter's mainloop
root.mainloop()
It allows us to move/hide/display any of the sprites we create. I also added a way for use to detect key/mouse presses (might come in handy later). Using this it shouldn't be hard to create the full program. Note that most of the methods in the Player and Property class are the same so if you know how to use inheritance, you should be able to simplify my code by a lot.

Building a chessboard with buttons, which is always a square

I'm trying to build a chessboard consisting of buttons.
I created 3 widgets in one line. There are labels outside (filling) and inside I want to put a chessboard.
I would like it to always occupy 90% of the screen width and automatically adjust its height so that it always remains a square. It would also be necessary to set the buttons always to be squares but I also can't handle it. Can You help me?
class ChessBoard(GridLayout):
def __init__(self, **kwargs):
super(ChessBoard, self).__init__(**kwargs)
self.cols = 8
for i in range(64):
self.cell = Button(text="", size_hint_y=self.height/8, height=self.width/8)
self.add_widget(self.cell)
class ChessBoardContainer(GridLayout):
def __init__(self, **kwargs):
super(ChessBoardContainer, self).__init__(**kwargs)
self.orientation='horizontal'
self.cols=3
self.lab1 = Label(text="1")
self.add_widget(self.lab1)
self.board = ChessBoard()
self.add_widget(self.board)
self.lab2 = Label(text="2")
self.add_widget(self.lab2)
class CombWidget(BoxLayout):
def __init__(self, **kwargs):
super(CombWidget, self).__init__(**kwargs)
self.orientation='vertical'
self.but1 = Button(text="But1", font_size=40)
self.add_widget(self.but1)
self.chessb = ChessBoardContainer()
self.add_widget(self.chessb)
self.but2 = Button(text="But2", font_size=40)
self.add_widget(self.but2)
class MyPaintApp(App):
def build(self):
return CombWidget()
Right now this is my result:
I would like to get something like this (Paint master ;) ). Maybe it could be done without this labels?
To make buttons be squares you just have to set the height and width of GridLayout cells, and you are trying to do it with size_hint. Try this:
from kivy.core.window import Window
class ChessBoard(GridLayout):
def __init__(self, **kwargs):
super(ChessBoard, self).__init__(**kwargs)
self.cols = 8
winsize = Window.size
sizedict = {}
# to set width and height of GridLayout cells, you should make a dict, where the key is col's/row's number and the value is size
for i in range(self.cols):
sizedict[i] = winsize[0]/8 #or you can divide it by 10 for example to have some black filling on the sides
# and then simply do this
self.cols_minimum = sizedict
self.rows_minimum = sizedict
This code produces buttons that look fairly square to me. If you plan to use images for your chess pieces, the buttons will conform to the size of those.
from tkinter import Tk, Button
window = Tk ()
squares = []
index = 0
for x in range (8) :
for y in range (8) :
squares.append (Button (window, width = 7, height = 4))
squares [index].grid (row = x, column = y)
index += 1
window.mainloop ()

how to create rectangles by getting scale values at the time in tkinter?

I want to get the value of a scale and create rectangles as many as the value is. For example, if I adjust the scale to number 7, 7 rectangles would be created next to each other, and after that if I adjust the scale value to 3, the rectangles shown in the canvas decreases to three at that moment. I had used the code below:
from tkinter import *
from tkinter import ttk
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.show()
def show(self):
x = 50
y = 50
for i in range(self.scale.get()):
self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
x += 50
self.canvas.pack()
self.scale.pack()
root = Tk()
a = rect(root)
root.mainloop()
I guess I have to use trace method, But I don't know how to.
Can anyone fix the code I used in the way which I explained.
Thank you.
One solution is to bind to <ButtonRelease>, and call your show method there. Since event bindings pass an object representing the event, you'll need to make that an optional argument if you also want to call show without any arguments.
For example:
class rect:
def __init__(self, root):
...
self.scale = Scale(...)
self.scale.bind("<ButtonRelease>", self.show)
I'm guessing you'll want to remove any previously drawn rectangles, so you'll need to call delete before creating the rectangles:
def show(...):
self.canvas.delete("all")
...

Categories