How can I draw a point with Canvas in Tkinter? - python

I want to draw a point in Tkinter,Now I'm using Canvas to make it,but I didn't find such method to draw a point in Canvas class.Canvas provides a method called crete_line(x1,y1,x2,y2),so i tried to set x1=x2,y1=y2 to draw a point, but it doesn't work.
So anyone can tell me how to make it,it will be better if use Canvas can make it,other solution will be also accepted.Thanks!

There is no method to directly put a point on Canvas. The method below shows points using create_oval method.
Try this:
from Tkinter import *
canvas_width = 500
canvas_height = 150
def paint(event):
python_green = "#476042"
x1, y1 = (event.x - 1), (event.y - 1)
x2, y2 = (event.x + 1), (event.y + 1)
w.create_oval(x1, y1, x2, y2, fill=python_green)
master = Tk()
master.title("Points")
w = Canvas(master,
width=canvas_width,
height=canvas_height)
w.pack(expand=YES, fill=BOTH)
w.bind("<B1-Motion>", paint)
message = Label(master, text="Press and Drag the mouse to draw")
message.pack(side=BOTTOM)
mainloop()

The provided above solution doesn't seem to work for me when I'm trying to put a sequence of a few pixels in a row.
I've found another solution -- reducing the border width of the oval to 0:
canvas.create_oval(x, y, x, y, width = 0, fill = 'white')

With create_line you have other possible workaround solution:
canvas.create_line(x, y, x+1, y, fill="#ff0000")
It overwrites only a single pixel (x,y to red)

Since an update of the Tk lib (somewhere between Tk 8.6.0 and 8.6.9) the behavior of create_line had changed.
To create a one pixel dot at (x, y) in 8.6.0 you add to write
canvas.create_line(x, y, x+1, y, fill=color)
Now on 8.6.9 you have to use:
canvas.create_line(x, y, x, y, fill=color)
Note that Debian 9 use 8.6.0 and Archlinux (in early 2019) use 8.6.9 so portability is compromised for a few years.

Related

Any way to scale up points (x, y) in Python tkinter gui?

I am working on the TSP problem and reading some points from a file (have to use the points I'm given) and want to plot the points on to a GUI. But the problem is that the points are all only 2 digit and when I plot them using the tkinter.Canvas(), they all look really smudged up and tiny, almost like they're overlapping on top of each other.
Like this:
I got the problem working but just want to use this GUI to show it working instead of outputting it all to the console but can't cause it just looks stupid even worse when the lines are being drawn. So is there some way I can scale up that canvas or modify the points some way to make them look better. Can I do really anything or am I just stuck throwing it all to the console?
Given nodes values centered at (0, 0) in any coordinate system, you need to manipulate the given coordinates to comply to what your screen and canvas needs to properly render the drawing
You can rescale your points by a scalar factor, then translate the origin to the center of the screen (or at any location for that matter), in order to visualize them more easily:
Maybe like this:
import tkinter as tk
WIDTH = 600
HEIGHT = 400
given_nodes = ((-1, -1), (-1, 1), (1, 1), (1, -1), (0, -1.5))
scale = 100
scaled_nodes = [(x * scale, y * scale) for x, y in given_nodes]
translated_to_center_nodes = [(x + WIDTH/2, y + HEIGHT/2) for x, y in scaled_nodes]
app = tk.Tk()
canvas = tk.Canvas(app, width=WIDTH, height=HEIGHT, bg='cyan')
canvas.pack()
# Draw connecting lines
line_nodes = translated_to_center_nodes + [translated_to_center_nodes[0]]
for idx, node in enumerate(line_nodes[:-1]):
x0, y0 = node
x1, y1 = line_nodes[idx+1]
canvas.create_line(x0, y0, x1, y1, fill='black')
# draw nodes
for node in translated_to_center_nodes:
x, y = node
dx, dy = 2, 2
canvas.create_oval(x-dx, y+dy, x+dx, y-dy, fill='white')
# draw origin & coordinate system at rescaled drawing scale
canvas.create_line(0, 0, 0 + scale, 0, width=9, fill='blue', arrow=tk.LAST)
canvas.create_line(0, 0, 0, scale, width=9, fill='blue', arrow=tk.LAST)
canvas.create_text(40, 40, text='SCALED\nCANVAS\nORIGIN')
# draw moved origin & coordinate system at rescaled drawing scale
canvas.create_line(0, HEIGHT/2, WIDTH, HEIGHT/2, fill='black', dash=(1, 3))
canvas.create_line(WIDTH/2, HEIGHT/2, WIDTH/2 + scale, HEIGHT/2, width=3, fill='black', arrow=tk.LAST)
canvas.create_line(WIDTH/2, 0, WIDTH/2, HEIGHT, fill='black', dash=(1, 3))
canvas.create_line(WIDTH/2, HEIGHT/2, WIDTH/2, HEIGHT/2 + scale, width=3, fill='black', arrow=tk.LAST)
canvas.create_text(WIDTH/2, HEIGHT/2, text='MOVED\nORIGIN')
if __name__ == '__main__':
app.mainloop()
This is commonly done with matrix multiplication and homogeneous coordinates (look it up), but the machinery needed to demonstrate a simple example is a little too heavy.
The process of using the coordinates of an object and drawing it at scale, at the proper place, maybe rotated of skewed is called instantiation (look it up too!)

Pillow, centering of text not working, how is this accomplished?

I have tested the calculations and the math is correct (and takes into account the height and width of the font), but after Python creates the image and I put it into Photoshop, the vertical and horizontal centering of the text is not correct. Should I be doing something else with my code?
from PIL import Image, ImageDraw, ImageFont
# base = Image.open("Images/Phones/KK17018_Navy_KH10089.jpg").convert("RGBA")
base = Image.open('Images/Tablets/KK17076_Hunter_KH235.jpg').convert('RGB')
# Create blank rectangle to write on
draw = ImageDraw.Draw(base)
message = 'GGS'
num1, num2 = base.size
bounding_box = [0, 0, num1, num2]
x1, y1, x2, y2 = bounding_box # For easy reading
# font = ImageFont.truetype('Fonts/Circle/Circle Monograms Three White.ttf', size=413)
font = ImageFont.truetype('Fonts/Modern/Vhiena_Monoline.otf', size=800)
font2 = ImageFont.truetype('Fonts/Modern/Vhiena_Base.otf', size=800)
font3 = ImageFont.truetype('Fonts/Modern/Vhiena_Extrude A.otf', size=800)
# Calculate the width and height of the text to be drawn, given font size
w, h = draw.textsize(message, font=font3)
# Calculate the mid points and offset by the upper left corner of the bounding box
x = (x2 - x1 - w) / 2 + x1
y = (y2 - y1 - h) / 2 + y1
# Write the text to the image, where (x,y) is the top left corner of the text
draw.text((x, y), message, align='center', font=font3, fill='orange')
draw.text((x, y), message, align='center', font=font2, fill='black')
draw.text((x, y), message, align='center', font=font, fill='white')
# Draw the bounding box to show that this works
# draw.rectangle([x1, y1, x2, y2])
base.show()
base.save('test_image.jpg')
The text's upper left x,y coordinates should be (874,1399.5), but in Photoshop, they show as (875,1586). The Python code above does calculate (874,1399.5) correctly, but something is placing the font lower than it should be.
Also, I'm stacking fonts like this because it gives a regular font, a shadow font and a font that makes it look beveled in the middle of the font. Would there be a better method or is stacking fonts an ok practice?
EDIT: Upon further testing, something is adding a 22% top margin to the font as the font size increases. I could account for this, but this seems rather odd.
I don't have your exact font handy to test, but this is probably because Pillow's textsize and text methods, by default, anchor to the ascender height instead of the actual rendered top; see this note in the docs. Try using textbbox instead of textsize, and specifying the top anchor in both that and the text method, and see if that behaves more intuitively.
Note that you could probably just anchor the text to 'mm', middle/middle, to center it based on the coordinates of the image's midpoint. This anchors vertically to halfway between the ascenders and descenders, though, so it may not actually look centered, depending on the font and what glyphs you render.

Tkinter Canvas create_window function does not show the desired widgets

I've tried to make a customized container function for my application, but when I pass the list of widgets, they does not get visible. The container is like the LabelFrame but with rounded corners. I tried a lot of things, but nothing seems to work. Please let me know what I'm doing wrong.
Here is my code and some comments for it:
parent -> this is the top level window
lbl_text -> this will be the little text like the LabelFrame has on top
widgets -> this is the list of the widgets I would like to place in my rounded frame
def rndframe(parent, lbl_text, widgets
# content_positioner and max_width is for calculate the requiered size of the canvas to be big
# enough for all the passed widgets, and to determine the coordinates for create_window function.
# It works perfectly and I get all the information I want.
content_positioner = [14]
max_width = 1
for i, item in enumerate(widgets):
content_positioner.append(content_positioner[i]+widgets[i].winfo_reqheight())
if widgets[i].winfo_reqwidth() > max_width:
max_width = widgets[i].winfo_reqwidth()
height = max(content_positioner)+10
width = max_width+10
# Here I create the canvas which will contain everything
canv = TK.Canvas(parent, height=height, width=width, bg='green', bd=0, highlightthickness=0)
# Here I draw the rounded frame
radius = 10
x1 = 2
y1 = 5
x2 = width-2
y2 = height-2
points = [x1+radius, y1, x2-radius, y1, x2, y1, x2, y1+radius, x2, y2-radius, x2, y2,
x2-radius, y2, x1+radius, y2, x1, y2, x1, y2-radius, x1, y1+radius, x1, y1]
canv.create_polygon(points, smooth=True, outline=DS.menu_bg, width=3, fill='')
# Here I put the litle label in the frmae
label = TK.Label(parent, text=lbl_text, bg=DS.main_bg, padx=3, pady=0, fg=DS.main_textcolor)
canv.create_window(18, 5, anchor='w', window=label)
# And thats the part where I would like to place all the passed widgets based on the coordinate
# list I preveously created but nothing appear just the frame polygon and the liitle label on it.
for w, widget in enumerate(widgets):
canv.create_window(width/2, content_positioner[w], anchor='n', window=widgets[w])
return canv
And finally the way I've tried to use:
id_num = TK.Label(result_area_leaf, text='123-4567')
id_num2 = TK.Label(result_area_leaf, text='123-4567')
id_holder = rndframe(result_area_leaf, lbl_text='id', widgets=[id_num, id_num2])
id_holder.grid()
There are many problems with your code, but the root of the issue is that the widgets you're adding to the canvas have a lower stacking order than the canvas so they are behind or "under" the canvas. This is because the default stacking order is initially determined by the order that the widgets are created. You create the widgets before the canvas so they are lower in the stacking order.
The simplest solution to the stacking order problem is to raise the widgets above the canvas, which you can do with the lift method.
for w, widget in enumerate(widgets):
canv.create_window(width/2, content_positioner[w], anchor='n', window=widgets[w])
widgets[w].lift()
When I make the above modification, along with fixing all of the other problems in the code, the labels appear in the canvas.
Finally I got it work. First of all sorry that I didn't posted a standalone runable code firstly. When I put the code in a file and make the necessary changes about the missing variables the suggested .lift() method worked. And the reason was that in that single file I used the root window as parent.
In my copied code the two Lables (id_num and id_num2) have a Canvas for parent (result_area_leaf) and that was the problem. If I made my root window as parent for the passed widgets, the widgets are shown. The .lift() actually does not needed after all.
Thanks guys ;)

Change the width of a rectangle in tkinter's canvas widget

I've tried several ways to change the width of the blue rectangle in this example code. Nothing seems to work. In the code, "a" represents a float variable between 1.00, and 0.00. That value is used to calculate "b," which is the desired width of the blue rectangle in pixels. I have some fairly complicated code that generates that value, and at least that works. In order for the code to work, the width of the blue rectangle must rely on "b." I've tried "Canvas.itemconfig()," and it didn't work.
import tkinter
from tkinter import *
root = Tk()
root.maxsize(320,240) # Sets max size of window
root.minsize(320,240)
canvas_height = 23
canvas_width = 315
w = Canvas(root, width=canvas_width, height=canvas_height)
w.pack()
w.create_rectangle(5, canvas_height, canvas_width, 2, fill="yellow")
w.create_rectangle(5, canvas_height, canvas_width, 2, fill="blue")
a = 1.0 # More complicated code creates this float between 0.00 and 1.00. It is a percentage of the desired 'blue rectangle' width
b = int(a * canvas_width)
root.mainloop()
If anyone could help, I would greatly appreciate it!
P.s. I'm new to the Stackoverflow community, so please let me know if there's anything I can do to make my questions easier to answer.
The rectangle is defined by a couple of coordinates for opposite corners. Get the coordinates of the left edge, add the width to the x coordinate, and use that to set the coordinates of the right edge.
First, keep track of the object id so you can change it later:
blue = w.create_rectangle(5, canvas_height, canvas_width, 2, fill="blue")
To resize, get the coordinates...
x0, y0, x1, y1 = w.coords(blue)
Do some math...
x1 = x0 + b
And reset the coordinates
w.coords(blue, x0, y0, x1, y1)

Python circle - moves and detects obstacles (moves alone, not with arrows)

I want to make a circle move and avoid obstacles with collision detection. Here's the code I have.
from Tkinter import *
window = Tk()
canvas = Canvas(window,width=800, height=500, bg='pink')
canvas.pack()
finishline = canvas.create_oval(700, 300, 700+50, 300+50,fill='green')
robot = canvas.create_oval(20,200,20+45,200+45,fill='yellow')#(x1, y1, x2, y2)
ob1 = canvas.create_rectangle(200,400,200+50,200+1,fill='black')
canvas.update()
ob1 = canvas.create_rectangle(500,200,150+400,300+100,fill='blue')
canvas.update()
You could always use the canvas.find_overlapping method (found here: http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.find_overlapping-method), and if it ever returns a value other than your own little circle, you can make the circle go in the other direction or something.
It's kind of hard to give a specific answer with such unspecific requirements.

Categories

Resources