I think transparent is the right word I'm looking for. Hopefully my description will explain it properly no matter what.
I have a graph of time based data going back decades. Pretty much simply setup to show one day per x-pixel, unless zoomed in closer. I would like to have a box, maybe along the lines of 5x100, appear on top of the graph so when I move the mouse over the graph the box will move and keep pace with the mouse. Anotherwords showing what was happening in the 5 furthest days 'x number of days prior'. Anotherwords when computing an average going forward what are the next values to be falling off as new data arrives. Naturally I want the underlying graphed data to be displayed with the transparent box on top of it outlining the days in question. This may get crazy enough to be a much wider box with two areas that are colored light grey or something like that to show the areas in question but the colored areas are separated by numerous days(could be multiple transparent windows that are tracked together as well. Is this feasible with tkinter? From the research I've been doing it's questionable if using
root.attributes('alpha', .30)
would work or not. It doesn't sound like I could do something like as it would end up making the graph transparent to whatever is underneath it.
self.Graph.create_line()
self.Box.attributes('alpha', .30)
If I understand correctly I have to use
attributes
right at the root level versus the individual 'window' level so the above (severely chopped down) code wouldn't work...or would it. I haven't had a chance to try anything out yet to see what happens...that will be later on this evening. Kinda hoping to save myself a little time by asking now and you never know who else may need the help sometime.
If I understand what you're trying to do, it's going to be pretty hard.
Setting window attributes works just fine to make a window transparent. You've got a minor problem in the code—attributes start with a hyphen—but otherwise you've got it right:
self.Box.attributes("-alpha", .30)
However, it sounds like you want that Box to be an embedded part of the graph window, not its own top-level window that can be dragged around by the user, etc. Unfortunately, Tkinter does not have any notion of child windows, and it doesn't expose nearly enough of the native stuff you'd need to fake them by creating an immobile window and manually moving it to track the movements of another window. So, you don't have a window, which means you don't have window transparency.
The obvious thing for Box to be is some kind of widget, like a Frame or Canvas. But widgets don't have transparency.
Box could instead be just a collection of elements drawn onto the same Canvas as the Graph. That seems promising… but none of the Canvas methods handle alpha transparency. (Some of them do handle all-or-nothing transparency, but that doesn't help.)
One thing that does handle transparency is PhotoImage. So, if you draw Box off-screen, get the resulting contents as a PhotoImage, add the alpha (e.g., via PIL), then create_image the result… close, but no cigar. Depending on the settings of the underlying Tk library, Tkinter may just draw the pixmap with 1-bit transparency or ignore transparency completely. (Experiment with loading alpha-transparent PNG files in PIL and drawing them on a Canvas.) So, unless you want an app that looks right on some systems, doesn't draw the Box at all on others, and draws it opaque on others, this is a dead end.
So, what's left? Only manual compositing: Draw the Graph and the Box on separate off-screen windows, get the pixmaps, use PIL to compose them, and create_image the result.
At which point you're probably better off just using something like PIL's ImageDraw or a more powerful library to construct the pixmap in the first place. Or, of course, using a more powerful GUI library than Tk, like Qt or wx.
Maybe this can give you some ideas to play with:
from tkinter import *
root = Tk()
c = Canvas(root, width=640, height=480, bd=0, highlightthickness=0)
c.create_line(0,240,640,240, fill='blue')
c.pack()
#pil image with transparency
try:
from PIL import Image, ImageTk
except ImportError:
pass
else:
pim = Image.new('RGBA', (5,100), (0,255,0,64))
photo = ImageTk.PhotoImage(pim)
c.create_image(200,200, image=photo, anchor='nw')
#blank standard photoimage with red vertical borders
im = PhotoImage(width=7, height=480)
dat = ('red',)*480
im.put(dat, to=(0,0))
im.put(dat, to=(6,0))
box = c.create_image(0, 0, image=im, anchor='nw')
def on_motion(event):
left,top = c.coords(box)
dx = event.x - (left+7)
c.move(box, dx, 0)
c.bind('<Motion>', on_motion)
root.mainloop()
Related
I'm attempting to adapt martineau's excellent answer on how to use tkinter to highlight and select an area of an image with a mouse. My goal is to modify the code to support huge images, such as 10000x6000 pixels or more!
This is my first attempt at using tkinter, and it's much more challenging than expected.
The three options which I've considered and experimented with are:
Load the image, then scale the image to the size available in the window before placing it on the canvas. This in turn requires that I know the scale factor, so I can reverse the scaling when operating on the original image.
Add scrollbars to the side/button on the visible part of the image in the canvas. (My preferred option).
Don't add scrollbars, but use the cursor keys to scroll the visible part of the image in the canvas.
I've tried to do all of them, but ran into various kinds of trouble:
I used root.state('zoomed') to maximize the window, then placed a button to top of the window using tk.Button(...).pack(fill=tk.X) before placing the canvas as in the linked code using self.canvas = tk.Canvas(...). At this time I discovered two issues: It turns out that I can't maximize the canvas to the remaining space in the window using self.canvas.pack(fill=tk.BOTH) (it only expands the width). Another more crucial issue was that I could not read the actual width/height value that the canvas was expanded to, (or get there available space in the window before placing the canvas), and without these values, I can't calculate the scaling factor. :-(
I tried to follow various methods of adding scrollbars to the packed canvas but they all, with one exception, required to use the grid method and I quickly found that pack and grid methods were conflicting methods. After several attempts at rewriting the code to use the grid method, I gave up. The place method seemed promising, except that it will require me to size the canvas manually and then place the scrollbars at calculated offsets to the canvas... and all of this required that I have the same values that were needed to calculate the scaling factor above. :-(
I didn't get very far with this, as it seems I can't get (keyboard?) binds to work at all (On windows with Python 3.10.6). Shouldn't something like self.canvas.bind("<Right>", lambda event: print("Ping")) followed by self.canvas.focus_set() (perhaps with root instead of the canvas) work?
I'm sure I'm overlooking something basic, but after a few evenings looking at this I've gotten nowhere. :-(
I hope some of you can provide help and/or pointers allowing me to get further.
Ohh, for completeness, I'm trying to hack together a small tool that does the following:
On startup requests and load an image file. (Adding a button to perform the load/re-load is nice to have, but not strictly needed. )
Display the image file and allow an area of the image to be selected with the mouse.
Perform some software analysis on the selected part of the image when a button is pressed.
(0) Introduction
Hello Everyone! I am an absolute beginner to Tkinter and not sure how to achieve my result. I tried several strategies and was disappointed multiple times. Didn't work due to my ignorance.
I am building my own little programming text editor.
I want to add functionality like yellow light bulbs in IntelliJ IDEA.
According to the idea, a yellow circle with a question mark appears on the screen next to a character which caused a syntax error in the code.
The question is not about positioning objects. I sort of confident in it. I am struggling with tk.Canvas imposing its background. I want to get rid of background and keep drawings visible.
(1) A "Naive" approach
In the code editor tk.Frame I have the following widgets:
tk.Text where user types code.
Positioned with .pack() method.
Line numbers tk.Canvas which is responsible for displaying line numbers.
Positioned with .pack() method
For each syntax error in the code I create a tk.Canvas object where I create yellow circle and question mark text.
Positioned with .place() method
It turned out, tk.Canvas _object has solid background which is covering half of the screen and looks ugly
(2) Attempted strategies, which failed
I looked for a way to make the Canvas object transparent, but from what I found, transparency is platform-dependent. Scary. I gave up.
I also tried to make the canvas have zero size with canvas.configure(width=0, height=0).
I was hoping that the canvas will become invisible and as a result, its objects will have a transparency illusion.
Unfortunately, Canvas objects, rectangles, circles and others are not displayed outside the canvas itself.
The image above is evidence of that property.
The circle is not displayed fully, because a part of it is outside the canvas rectangle.
(3) Some Ideas
I have a couple ideas, which haven't been attempted yet, due to the lack of confidence and research
3.1. Maybe Z-index hack ?
I hope, that It is possible to use a single tk.Canvas to store all circles.
The canvas will be placed in the same position as the text.
Tweaking z-indices, hopefully, can be done in such a way, that the canvas background will be "bellow" The text field, while the yellow circles will be "above" the text.
However. That Idea raises the following questions:
Does tkinter have absolute Z-indexing?
Do the Canvas objects (circles) share the same Z-index?
If so then I guess, the strategy is doomed and will fail. The Canvas is a widget, while as long as I know, canvas objects are not widgets.
3.2. Switch from Canvas approach to Images?
I heard that tk.Label can store Images.
I could draw a simple PNG of yellow circle with question mark. Maybe instead of canvas circle I could display these labels with images and use .place() to adjust their positions. Maybe worth trying.
(4) Summary of the question
How to hack tk.Canvas or achieve transparent background by keeking floating circles not transparent on top of a text? Thank you very much!
EDIT:
The target Platform is Linux.
Hence using root.wm_attrinutes("-transparentcolor", somecolor) hack doesn't work.
wxPython 2.8.10, Python 2.6.2, Windows 7 x64
So I am having a bit of a wxPython issue I am hoping someone can say where I am going wrong. In my application I have Sliders where the background colour changes as you move the slider around. I do this via SetBackgroundColour from within a EVT_SLIDER handler. It mostly works, but I end up with a border of unchanged colour around the outer edge of the widget.
I have tried Refresh up the chain, RefreshRect on the top level Frame around the widget (and a few pixels further out), various combinations of Update and Layout, but nothing seems to erase it. The best I have managed is if I use something like:
frame = wx.GetTopLevelParent(slider)
rect = slider.GetRect()
slider.ClearBackground()
slider.SetBackgroundColour(wx.Colour(red,green,blue))
frame.RefreshRect(rect)
It will correctly draw the background box with the new colour without the border issue, but it will not repaint the slider itself.
However, if I click away so the focus is elsewhere, the border snaps to the new colour.
I could probably use an overlay or custom paint function but I am hoping there is something I am missing here that will get me the desired results.
Any suggestions?
(edited to add)
Looks like resizing the window manually corrects the issue, but Layout() does not.
It also functions correctly if you do the client size trick:
w,h = slider.GetClientSize()
slider.SetClientSize( (w+1,h) )
slider.SetClientSize( (w,h) )
For some reason, when I go to create_window in my Tkinter canvas, it erases everything that was previously in said window, and jams the window in the top left corner (even though I set it somewhere else.
canvas.create_window(30, height - 40, anchor = NW, width = 40,
window = canvas.data.buildSquareButton)
precedes
canvas.create_rectangle(0,0,width, 40, fill = "#888888888",
outline = "#888888888")
canvas.create_rectangle(0, height, width, (height-40), fill = "#888888888",
outline = "#888888888")
canvas.create_rectangle(0, 40, width, (height - 40), fill = "#fffffffff",
outline = "#fffffffff")
and an image.
I put in a 1 second time.sleep after the create_window, and I could see that the button was put in the right place. Then after the time.sleep was over, the button threw itself in the top right corner and the rectangle never appeared. I commented out the window, and the rectangles appeared fine.
Am I doing something wrong when I call the window, or is there a Tkinter glitch?
There's not enough information in your question to know for sure. However, my guess is that you are packing or griding a widget in the canvas, and that's causing the canvas to shrink to fit its contents. Or, you're doing something else to cause the canvas to shrink.
To compound the problem, your canvas probably has the same background color as your main window, so you think the contents are being erased, but in reality you're looking at the widget that the canvas is in rather than the canvas itself.
To help prove or disprove that theory, give your canvas a garish background color, such as a bright red. Then run your code and see what happens to the red part of the screen.
Bottom line: there is no bug in tkinter that would cause the behavior you describe. There is a bug in some code that you aren't showing us.
The best thing is for you to create the smallest possible program that reproduces the problem. The mere act of trying to do that may expose the bug in your code. If you are able to reproduce it in a dozen or two lines of code, update the question and we can probably spot the error.
I'm not entirely sure what your question is, but it looks like you're trying to remove everything from a canvas widget at one point in your code to allow for other things to override it, yes?
Try this to reset the canvas: canvas.delete("all")
I've looked through the documentation, and searched quite a bit now, and couldn't find an answer for this problem. Before I give up, I'll ask here.
I want to create a window that has no frame (as in, no titlebar, no border around the window), and maybe a transparent background. I've found how to set the background to a certain colour, but not to transparent, and couldn't find anything about whether I can have the frame not display itself.
So I ask, is it possible to create a Tkinter window that has no border, and a transparent background?
No border is possible. Try something like wm transient or wm overrideredirect.
Transparent depends, have a look at the documentation for wm attributes:
http://www.tcl.tk/man/tcl/TkCmd/wm.htm#M8 On windows you can set a transparentcolor.
Otherwise you might need a platform specific extension to create oddly shaped and transparent windows, for example: http://www.cs.man.ac.uk/~fellowsd/tcl/shapeidx.html