tkinter not drawing shapes with negative points - python

This maybe a very stupid question but I am having some troubles with tkinter. I am very new to using tkinter and wanted to learn something during my free time which I don't have much of. I have set up the canvas and everything but in a pickle when I try to dare a rectangle with negative points. I don't kow why, it just doesn't draw anything.
What I am trying to do is to draw a rectangle underneath one another.
I have attached the code below. It draws the rectangle which is filled with green, but when I try to draw the rectangle filled with red, nothing appears.
I really appreciate the help.
from tkinter import *
mainWindow = Tk()
drawingCanvas = Canvas(mainWindow, width=300, height=300)
drawingCanvas.pack()
drawingCanvas.create_rectangle(50, 25, 150, 75, fill="green")
drawingCanvas.create_rectangle(50, -200, 150, -100, fill="red")
mainloop()
I tried to plot the points using using a online plotter demos to check if the points are correct. It is, but whne I draw it in tkinter, the rectangle with negative points is missing/doesn't get drawn.

The top-left corner of the canvas defaults to 0,0. Since your objects are being drawn above that coordinate they aren't visible.
You can adjust the scrollregion after you've created the objects, and the canvas will be adjusted so that the top-left-most object is visible. You can do this with the following statement, which should come after you've created the items on the canvas:
drawingCanvas.configure(scrollregion=drawingCanvas.bbox("all"))
drawingCanvas.bbox("all") returns the bounding box of all of the items on the canvas. This is then passed into the configure method to change the scrollregion attribute of the canvas.
This works in this specific case since everything you draw will fit on the window. If you draw items far to the right or far below they won't be visible. This is a convenient way to adjust the viewable portion of the canvas which, as a side effect, adjusts what is displayed in the upper-left corner.

The rectangle with negative points is not missing but is drown outside of the screen. A tkinter Canvas starts at 0/0 on the top left of the screen so negative numbers will be drawn outside the window.
Change:
drawingCanvas.create_rectangle(50, -200, 150, -100, fill="red")
To:
drawingCanvas.create_rectangle(50, 200, 150, 100, fill="red")

Related

How to make tkinter postpone canvas updates?

Using my testing, I found that after every shape you draw on the canvas, it recalculates and draws the pixels.
c = Canvas(root)
c.pack()
c.create_line(10, 10, 50, 50)
# Draws line on canvas immediately.
c.create_line(20, 20, 60, 60)
# Draws second line on canvas.
This seems highly inefficient as I'm trying to create dozens of shapes per frame (as in each simulation update). It would probably be better to wait until I've queued up all my draw commands, then tell the canvas to redraw the frame all in one go.
So far I tried creating a canvas using the interactive shell without calling mainloop or even update on the root, but after each draw function it redrew the canvas!
Something like this would be ideal:
c = Canvas(root)
c.pack()
c.queue_create_line(10, 10, 50, 50)
# Don't draw line yet.
c.queue_create_line(20, 20, 60, 60)
# Also don't draw yet.
c.draw_queue()
# Finally draw both lines at once.
Please let me know if there is a way to fix this problem.
Using my testing, I found that after every shape you draw on the canvas, it recalculates and draws the pixels.
The behavior you see is only because you are running from a python shell. In normal operation, the canvas will not update the display immediately. The only time the display is updated is when you call update() or your function exits and control is given back to the event loop. Because you are running from the pythons shell, the display is updated after processing each command.

Tkinter create canvas that overlays screen with transparent background

I am creating a canvas that overlays everything else on the screen using a widget without a window that is lifted with the topmost attribute.
However, I would like a transparent background.
Here is what I've got:
import Tkinter
vsize = vw, vh = 600, 350
w = Tkinter.Canvas(width=vw, height=vh, highlightthickness=0)
w.master.overrideredirect(True)
w.master.geometry("+0+0")
w.master.lift()
w.master.wm_attributes("-topmost", True)
w.master.wm_attributes("-disabled", True)
w.create_rectangle(0, 0, vw, vh, fill="black")
w.pack()
w.mainloop()
I tried adding the following attribute to the canvas and adding stipple="gray25" to the rectangle:
w.master.wm_attributes("-transparentcolor", "gray")
This for some reason didn't make the gray transparent, and even if this did work it would look awful using stipple.
Does anyone have any ideas on how to achieve this?
EDIT:
I tried configuring the background as black, and giving it some transparency, but I would only like the background to be black so I can have non-transparent items inside of the canvas.
w.configure(background="black")
w.master.wm_attributes("-alpha", 0.5)
There is currently no way to achieve this in Tkinter I believe.
If you are looking for a way to overlay things on your screen, why not use individual windows placed on the screen? Else, I would suggest looking at other solutions outside of Tkinter.

python turtle canvas scrolling

I am using python turtle library to draw a big diagram. Whenever it's drawn the displayed region is the center (i.e. the scrollbar's are in the middle position). I'd like to scroll to the top-left region. Is there any way to do that?
Found solution:
ts = getscreen().getcanvas()
ts.xview_moveto(0)
ts.yview_moveto(0)
You can also make the screen bigger and you can manually scroll it.
screen = turtle.Screen()
screen.screensize(x ,y)
Just make the x bigger and you can scroll manually (by default the x is 400 and the y is 300)

Pygame Surface Mechanics

I'm currently writing up some GUI code for a small project I'm working on and I've come to the point where I need to implement scroll bars and their associated containers. For ease of execution, I would love to be able to draw all elements within the "scroll box" (the window that the scroll bar will affect) to a separate surface from my main display surface. The separate surface would then be cropped as need be and then drawn to the display surface in the render loop. I'm having trouble getting this to work, however.
In the draw() method of my ScrollBox class, I have the following code.
def draw(self):
self.subSurface.blit(self.image, (x, y))
#subSurface is, naturally, a Surface, and image is a pygame.Image; x and y are whatever
self.displaySurface.blit(self.subSurface, (x,y))
As with all drawable GUI elements in my code, draw() is called every pass through the main render loop. What the above code gives me is the default filled-in black Rect and self.image is not displayed in any capacity. I tried replacing the first line with
pygame.draw.rect(self.subSurface, color, rect)
but it yielded the same results. From my reading up on other Pygame GUI libraries, it seems what I want to do is possible but I don't think I'm executing it properly. How do I attach other sources/surfaces to subSurface and then have subSurface be drawn (with the sources attached) by displaySurface?
Any help would be much appreciated. Thanks.
For people visiting this question in the future:
Remember that the dest argument for Surface.blit() is relative to the upper-left corner of the destination surface. So if you're assembling an image on a subsurface, remember to use coordinates relative to the top-left corner of the object you're assembling, rather than absolute display coordinates.
So to assemble a scrollbar and draw it somewhere:
class ScrollBar:
# ... code ...
def render(self, display, x, y):
self.subSurface.blit(self.handle_image, (0, self.handle_pos))
self.subSurface.blit(self.upbtn_image, (0, 0))
self.subSurface.blit(self.dnbtn_image, (0, self.height - self.btn_height))
# ... other rendering operations
display.blit(self.subSurface, (x, y))
Adjust all numbers and variable names to taste, but you get the idea. Notice that all the scrollbar elements are positioned in "scrollbar-local" coordinates, with only the final blit to the display surface positioned in screen/application coordinates.

Repainting cairo windows?

I have ran into a problem that i suspect has to do painting/drawing elements in cairo.
I have a borderless window in pygtk, but i draw two rectangles with cairo.a black rectangle, and a grey rectangle inside. When the window is resized, there seems to be parts of the inner rectangle doesn't get drawn/painted. I have included 3 screenshots to show this issue.
As you can see in the second and third picture, some pieces of the window do not get painted grey. One way to fix this, is to call pygtk's window's present() method..but this makes my program extrmely slow, as the height of the window changes with pretty much each keystroke. So i was wondering what alternatives i have to fix this.
below is the relevant cairo code i use
def expose(self, widget, e):
cr = widget.window.cairo_create()
# Draw the background
cr.set_operator(cairo.OPERATOR_SOURCE)
# Create black rectangle with 60% opacity (serves as border)
(width, height) = widget.get_size()
cr.set_source_rgba(0, 0, 0, 0.6)
cr.rectangle(0, 0, width, height)
cr.fill()
# Inside the black rectangle, put a lighter one (will hold widgets)
(width, height) = widget.get_size()
cr.set_source_rgb(205/255, 205/255, 193/255)
cr.rectangle(10, 10, width-20, height-20)
cr.fill()
return False
def screen_changed(self, widget, old_screen = None):
screen = widget.get_screen()
colormap = screen.get_rgba_colormap()
widget.set_colormap(colormap)
It's basically a GTK+ bug, I believe. When a window is resized, GTK+ doesn't always queue the entire window for repainting. As a workaround, you can call window.queue_draw() at the place where you cause the window to be resized.
Try using the following right after you create your cairo widget:
cr.set_source_rgb(0,0,0)
cr.paint()
This will ensure you are always having a clean canvas.

Categories

Resources