I wanted to create a transparent box with a red outline between these two specified points at (614, 162) and (759, 306). However, it returns me an invisible or no box at all. But for (100, 50) and (160, 100), the box is visible.
import tkinter as tkr
app = tkr.Tk()
app.title("AI Cashier")
app.geometry("1366x768")
app.wm_attributes("-transparentcolor", "white")
app.config(bg = "White")
can = tkr.Canvas(app,bg = "White",highlightthickness = 0)
can.create_rectangle(100,50,160,100,outline = "red", width = 2)
can.pack()
app.mainloop()
Could anyone explain to me because I'm still new to tkinter. Or is it a bug?
Why does my create_rectangle disappears after specifying these points using tkinter?
It is because the canvas is only a couple hundred pixels wide and tall, so you are drawing outside the visible area of the canvas.
A simple fix for the code in the question is to make the canvas bigger. You can either give it an explicit width and height (eg: can = tkr.Canvas(..., width=800, height=400)), or force the canvas to fill the window (eg: can.pack(fill='both', expand=True')). In either of those cases, the image will be visible.
Related
I need to draw a few Polygons using tkinter and so I tried the following code:
from tkinter import *
master = Tk()
w = Canvas(master, width=200, height=200)
w.pack()
points = [0, 0, 200, 100, 0, 200]
w.create_polygon(points, outline="green",fill='yellow', width=0)
mainloop()
and I get the following output
But the problem is... the width isn't being set to 0. Is this is an internal issue with Tkinter or is there a way I can fix this?
You are specifying a color for an outline, so the canvas must draw an outline that is at least one pixel wide in order to show the outline. If you don't want an outline, set outline to None or the empty string.
So I'm creating the game of life in python, and would like to create a grid on top of a canvas using the tkinter class of python. Thanks in advance.
To make the grid I'm using the create_line() function of the canvas class and looping through using the range() function so that my lines are drawn to the width and the height of the canvas, using two separate for loops one for width and one for the height. The loops I have gotten are taken from the following piece of code on stackoverflow: How to create a grid on tkinter in python?
I thought I understood what is happening but what I thought should happen has not
from tkinter import *
from random import *
window = Tk()
window.title('Game Of Life')
canvas = Canvas(window, background='white', width=800, height=600)
def create_grid(canvas):
width = canvas.winfo_width() # gets width of the canvas
height = canvas.winfo_height() # gets height of the canvas
for line in range(0, width, 1): # range(start, stop, step)
canvas.create_line([(line, 0), (line, height)], fill='black', tags='grid_line_w')
for line in range(0, height, 1):
canvas.create_line([(0, line), (width, line)], fill='black', tags='grid_line_h')
create_grid(canvas)
canvas.grid(row=0, column=0)
window.mainloop()
My expected results are to have a white canvas with vertical and horizontal lines stretching the width and height of the canvas. But my actual results are just a white canvas with no lines on top.
I don't trust winfo_width and winfo_height to give accurate values in this context. According to https://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.winfo_height-method, the functions simply return 1 if you haven't packed/gridded/placed the widget yet, and even if you have, it might still return 1 if the window's event loop hasn't updated lately. You already know that you want the width to be 800 and the height to be 600, so you may as well define them as such directly.
Another problem is that your range calls have a step argument of 1. This means that the lines will be one pixel apart, which will effectively paint the entire canvas black. I recommend a larger step.
from tkinter import *
from random import *
window = Tk()
window.title('Game Of Life')
def create_grid(window):
width = 800
height = 600
canvas = Canvas(window, background='white', width=width, height=height)
for line in range(0, width, 10): # range(start, stop, step)
canvas.create_line([(line, 0), (line, height)], fill='black', tags='grid_line_w')
for line in range(0, height, 10):
canvas.create_line([(0, line), (width, line)], fill='black', tags='grid_line_h')
canvas.grid(row=0, column=0)
create_grid(window)
window.mainloop()
Result:
I am trying to place images next to each other side by side but the labels overlap and the label sticks out.
from tkinter import *
root = Tk()
root.geometry("1000x700")
root.resizable(0, 0)
##############################################
TFi = PhotoImage(file="images/Topframe.png")
TF = Label(root, image=TFi)
TF.place(x=-3, y=-3)
BFi = PhotoImage(file="images/Botframe.png")
BF = Label(root, image=BFi)
BF.place(x=-3, y=650)
LF1i = PhotoImage(file="images/LeftFrame1.png")
LF1 = Label(root, image=LF1i)
LF1.place(x=-3, y=50)
##############################################
root.mainloop()
Is it possible to place an image in Tkinter without a Label or canvas
Your most common choices for labels are a canvas or a label. You can also put images on buttons, and embed them in a text widget.
The best choice for creating labels that are next to each other are to use pack or grid. pack is good if you're making a single horizontal or vertical grouping, but grid is better if you're making both rows and columns of widgets.
You can use place, but that requires that you do all of the math to compute the location of each image, and usually results in a user interface that isn't very resilient to changes in screen resolution. It also sometimes ends up causing you to have to do changes to every widget even if you only want to tweak the layout slightly.
My guess for why they overlap is that you aren't aware that the coordinates you give place by default specify the center of the image rather than the upper-left corner. You can specify which part of the image is at the given coordinate with the anchor option.
the label sticks out.
I'm not exactly sure what you mean by that, but if you mean that it has a 3D appearance, you can control that by giving it a borderwidth of zero and/or a relief of "flat".
I have a form (in .png format) with blank text boxes (usually filled out by hand). I would like to populate the boxes with text.
To do this, I am using tkinter to display the form on the screen, I then use the mouse (with finer positioning using arrow keys) to get the pixel coordinates of a box, then use PIL to write text to that box. A working example is below.
My primary issue is I am struggling to align pixel coordinates in the tkinter canvas and pixel coordinates in the PIL image.
Some additional background. The image is in high resolution and is circa 4961 by 7016 pixels. My screen resolution is 1920 x 1080. I had problems writing text where I needed it to write like this and found greater success if I scaled the image to fit entirely within my screen. I can only assume this is because I was / am confusing screen pixels with picture pixels (and resolved this when I fit the image to the screen to align these two - but understanding the differences here and how to do this task without scaling would be most helpful also).
But I am also having trouble reconciling the pixel coordinates on the tkinter canvas with the PIL picture. For example, the code below is designed to write (x, y) pixel coordinates and then its page relativity {x% across the page, y% up the page} to the box (the reason for this is because it is an input into another process). An example is: (346, 481) >> {49.856, 51.018}
But if (using a scaling factor of 0.14) I click very low to the bottom of the image, I get (209, 986) >> {30.115, -0.407}. The relativities should be bounded between 0 and 100% so should not be negative and I cannot see this on my PIL produced .png file.
If I use a scaling factor of 0.125, I can write text to the tkinter canvas box fine, but the text appears quite a bit lower (ie outside the box) on the PIL .png file which is saved to the drives. So something is clearly not working between these two systems.
How can I reconcile PIL and tkinter pixel coordinates?
As an aside, I have a four separate functions to handle finer key adjustments. Ideally these would be one function, but I could not get the arrow buttons ( etc) to work inside an if elif block (eg, I tried this and some more derivatives of left, right etc)
def mouseMovement(event):
moveSpeed = 1
try:
int(event.char)
moveSpeed = max(1, int(event.char)*5)
return True
except ValueError:
return False
x, y = pyautogui.position()
if event.char == '<Left>':
pyautogui.moveTo(x-moveSpeed, y)
elif event.char == '<Right>':
pyautogui.moveTo(x+moveSpeed, y)
root.bind('<Key>' , mouseMovement)
Any help greatly appreciated!
Almost working example below:
from tkinter import *
from PIL import Image, ImageDraw, ImageFont, ImageTk
import pyautogui
# Form
formName = '2013+ MCS4'
# PIL image'
formImage = Image.open(formName+'.png')
wForm, hForm = formImage.size
scale = 0.14
formImage = formImage.resize((int(scale*wForm), int(scale*hForm)), Image.ANTIALIAS)
draw = ImageDraw.Draw(formImage)
font = ImageFont.truetype('arial.ttf', 10)
textColor = (255, 40, 40)
# tkinter canvas
def colorConversion(RGB):
def hexadecimalScale(RGB):
hexadecimalSystem = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F')
return str(hexadecimalSystem[RGB//16]) + str(hexadecimalSystem[RGB%16])
return '#' + hexadecimalScale(RGB[0]) + hexadecimalScale(RGB[1]) + hexadecimalScale(RGB[2])
fontCanvas = 'arial 7'
textColorCanvas = colorConversion(textColor)
# generate canvas
if __name__ == '__main__':
root = Tk()
# set up tkinter canvas with scrollbars
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, width=int(scale*wForm), height=int(scale*hForm), bd=0, xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
# add image
#img = PhotoImage(file=formName+'.png')
img = ImageTk.PhotoImage(formImage)
canvas.create_image(0,0,image=img,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
wForm = img.width()
hForm = img.height()
# finer mouse movements
moveSpeed = 1
def setMoveSpeed(event):
global moveSpeed
try:
int(event.char)
moveSpeed = max(1, int(event.char)*5)
return moveSpeed
except ValueError:
return False
def moveMouseLeft(event):
x, y = pyautogui.position()
pyautogui.moveTo(x-moveSpeed, y)
def moveMouseRight(event):
x, y = pyautogui.position()
pyautogui.moveTo(x+moveSpeed, y)
def moveMouseUp(event):
x, y = pyautogui.position()
pyautogui.moveTo(x, y-moveSpeed)
def moveMouseDown(event):
x, y = pyautogui.position()
pyautogui.moveTo(x, y+moveSpeed)
root.bind('<Key>' , setMoveSpeed)
root.bind('<Left>' , moveMouseLeft)
root.bind('<Right>', moveMouseRight)
root.bind('<Up>' , moveMouseUp)
root.bind('<Down>' , moveMouseDown)
# print coordinates
def printCoordinates(event):
x = event.x # minor adjustments to correct for differences in tkinter vs PIL methods (investigate further)
y = event.y # minor adjustments to correct for differences in tkinter vs PIL methods (investigate further)
canvas.create_text(x, y-5, fill= textColorCanvas, font= fontCanvas, anchor= 'sw',
text= '{'+str(round(x/wForm*100,3))+', '+str(round((1-y/hForm)*100,3))+'}' )
draw.text( (x, y-5), '{'+str(round(x/wForm*100,3))+', '+str(round((1-y/hForm)*100,3))+'}' , fill=textColor, font=font)
print('('+str(x)+', '+str(y)+') >> {'+str(round(x/wForm*100,3))+', '+str(round((1-y/hForm)*100,3))+'}')
root.bind('<Return>', printCoordinates)
root.mainloop()
formImage.save('coordinates - '+formName+'.png')
I cannot run your code, so this is just an educated guess.
Since the canvas doesn't typically have focus, and the binding is on the root window, the values for event.x and event.y are possibly relative to the window as a whole rather than the canvas.
This should be easy to determine. You can print out the coordinates in the binding, and then click in the upper left corner of the canvas, as near to 0,0 as possible. The printed coordinates should also be very near to 0,0 if the coordinates are relative to the canvas. If they are off, they might be off by the distance of the top-left corner of the canvas to the top-left corner of the window.
It's due to Pillow setting the text coordinates on the left-hand corner(i.e. the coordinates selected become the top left-hand corner of whatever textbox pillow adds). But in newer versions of Pillow(I think 8.1), the text has an anchor option. Simply in the parameters for creating text add anchor="mm". The Pillow docs have more info on this(ps. you'll also have to slightly increase the font in some cases due to Pillow font sizes being slightly smaller. I found adding 4 gets pretty close)
EDIT: make sure the cords you're using are also Canvas cords
I'm trying to change the width of a Message widget in Tkinter by using the width parameter. I couldn't get it to work so I tried align, justify and aspect which all produced the same result - the box remains centred and the width of the text.
Here is my code:
console_Fetch = Message(text="test\ntest\ntest\ntest\ntest",bd=1,relief="sunken",width=300)
console_Fetch.grid(row=7,column=0,padx=5,pady=1,columnspan=2)
I'm obviously using .grid() to pack it into the window.
Here's a screenshot of my window:
Pragmatic answer
Often setting width in widgets is not working as expected, depending on priority of other aspects, cell width, you name it. It gets easily overruled, or depends on other conditions.
What I always do is give the grid a "skeleton" of canvases in adjecent cells, with height (or width) of zero. Subsequently stretch the widget with sticky inside their cells. Just look at the example below:
from tkinter import *
win = Tk()
console_Fetch = Message(text="test\ntest\ntest\ntest\ntest",bd=1,relief="sunken",width=3000)
canvas = Canvas(width = 500, height = 0)
canvas.grid(row=0,column=0)
console_Fetch.grid(row=1,column=0,padx=5,pady=1,columnspan=2, sticky = N+W+E+S)
win.mainloop()
The same I did in shaping the grid in the minesweeper- matrix in this answer.