I am creating a program in Python that creates a shape based on user input. I need to create two functions to create buttons using Zeller's graphic.py file. One button needs to say Quit and the second needs to say Process. Here is what i have so far but as you can see, they are not in defined functions:
#create Quit button
quitButton = Text(Point(70,73), "Quit")
quitButton.draw(w)
Rectangle(Point(45, 50), Point(95,97)).draw(w)
#create Process button
enterButton = Text(Point(145,73), "Process")
enterButton.draw(w)
Rectangle(Point(120, 48), Point(170,98)).draw(w)
Here is a description of the necessary methods
createButton(text, pt1button, pt2button, win) creates a rectangle with corner points pt1button and pt2button with centered text in window win
clickedButton(button, clickPt) returns true/false if clickPt is in button.
I tried to create the function and received the following error.
Here is my function:
def createButton(text, pt1button, pt2button, win):
button = Text(Point(pt1button, pt2button), text)
button.draw(win)
Here is where I called the function:
createButton("Process",145,73,win)
createButton("Quit",70,73,win)
Here is the error that was thrown:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/project4FINAL.py", line 77, in <module> main()
File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/project4FINAL.py", line 27, in main buttonCreate("Process", 145,73, win)
NameError: global name 'win' is not defined
Any suggestions?
So from looking at the code it looks like you want to create a callback function for each button and then assign each to the canvas via the setMouseHandler of method of GraphWin.
So from the example given in the API:
from graphics import *
def example_callback():
print "I love unicorns!"
def main():
# win is a Canvas with a setMouseHandler method
win = GraphWin("My Circle", 100, 100)
c = Circle(Point(50,50), 10)
c.draw(win)
#Add a callback to the canvas
c.cavas.setMouseHandler(example_callback)
# or win.setMouseHandler(example_callback)
win.getMouse() # Pause to view result
win.close() # Close window when done
main()
Unless you have bounds checking in you callbacks (to see which shape on the canvas the mouse is inside), you should only have one canvas per drawn shape.
An example following the use of a createButton function:
def createButton(text, pt1button, pt2button, win):
button = Text(Point(pt1button, pt2button), text)
button.draw(win)
return button
def _callback(pt):
print "I love unicorns!"
print
print "Mouse clicked at x=%d, y=%d"%(pt.x,pt.y)
print
def test():
win = GraphWin()
win.setCoords(0,0,100,100)
quitButton = createButton("Quit",70,73,win)
Rectangle(Point(45, 50), Point(95,97)).draw(win)
win.setMouseHandler(_callback)
while True:
win.getMouse()
win.close()
Below is a complete example using a new Button object:
from graphics import *
class Button(object):
def __init__(self, text, text_pos, rect_pos, win, callback):
self.win = win
self.text = Text(text_pos, text)
self.text.draw(self.win)
# the limits of the button will be defined by the rectangle
self.coords = [rect_pos[0].x,rect_pos[0].y,rect_pos[1].x,rect_pos[1].y]
self.rect = Rectangle(*rect_pos)
self.rect.draw(self.win)
# set the buttons callback
self.callback = callback
def _is_inside(self,click):
limits = self.coords
return (limits[0] < click.x < limits[2]) and (limits[1] < click.y < limits[3])
class MyWindow(object):
def __init__(self,coords=(0,0,100,100)):
self.win = GraphWin()
self.win.setCoords(*coords)
# a list of all possible buttons
self.buttons = []
# register the master callback function
self.win.setMouseHandler(self._callback)
self._quit = False
#create a quit and confess button with a custom create method
self.create_button("Quit",(Point(10,10),Point(40,40)),Point(20,20),self.quit)
self.create_button("Confess",(Point(50,50),Point(80,80)),Point(60,60),self.confess)
def create_button(self,text,coords,text_coords,callback):
button = Button(text,text_coords,coords,self.win,callback)
# create a button and add it to our list of buttons
self.buttons.append(button)
def confess(self,point):
print
print "I love unicorns!"
print
def quit(self,point):
self._quit = True
self.win.close()
# This function is called for any click on the canvas
def _callback(self,point):
# Need to do a coordinate transform here to get the click in the coordinates of the button
x,y = self.win.trans.world(point.x,point.y)
point = Point(x,y)
print "Clicked at x=%d, y=%d"%(point.x,point.y)
# cycle through buttons and execute all for which the clicked point lies inside their rectangle
for button in self.buttons:
if button._is_inside(point):
button.callback(point)
# a loop to keep getting mouse input
def run(self):
while not self._quit:
try:
self.win.getMouse()
except GraphicsError:
break
if __name__ == "__main__":
x = MyWindow()
x.run()
Related
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()
I'm looking for a way to change the rendered text while the game is running.
I found someone who says that simply change the text variable would work.
So I try this:
import arcade
WINDOW = {"width":800, "height": 600, "title": ""}
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self):
# Call the parent class and set up the window
super().__init__(WINDOW['width'], WINDOW['height'], WINDOW['title'])
arcade.set_background_color(arcade.csscolor.CORNFLOWER_BLUE)
def setup(self):
""" Set up the game here. Call this function to restart the game. """
pass
def on_draw(self):
""" Render the screen. """
arcade.start_render()
# Code to draw the screen goes here
self.text = "Hello world!"
arcade.draw_text(self.text, WINDOW['width'] / 3 + (WINDOW['width'] / 3 / 3) - 20, WINDOW['height'] / 2, arcade.csscolor.WHITE, 18)
def on_mouse_release(self, x, y, button, key_modifiers):
print("Clicked!")
self.text = "Clicked!"
def main():
""" Main method """
window = MyGame()
window.setup()
arcade.run()
if __name__ == "__main__":
main()
But still, The text didn't change but It can detect clicks.
arcade.run() runs loop which executes on_draw() in this loop. If code runs with speed 25 FPS (Frames Per Second) then it executes on_draw() 25 times in every second. So when you click mouse then on_mouse_release() changes text to "Clicked!" and later on_draw() changes it back to "Hello world!" and finally it displays "Hello world!"
You should use self.text = "Hello world!" in __init__() or (better) in setup() to set it only once.
import arcade
WINDOW = {"width":800, "height": 600, "title": ""}
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self):
# Call the parent class and set up the window
super().__init__(WINDOW['width'], WINDOW['height'], WINDOW['title'])
arcade.set_background_color(arcade.csscolor.CORNFLOWER_BLUE)
def setup(self):
""" Set up the game here. Call this function to restart the game. """
self.text = "Hello world!"
def on_draw(self):
""" Render the screen. """
arcade.start_render()
# Code to draw the screen goes here
arcade.draw_text(self.text, WINDOW['width'] / 3 + (WINDOW['width'] / 3 / 3) - 20, WINDOW['height'] / 2, arcade.csscolor.WHITE, 18)
def on_mouse_release(self, x, y, button, key_modifiers):
print("Clicked!")
self.text = "Clicked!"
def main():
""" Main method """
window = MyGame()
window.setup()
arcade.run()
if __name__ == "__main__":
main()
Here is the short example of changing text on mouse click:
import arcade
class Game(arcade.Window):
def __init__(self):
super().__init__(400, 300)
self.text = 'Waiting for click!'
def on_draw(self):
arcade.start_render()
arcade.draw_text(self.text, 200, 150, arcade.color.RED, 18, anchor_x='center')
def on_mouse_release(self, x, y, button, key_modifiers):
self.text = 'Clicked!'
Game()
arcade.run()
Output:
I am attempting to create a layout using Tkinter for Python3 that involves several buttons and scales. The buttons work fine, but the command that I give to the scale widget is called when I call grid on the scale. Why is this happening and what can I do to stop it?
Here is a simplified version of my code:
import tkinter
import time
WINDOW_HEIGHT = 150
WINDOW_WIDTH = 340
class Player(object):
def __init__(self):
self.window = tkinter.Tk()
self.window.geometry(str(WINDOW_WIDTH) + 'x' + str(WINDOW_HEIGHT))
self.current_time = tkinter.DoubleVar()
self.progress_bar = tkinter.Scale(self.window,
variable = self.current_time,
command = self.set_current_time,
orient = tkinter.HORIZONTAL,
showvalue = 0, resolution=.001)
self.progress_bar.grid(row=1, column=10)
def set_current_time(self, time):
print('setting current time')
print(time)
def update(self):
self.window.update_idletasks()
self.window.update()
def main():
media_player = Player()
while True:
media_player.update()
time.sleep(.1)
if __name__ == "__main__":
main()
The set_current_time function should only be called when the slider is actually clicked and moved, however as soon as grid is executed, set_current_time is called with a time value of 0. How can I place the slider without executing the command? After placement the slider works as expected, but I would like to avoid the initial calling of the set_current_time function.
I've created a game "dice poker" using Zelle's graphing package, and have a button on the main screen which opens a text file. The text file opens when the button is clicked, but the main window closes. How can I keep the parent window open?
The button class is below:
from graphics import *
from tkinter import Button as tkButton
class Button():
"""A button is a labeled rectangle in a window.
It is activated or deactivated with the activate()
and deactivate() methods. The clicked(p) method
returns true if the button is active and p is inside it."""
def __init__(self, win, center, width, height, label):
""" Creates a rectangular button, eg:
qb = Button(myWin, centerPoint, width, height, 'Quit') """
w,h = width/2.0, height/2.0
x,y = center.getX(), center.getY()
self.xmax, self.xmin = x+w, x-w
self.ymax, self.ymin = y+h, y-h
p1 = Point(self.xmin, self.ymin)
p2 = Point(self.xmax, self.ymax)
self.rect = Rectangle(p1,p2)
self.rect.setFill('lightgray')
self.rect.draw(win)
self.label = Text(center, label)
self.label.draw(win)
self.deactivate()
def clicked(self, p):
"Returns true if button active and p is inside"
return (self.active and
self.xmin <= p.getX() <= self.xmax and
self.ymin <= p.getY() <= self.ymax)
def getLabel(self):
"Returns the label string of this button."
return self.label.getText()
def activate(self):
"Sets this button to 'active'."
self.label.setFill('black')
self.rect.setWidth(2)
self.active = True
def deactivate(self):
"Sets this button to 'inactive'."
self.label.setFill('darkgrey')
self.rect.setWidth(1)
self.active = False
How can I include a command argument that can open an executable in a fashion similar to this tkinter implementation:
import Tkinter as tk
def create_window():
window = tk.Toplevel(root)
root = tk.Tk()
b = tk.Button(root, text="Create new window", command=create_window)
b.pack()
root.mainloop()
Where the command can be subprocess.run(['open', '-t', 'poker_help.txt']) and still keep the original window open?
I have to make some assumptions since you didn't include top level code (e.g. you're on a Mac):
Zelle graphics, unlike tkinter and turtle, which is also built on tkinter, doesn't have an explicit win.mainloop() call to turn control over to the Tk event handler to idle awaiting events to happen. Instead, you have to patch one together yourself, otherwise once you get the mouse click that fires off your button, the program falls through the end of the file and the main window closes:
import subprocess
from graphics import *
from button import Button
win = GraphWin()
help_button = Button(win, Point(150, 150), 50, 50, "Help")
help_button.activate()
quit_button = Button(win, Point(50, 50), 50, 50, "Quit")
quit_button.activate()
while True:
point = win.getMouse()
if help_button.clicked(point):
subprocess.call(['open', '-t', 'poker_help.txt'])
elif quit_button.clicked(point):
win.close()
Where from button import Button brings in your button code above. Another thing to check is your window is actually closing, and not simply being obscured by the new window opened atop it.
I've been having this problem with a python program I am making where if I display a TopLevel window, in this case my Help Menu, then withdraw it then try to display it again I get the following error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1533, in __call__
return self.func(*args)
File "C:\Users\****\Documents\GitHub\ProjectName\ProjectName\GUI.py", line 60, in displayHelp
self.helpMenu.display();
File "C:\Users\****\Documents\GitHub\ProjectName\ProjectName\HelpMenu.py", line 35, in display
self.deiconify();
File "C:\Python34\lib\tkinter\__init__.py", line 1646, in wm_deiconify
return self.tk.call('wm', 'deiconify', self._w)
_tkinter.TclError: bad window path name ".60000336"
The error first happened when I was withdrawing from within HelpMenu.py and using deiconify to redisplay it from the GUI.py file.
Since then I have tried multiple ways to fix the problem including calling deiconify from within HelpMenu.py and updating the copy of help menu stored in the GUI when I withdraw it.
I am running Python 3.4.2
I have already done extensive searches online and failed to find a solution to my problem. I have found other mentions of this error but they either didn't relate to my situation or their solutions did not work.
Here is the entire code for the HelpMenu.py followed by an extract from GUI.py that retains the functionality to reproduce the error but has other code removed.
#!/usr/bin/python
try:
from Tkinter import *
except ImportError:
from tkinter import *
class HelpMenu(Toplevel):
def __init__(self, parent, observer):
Toplevel.__init__(self);
self.observer = observer;#Observer is the GUI, this is here just so I can update the GUI when I withdraw this window
self.setup();
self.withdraw();
self.protocol('WM_DELETE_WINDOW', self.quit());#Changes the close button to just hide the window
def setup(self):
self.columnconfigure(0,weight=1);
w = 400;#Sets up the window position on the screen
h = 150;
sw = self.winfo_screenwidth();
sh = self.winfo_screenheight();
x=(sw-w)/2;
y =(sh-h)/2;
self.update();
self.geometry('%dx%d+%d+%d' % (w,h,x,y));
self.resizable(width=0, height=0);
self.grid();
self.title("Help Menu");
def quit(self):#Hides the window
self.withdraw();
self.observer.updateHelp(self);
def display(self):#Re-displays the window
self.deiconify();
Here is code taken from GUI.py and modified to only have the code needed to reproduce the issue.
#!/usr/bin/python
#Allows compatibility with any version of Python by checking for both versions of Tkinter
try:
from Tkinter import *
except ImportError:
from tkinter import *
#Imports the AutoCompleteEntry
from HelpMenu import HelpMenu
class UI(Tk):
def initialize(self):
#Handles setting up most of the GUI
w = 500;#Window width
h = 500;#Window height
sw = self.winfo_screenwidth();#Gets screen width
sh = self.winfo_screenheight();#Gets screen height
x=(sw-w)/2;#Calculates the x position for the left side of the window that allows it to be placed in the center of the screen
y =(sh-h)/2;#Calculates the y position for the top of the window that allows it to be placed in the center of the screen
self.update();#Forces and update on the window
self.geometry('%dx%d+%d+%d' % (w,h,x,y));#Sets the windows width, height and position
self.minsize(int(w),int(h/2));#Sets the minimum size of the window
self.configureMenu();
def updateHelp(self, helpMenu):
self.helpMenu=helpMenu;
def displayHelp(self):
self.helpMenu.display();
def configureMenu(self):
#Handles configuring and setting up the menus
menu = Menu(self);#Setup the menu bar
menu.add_command(label="Help",command=self.displayHelp);
self.config(menu=menu);
def __init__(self, parent):
#Handles the initial call to create a GUI
Tk.__init__(self,parent);#Parent constructor
self.parent = parent;#Store the parent
self.initialize();#Initilize the GUI
self.helpMenu = HelpMenu(self, self);
self.mainloop();#Start the main loop
if __name__ == "__main__":
import sys
main = UI(None);
One last note, I am slightly new to Python, so there might be other errors in my code and while I wont mind if they get pointed out, the main focus I have right now is fixing this path name error.
EDIT:Almost a month now and I have still not found a solution to the problem. Any help would be great but at this point I am probably going to have to abandon my project.
So, after a break I went back to look at this problem again.
Turns out that the issue was self.protocol('WM_DELETE_WINDOW', self.quit()) was not actually calling self.quit() and was destroying the window completely.
A quick change to self.protocol('WM_DELETE_WINDOW', self.quit) seems to have fixed it.
I think maybe the comma causes the problem. Try write it like this:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
try:
from Tkinter import *
except ImportError:
from tkinter import *
class HelpMenu(Toplevel):
def __init__(self, parent, observer):
Toplevel.__init__(self)
self.observer = observer # Observer is the GUI, this is here just so I can update the GUI when I withdraw this window
self.setup()
self.withdraw()
self.protocol('WM_DELETE_WINDOW', self.quit()) # Changes the close button to just hide the window
def setup(self):
self.columnconfigure(0, weight=1)
w = 400 # Sets up the window position on the screen
h = 150
sw = self.winfo_screenwidth()
sh = self.winfo_screenheight()
x = (sw - w) / 2
y = (sh - h) / 2
self.update()
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
self.resizable(width=0, height=0)
self.grid()
self.title("Help Menu")
def quit(self): # Hides the window
self.withdraw()
self.observer.updateHelp(self)
def display(self): # Re-displays the window
self.deiconify()
class UI(Tk):
def initialize(self):
# Handles setting up most of the GUI
w = 500 # Window width
h = 500 # Window height
sw = self.winfo_screenwidth() # Gets screen width
sh = self.winfo_screenheight() # Gets screen height
x = (sw - w) / 2 # Calculates the x position for the left side of the window that allows it to be placed in the center of the screen
y = (sh - h) / 2 # Calculates the y position for the top of the window that allows it to be placed in the center of the screen
self.update() # Forces and update on the window
self.geometry('%dx%d+%d+%d' % (w, h, x, y)) # Sets the windows width, height and position
self.minsize(int(w), int(h / 2)) # Sets the minimum size of the window
self.configureMenu()
def updateHelp(self, helpMenu):
self.helpMenu = helpMenu
def displayHelp(self):
self.helpMenu.display()
def configureMenu(self):
# Handles configuring and setting up the menus
menu = Menu(self) # Setup the menu bar
menu.add_command(label="Help", command=self.displayHelp)
self.config(menu=menu)
def __init__(self, parent):
# Handles the initial call to create a GUI
Tk.__init__(self, parent) # Parent constructor
self.parent = parent # Store the parent
self.initialize() # Initilize the GUI
self.helpMenu = HelpMenu(self, self)
self.mainloop() # Start the main loop
if __name__ == "__main__":
main = UI(None)
It works perfectly from myside.