Tkinter Draw Problems - python

I just finished writing a Tkinter based GUI of the board game Othello for a programming class. Everything seems to be working correctly, but something strange is still happening: any changes that occur on the game board GUI are updated only when I click outside of the window.
This happens not only in my application, but in other Tkinter based GUI applications that my instructor has written--only when running on my machine. I've seen these applications work correctly on other machines, which has led me to believe that there's something specific to my machine that is causing this issue, and for this reason, I haven't included any code in my question.
Does anyone have any insight into this? I'm sorry if I haven't included sufficient details; I'm not exactly sure what to include. I'm using Python 3.3.2 and Tkinter 8.5
edit:
Here's the code for the GUI that my instructor wrote. It's a simple application that creates ovals on a canvas wherever the user clicks. It worked fine on the machines at school, but on my computer, when I click on the canvas, nothing happens until I click outside of the tkinter window. After clicking outside of the window, the spots draw correctly.
Also, I'm running OS X Mavericks (10.9).
import coordinate
import spots_engine
import tkinter
class SpotsApplication:
def __init__(self, state: spots_engine.SpotsState):
self._state = state
self._root_window = tkinter.Tk()
self._canvas = tkinter.Canvas(
master = self._root_window, width = 500, height = 450,
background = '#006000')
self._canvas.grid(
row = 0, column = 0, padx = 10, pady = 10,
sticky = tkinter.N + tkinter.S + tkinter.E + tkinter.W)
self._canvas.bind('<Configure>', self._on_canvas_resized)
self._canvas.bind('<Button-1>', self._on_canvas_clicked)
self._root_window.rowconfigure(0, weight = 1)
self._root_window.columnconfigure(0, weight = 1)
def start(self) -> None:
self._root_window.mainloop()
def _on_canvas_resized(self, event: tkinter.Event) -> None:
self._redraw_all_spots()
def _on_canvas_clicked(self, event: tkinter.Event) -> None:
width = self._canvas.winfo_width()
height = self._canvas.winfo_height()
click_coordinate = coordinate.from_absolute(
(event.x, event.y), (width, height))
self._state.handle_click(click_coordinate)
self._redraw_all_spots()
def _redraw_all_spots(self) -> None:
self._canvas.delete(tkinter.ALL)
canvas_width = self._canvas.winfo_width()
canvas_height = self._canvas.winfo_height()
for spot in self._state.all_spots():
center_x, center_y = spot.center_coordinate().absolute(
(canvas_width, canvas_height))
radius_x = spot.radius_frac() * canvas_width
radius_y = spot.radius_frac() * canvas_height
self._canvas.create_oval(
center_x - radius_x, center_y - radius_y,
center_x + radius_x, center_y + radius_y,
fill = '#ffff00', outline = '#000000')
if __name__ == '__main__':
SpotsApplication(spots_engine.SpotsState()).start()

If I'm not mistaken, the problem we are talking about here is a known bug:
http://bugs.python.org/issue19373
EDIT:
Installing/reinstalling ActiveTcl8.5.15.1.297588 over ActiveTcl8.6.1.1.297588 is working! My Tkinter GUI is responsive again.
NOTE: I'm using Python 3.3.3.

Related

App's icon is not show in taskbar because of Custom title bar?

I am creating an code editor in which I want to custom title bar to match my app theme and I have created an custom title bar but my app is not showing in taskbar
If any external libraries are for this, Please tell me
What libraries I have to learn to solve my problem please tell me
how to show app icon on taskbar, Actually I have no idea about it
if you can solve it
Please help me to solve my problem
this is my full code(not full code but short version of real one):-
from tkinter import*
def move(e):
xwin = root.winfo_x()
ywin = root.winfo_y()
startx = e.x_root
starty = e.y_root
ywin -= starty
xwin -= startx
def move_(e):
root.geometry(f"+{e.x_root + xwin}+{e.y_root + ywin}")
startx = e.x_root
starty = e.y_root
frame.bind("<B1-Motion>",move_)
def minieme1_(event=None):
root.update_idletasks()
root.overrideredirect(False)
root.state("iconic")
def frame_map(event=None):
root.update_idletasks()
root.overrideredirect(True)
root.state("normal")
root.call()
def minimefunction(event=None):
global size
if size:
root.geometry(f"{screen_width}x{screen_height-40}+0+0")
minimsi.config(text=" \u2752 ")
size = False
else:
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
minimsi.config(text=" \u25a0 ")
size = True
def quitApp():
root.destroy()
def close_blink(event=None):
close_button.config(bg="red")
def close_blink1(event=None):
close_button.config(bg="gray19")
def minimsi_blink(event=None):
minimsi.config(bg="gray29")
def minimsi_blink1(event=None):
minimsi.config(bg="gray19")
def minimsi1_blink(event=None):
minimsi1.config(bg="gray29")
def minimsi1_blink1(event=None):
minimsi1.config(bg="gray19")
root = Tk()
size = True
app_width = 600
app_height = 500
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
print(screen_width,screen_height)
x = (screen_width/2) - (app_width/2)
y = (screen_height/2) - (app_height/2)
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
root.overrideredirect(True)
frame = Frame(root,bg="gray29")
Label(frame,text="My App",font="Consolas 15",bg="gray29",fg="white").pack(side=LEFT,padx=10)
close_button = Button(frame,text=" X ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=quitApp)
close_button.pack(side=RIGHT)
minimsi = Button(frame,text=" \u25a0 ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minimefunction)
minimsi.pack(side=RIGHT)
minimsi1 = Button(frame,text=" - ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minieme1_)
minimsi1.pack(side=RIGHT)
frame.pack(fill=X)
yscroll = Scrollbar(orient=VERTICAL)
yscroll.pack(side=RIGHT,fill=Y)
editor = Text(font="Consolas 15",bg="gray19",fg="white",insertbackground="white",borderwidth=0,yscrollcommand=yscroll.set)
yscroll.config(command=editor.yview)
editor.pack(expand=True,fill=BOTH)
root.config(bg="gray19")
frame.bind("<Button-1>",move)
frame.bind("<B1-Motion>",move)
# minimsi1.bind("<Button-1>",minieme1_)
frame.bind("<Map>",frame_map)
close_button.bind("<Enter>",close_blink)
close_button.bind("<Leave>",close_blink1)
minimsi.bind("<Enter>",minimsi_blink)
minimsi.bind("<Leave>",minimsi_blink1)
minimsi1.bind("<Enter>",minimsi1_blink)
minimsi1.bind("<Leave>",minimsi1_blink1)
root.mainloop()
You can see the problem in this image:-
Disclaimer, this may not be the best approach to achieve OP`s goal, but I'm sticking to this example because:
There are several question with this example on StackOverflow
It shows a basic knowledge that is important for deeper digging.
The improvement of this code to the original is that the style applies again after you iconify it and bring that windows back up.
See the twitch of this example is how the taskbar keeps track of the application. There is a good article of Raymond Chen (Ms-Developer) where he quotes this:
“If you want to dynamically change a window’s style to one that
doesn’t support visible taskbar buttons, you must hide the window
first (by calling ShowWindow with SW_HIDE), change the window style,
and then show the window.”
And also points out a weak point of some programs and why they lose or get a blank taskbar icon:
Window is taskbar-eligible.
Window becomes visible ? taskbar button created.
Window goes taskbar-ineligible.
Window becomes hidden ? since the window is not taskbar-eligible at this point, the taskbar ignores it.
Anyway, the basic idea is the following:
Get the handle of the window. (Parent window of the root in this case for tkinter related reasons)
get the current style in a hexadecimal code
alter the hexadecimal code to your need with a bitwise operation
apply the altered style to the window
I added the following code at the beginning of your script:
from ctypes import windll
GWL_EXSTYLE=-20
WS_EX_APPWINDOW=0x00040000
WS_EX_TOOLWINDOW=0x00000080
def set_appwindow():
global hasstyle
if not hasstyle:
hwnd = windll.user32.GetParent(root.winfo_id())
style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_TOOLWINDOW
style = style | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
root.withdraw()
root.after(100, lambda:root.wm_deiconify())
hasstyle=True
This code at the end of your script:
hasstyle = False
root.update_idletasks()
root.withdraw()
set_appwindow()
and added the line set_appwindow() in def frame_map(event=None):. Als I had to implement another additional two lines in def minieme1_(event=None): to update the hasstylevariable.
So the overall implementation of this approach was possible to have your window ready and withdrawn for described reason. An additional variable to avoid a infinite loop while you alter the style of your window that is either True while it is shown or False while it is iconyfied, alongside with your overrideredirect method.
Full Code
from tkinter import*
from ctypes import windll
GWL_EXSTYLE=-20
WS_EX_APPWINDOW=0x00040000
WS_EX_TOOLWINDOW=0x00000080
def set_appwindow():
global hasstyle
if not hasstyle:
hwnd = windll.user32.GetParent(root.winfo_id())
style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_TOOLWINDOW
style = style | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
root.withdraw()
root.after(100, lambda:root.wm_deiconify())
hasstyle=True
def move(e):
xwin = root.winfo_x()
ywin = root.winfo_y()
startx = e.x_root
starty = e.y_root
ywin -= starty
xwin -= startx
def move_(e):
root.geometry(f"+{e.x_root + xwin}+{e.y_root + ywin}")
startx = e.x_root
starty = e.y_root
frame.bind("<B1-Motion>",move_)
def minieme1_(event=None):
global hasstyle
root.update_idletasks()
root.overrideredirect(False)
root.state("iconic")
hasstyle = False
def frame_map(event=None):
root.overrideredirect(True)
root.update_idletasks()
set_appwindow()
root.state("normal")
def minimefunction(event=None):
global size
if size:
root.geometry(f"{screen_width}x{screen_height-40}+0+0")
minimsi.config(text=" \u2752 ")
size = False
else:
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
minimsi.config(text=" \u25a0 ")
size = True
def quitApp():
root.destroy()
def close_blink(event=None):
close_button.config(bg="red")
def close_blink1(event=None):
close_button.config(bg="gray19")
def minimsi_blink(event=None):
minimsi.config(bg="gray29")
def minimsi_blink1(event=None):
minimsi.config(bg="gray19")
def minimsi1_blink(event=None):
minimsi1.config(bg="gray29")
def minimsi1_blink1(event=None):
minimsi1.config(bg="gray19")
root = Tk()
size = True
app_width = 600
app_height = 500
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
print(screen_width,screen_height)
x = (screen_width/2) - (app_width/2)
y = (screen_height/2) - (app_height/2)
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
root.overrideredirect(True)
frame = Frame(root,bg="gray29")
Label(frame,text="My App",font="Consolas 15",bg="gray29",fg="white").pack(side=LEFT,padx=10)
close_button = Button(frame,text=" X ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=quitApp)
close_button.pack(side=RIGHT)
minimsi = Button(frame,text=" \u25a0 ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minimefunction)
minimsi.pack(side=RIGHT)
minimsi1 = Button(frame,text=" - ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minieme1_)
minimsi1.pack(side=RIGHT)
frame.pack(fill=X)
yscroll = Scrollbar(orient=VERTICAL)
yscroll.pack(side=RIGHT,fill=Y)
editor = Text(font="Consolas 15",bg="gray19",fg="white",insertbackground="white",borderwidth=0,yscrollcommand=yscroll.set)
yscroll.config(command=editor.yview)
editor.pack(expand=True,fill=BOTH)
root.config(bg="gray19")
frame.bind("<Button-1>",move)
frame.bind("<B1-Motion>",move)
# minimsi1.bind("<Button-1>",minieme1_)
frame.bind("<Map>",frame_map)
close_button.bind("<Enter>",close_blink)
close_button.bind("<Leave>",close_blink1)
minimsi.bind("<Enter>",minimsi_blink)
minimsi.bind("<Leave>",minimsi_blink1)
minimsi1.bind("<Enter>",minimsi1_blink)
minimsi1.bind("<Leave>",minimsi1_blink1)
hasstyle = False
root.update_idletasks()
root.withdraw()
set_appwindow()
root.mainloop()
Tested with python 3.10 and windows 11
You can use a hidden root window to let the system window manager to show an icon in the taskbar, and make the custom window as a transient child window of the hidden root window in order to simulate those iconify and deiconify effect.
You need to bind <Button> event on the custom window so to bring it to the front when it is clicked. Also need to bind <FocusIn> event on the hidden root window to move the focus to the custom window instead.
Below is the required changes:
...
def minieme1_(event=None):
# iconify hidden root window will iconify the custom window as well
hidden_root.iconify()
...
def quitApp():
# destroy the hidden root window will destroy the custom window as well
hidden_root.destroy()
...
def on_focus(event):
# bring custom window to front
root.lift()
# create a hidden root window
hidden_root = Tk()
hidden_root.attributes('-alpha', 0)
hidden_root.title('My App')
# you can use hidden_root.iconbitmap(...) or hidden_root.iconphoto(...) to set the icon image
# use Toplevel instead of Tk for the custom window
root = Toplevel(hidden_root)
# make it a transient child window of hidden root window
root.transient(hidden_root)
root.bind('<Button>', on_focus)
hidden_root.bind('<FocusIn>', on_focus)
...
#frame.bind("<Map>",frame_map) # it is not necessary and frame_map() is removed as well
...
The short answer is: you can't do this purely with Tkinter. You can set the window's icon via self.iconbitmap(path_to_icon), but in order to have a unique taskbar icon, you'll need to compile your app into a Windows executable.
See here
Edit: Unrelated, but as a matter of practice it's best to avoid star imports, e.g. from tkinter import * - it's much better to use something like import tkinter as tk and then prefix your Tkinter objects with tk. to avoid namespace pollution!

my tkinter window doesn't open up when I add the sleep() part

I am trying to make a ball go to one side of the screen turn around, then come back. Whenever I try running this program the tkinter window doesn't show up, but when i get rid of the sleep(0.5) part it shows up when the ball is already off the screen. Can someone tell my what I did wrong?
from tkinter import *
from time import sleep
window = Tk()
cWidth = 800
cHeight = 500
c = Canvas(window, width = cWidth, height = cHeight, bg='black')
c.pack()
x = 400
y = 250
ball = c.create_polygon(x, y, x, y+25, x+25, y+25, x+25,y, fill='yellow')
Ball_move = 10
for i in range(200):
c.move(ball, Ball_move, 0)
window.update
x += Ball_move
if x == cWidth:
Ball_move = -Ball_move
sleep(0.5)
window.mainloop()
In windows, Tkinter frame shows up only after you call mainloop(). In your case, the for loop might be blocking it. Keep the for loop in a function and then call that function using threads so that it won't block the main loop.

Open Tkinter Window so it sits on start menu bar

I would like to have a Tkinter window open at the bottom right of the screen or where ever the start bar is. Much like when you click on the battery icon on your laptp and the box pops up. My code currently hides it behind the start menu bar. I would essentially like it at the bottom right but sitting on top of the start menu bar. Also, not sure how to account for things if the start menu is not at the bottom.
My Code:
from Tkinter import *
def bottom_right(w=300, h=200):
# get screen width and height
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# calculate position x, y
x = (screen_width - w)
y = (screen_height-h)
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
root = Tk()
bottom_right(500, 300)
root.mainloop()
You can use the win32api's .GetMonitorInfo() to find the 'working area' of the monitor, which is the area of the monitor without the taskbar. Then, seeing where the taskbar is, you can place the window in a corner of the working area. See this example:
import win32api
import Tkinter as tk
for monitor in win32api.EnumDisplayMonitors():
monitor_info = win32api.GetMonitorInfo(monitor[0])
if monitor_info['Flags'] == 1:
break
work_area = monitor_info['Work']
total_area = monitor_info['Monitor']
width = 300
height = 200
side = [i for i in range(4) if work_area[i]!=total_area[i]]
# Left
if side ==[0]:
x = str(work_area[0])
y = str(work_area[3]-height)
# Top
elif side == [1]:
x = str(work_area[2]-width)
y = str(work_area[1])
# Right
elif side == [2]:
x = str(work_area[2]-width)
y = str(work_area[3]-height)
# Bottom
elif side == [3]:
x = str(work_area[2]-width)
y = str(work_area[3]-height)
else:
x = str(work_area[2]-width)
y = str(work_area[3]-height)
geom = '{}x{}+{}+{}'.format(width, height, x, y)
root = tk.Tk()
root.configure(background='red')
root.geometry(geom)
root.overrideredirect(True)
root.mainloop()
Note that I've used overrideredirect to get rid of the frame of the window, since this messes with the placing a bit.
What you are calling the 'start menu bar' is usually called the taskbar, at least on Windows. root.iconify() minimizes the root window to the taskbar, wherever it happens to be, just as when one clicks [_] in the upper right of the window. Clicking on the icon de_iconifies it, just as with any other app.
root = tk.Tk()
root.iconify()
<build gui>
root.deiconify()
root.mainloop()
is a common pattern in a polished app when the part takes long enough to cause potentially visible construction activity. I believe putting the gui in a frame and packing the frame as the last step has the same effect (of hiding construction).

How to resize GraphWin window?

I'm using Zelle's graphics library to do some online coursework. Part of the assignment I'm working on seems to assume I can resize an existing GraphWin window. But this hasn't been touched on previously in the course, and looking over the documentation for graphics.py I don't see a way to accomplish this. I poked around a GraphWin object, and nothing seems to alter the window's size. Is it possible to resize a GraphWin window?
I've tried:
from graphics import *
new_win = GraphWin('Test', 300, 300)
new_win.setCoords(0, 0, 100, 200)
new_win.width = 100
The setCoords() method just creates a new virtual coordinate system within an existing window.
We might be able to achieve sufficent functionality for your purpose by dropping down to the tkinter level and specializing GraphWin:
from graphics import *
class ResizeableGraphWin(GraphWin):
""" A resizeable toplevel window for Zelle graphics. """
def __init__(self, title="Graphics Window", width=200, height=200, autoflush=True):
super().__init__(title, width, height, autoflush)
self.pack(fill="both", expand=True) # repack?
def resize(self, width=200, height=200):
self.master.geometry("{}x{}".format(width, height))
self.height = int(height)
self.width = int(width)
# test code
win = ResizeableGraphWin("My Circle", 100, 100)
win.setBackground('green')
c = Circle(Point(75, 75), 50)
c.draw(win) # should only see part of circle
win.getMouse() # pause for click in window
win.resize(200, 400) # should now see all of circle
win.getMouse() # pause for click in window
c.move(25, 125) # center circle in newly sized window
win.getMouse() # pause for click in window
c.setFill('red') # modify cirlce
win.getMouse() # pause for click in window
win.close()
A Python 3 implementation since I called super(). It can probably be retrofit for Python 2.
Zelle's graphics library does not have a method for resizing a window after it has been drawn.
I just found out how to do it
from graphics import *
win= GraphWin("Person",400,400)

Tkinter, Flashing Window after calling "wm_iconbitmap" and "resizable"

Everytime I create a window set and icon for the window manager and change some other property of the window (e.c. "resizable") it "flash" up. I suppose this is because the window manager is called again and it redos the window. Is there any way I can call maybe a "silent" change or something like that? I have not been very lucky in my research.
If you want to see for yourself, you are going to need an icon file. I uploaded the icon I am using here. If you are not comfortable with downloading that, you can also find suitable icon files in your system directory. Search "*.ico". Any 16x16 icon will do.
#! coding=utf-8
import tkinter as tk
import os
def CENTER_WINDOW(window,parent):
""" Verschiebt ein Fenster auf dem Bildschirm"""
# get window size
window.update_idletasks()
w = window.winfo_width()
h = window.winfo_height()
# get parent size
if parent=="screen":
p_x , p_y = window.winfo_screenwidth(), window.winfo_screenheight()
p_ox, p_oy = 0,0
else:
parent.update_idletasks()
p_x , p_y = parent.geometry().split("+")[0].split("x")
p_x , p_y = int(p_x) , int(p_y)
p_ox, p_oy = int(parent.geometry().split("+")[1]),int(parent.geometry().split("+")[2])
# calculate position x, y
x = p_x/2 + p_ox - w/2
y = p_y/2 + p_oy - h/2
window.geometry('%dx%d+%d+%d' % (w,h,x,y))
window.update_idletasks()
def top():
T = tk.Toplevel()
T.wm_iconbitmap( os.path.abspath("icon.ico"))
T.resizable(width=tk.FALSE, height=tk.FALSE)
CENTER_WINDOW(T,root)
root = tk.Tk()
root.geometry("100x50")
but = tk.Button(root,text="Toplevel",command=top)
CENTER_WINDOW(root,"screen")
but.pack()
root.mainloop()
In rough outline:
Call withdraw on the window immediately after creating it
Make your adjustments
Call deiconify on the window when you're ready to show it

Categories

Resources