I have defined a Label object as:
panel = Label(image_frame, image=self.img, cursor="cross")
Now, I'd like to do draw polygon on top of this, and I have created a function called draw() that binds to a canvas and will allow me to draw a polygon on top of it. So, I know my draw() command works.
However, I need to do this on top of the panel that I've defined as a Label.
The biggest problem I'm having is this line in my draw() command
if event.widget.canvasx(event.x)-2 < orig_x < event.widget.canvasx(event.x)+2 and event.widget.canvasy(event.y)-2 < orig_y < event.widget.canvasy(event.y)+2 :
I'm producing the following error:
AttributeError: 'Label' object has no attribute 'canvasx'
Is there an analog for canvasx for Label object? How can I bypass this without changing Label? Or is changing Label to canvas my only option?
The only other thing I can think of is to have a transparent canvas behind the Label, but then on-resize, things get messy.
Is there an analog for canvasx for Label object? How can I bypass this without changing Label? Or is changing Label to canvas my only option?
No, because there is no need. canvasx exists because the canvas can be scrolled in any direction, and you need to be able to convert from widget coordinates to the inner canvas coordinates.
In the case of a label, if you click at the 0,0 coordinate of a label, that will always be the upper left corner of the label, because the interior of the label cannot be scrolled.
Note: it is not possible to embed a label widget (or any other widget) on a canvas, and then draw on top of the label. The official canvas documentation says this about that:
Note: due to restrictions in the ways that windows are managed, it is not possible to draw other graphical items (such as lines and images) on top of window items. A window item always obscures any graphics that overlap it, regardless of their order in the display list.
Related
So, I'm using the place method to have a widget overlap other widgets, but its position is relative (with winfo) to a widget that uses pack. When the parent frame is resized, the pack position will change, but the place position will not.
This is my code:
from tkinter import *
root = Tk()
root.geometry("200x300")
search = Entry(root)
search.pack()
search.update()
x = search.winfo_x()
y = search.winfo_y()
width = search.winfo_width()
height = search.winfo_height()
frame = LabelFrame(root, width=width, height=200)
frame.place(x=x, y=y+height)
root.mainloop()
The LabelFrame stays in its x and y position when the window is resized. The Entry widget will be used as a search bar and I want autocompletion under it. There will be widgets under the entry widget and the autocompletion will only appear when you are typing (That's not what I'm looking for though. Its just more exposition if you need it). So, is there a way to have the place widget always be relative to the pack widget. If you have any answers, thank you:)
If your goal is to put one widget relative to another, place lets you do that. It's great for things like tooltips or other transient widgets that don't otherwise fit into a normal layout.
The easiest way to do that is to make the widget a child of the controlling widget. For example, for your frame to be placed relative to the search box you can make it a child of the search box. If it's inconvenient to do that, you can use the in_ parameter to tell place which widget is the other widget.
For example, to place your labelframe immediately below the search box and with the same width as the search box you might do it something like this:
frame.place(
in_=search,
bordermode="outside",
anchor="nw",
relx=0,
rely=1.0,
y=5,
relwidth=1.0
)
This is what the options mean:
in_=search: place the frame relative to the search box
bordermode="outside": relative measurements are from the outside of the border (default is "inside")
anchor="nw": place the widget so that the northwest corner of the frame is at the computed coordinate
relx=0: place the anchor point 0% from the left edge of the search box
rely=1.0: place the frame at 100% of the height of the search box
y=5: add 5 pixels to the computed position so it floats just a little below the window
relwidth=1.0: make the width of the frame 100% the width of the search box.
Obviously you don't have to use y=5, I just added it to illustrate the additive behavior of using rely and y.
I am creating a gui with tkinter in python. I have created a scrollbar and this is what the section of code looks like:
beta_frame = Frame(width="500", height="680")
beta_frame.pack()
holder = ScrolledWindow(beta_frame, width=500, height=680)
holder.pack()
alpha_frame = holder.window
I would like to position this scrollbar at the very bottom every time something new is put on the screen (which would obviously be added to the bottom The only things I'm adding to the screen are labels and buttons), though I'm unsure how to do this and I've searched everywhere. All I came up with is the method see, which I am unsure if it is even applicable in this instance. Any help would be appreciated.
.see() is the normal way to get Tkinter to auto-scroll to a given position, but that method only exists on the widgets that have built-in support for scrolling - Listbox, Canvas, Text, and Entry. The Tix ScrolledWindow makes an ordinary Frame scrollable, so no such method will exist.
It appears that this line of code will do what you want:
holder.tk.eval(holder.vsb['command'] + " moveto 1.0")
vsb is the vertical scrollbar component of the ScrolledWindow, 'command' is the scrollbar configuration option that specifies a callback to invoke when the position is changed. This will refer to something deep inside Tix, but we don't care exactly what it is; we just invoke it with the same parameters that the scrollbar itself would, if being moved to the very end.
How can I give a widget a fixed position? Like so I can "attach"/put it at the bottom of the window and it will always be there; even when the window is expanded. I couldn't find anything useful on how to do it and, I suppose obviously, none of the obvious things work (resize(), setGeometry(), etc.). Any help?
I assume by "fixed position" you mean a position relative to one of the window edges. That's what your second sentence implies. So that's the question I will answer.
Use a layout manager with stretches and spacings. Here's a simple example to attach a widget "w" to the bottom of a window "win". This code typically gets called by (or goes inside) your window's constructor.
lay = QVBoxLayout(win)
lay.addStretch(1)
lay.addWidget(w)
The BoxLayout makes "w" stick to the bottom of the window and stay in that position as the window is resized.
you must reimplement the parent windows resizeEvent function, here is the code to make a widget "attached" to the bottom of the window:
def resizeEvent(self, event):
#widget.move(x, y)
self.bottom_widget.move(0, self.height() - self.bottom_widget.height())
#if you want the widgets width equal to window width:
self.bottom_widget.setWidth(self.width())
whenever the window is resized, this function will be called and it will move the widget to the bottom of the window. this is an absolute positioning approach, but you can always use QSpacerItem to push your widget to the bottom.
So I'm learning python and the book that is teaching me gives me two ways to create a label using tkinker:
self.canvas.create_text(30,10,text="Welcome",tags="text")
&
self.lbl = Label(frame1, text = "Welcome")
In the former example, moving it is easy:
self.canvas.move("text", 1, 0)
In the latter example, changing it's background color is easy:
self.lbl["bg"] = "red"
However I do not know how to both move it AND change it's background color in either example, at least not how to move it incrementally. I can do this:
self.lbl.place(x=2)
But unless I can get the x coordinate ahead of time, I can only move it once. I could set it ahead of time, but I'd like to avoid that option if possible.
There are ways to do both.
Firstly, Canvas text does not have a background, but you can create your own with a rectangle.
text = self.canvas.create_text(30, 10, text="Welcome", tags="text")
# The canvas.bbox method returns the corner coordinates of the provided item id.
rect = self.canvas.create_rectangle(self.canvas.bbox(text), fill='red')
# Then you need to reposition the rectangle so that it is behind the text.
self.canvas.lower(rect, text)
From there you just move them the same way as you mentioned in your question. Adding a group tag to both the text and rectangle would save you from having to move both items separately.
Secondly, you can get the current x, y coordinates of a widget with the .winfo_x() and .winfo_y() methods. So moving the Label becomes a simple matter of addition/subtraction:
self.lbl.place(x=self.lbl.winfo_x()+2)
I do not know of a method that moves a widget in increments as the move method does for the canvas.
As for which is best, I can't think of much between them. I suppose using a Canvas would mean you couldn't overlap any other widgets that may be in the window, since the text would just scroll out of view, and if you start using the ttk version of Label then styling isn't quite as straight forward, although it's not difficult.
I have a Tkinter canvas with a scrollbar, and some items that when I click them, it's supposed to return the coordinates. (Using Python.)
This works fine with the objects that's initially visible in the window. When I scroll down, however, and the items further down on the canvas come into view, I don't get their canvas coordinates when clicking, but the window coordinates.
I can't find info on how to get the absolute coordinates, so I'm wondering if anyone here knows how to do it?
Thanks.
Check out the documentation for the canvas widget here.
To convert from window coordinates to canvas coordinates, use the canvasx and canvasy methods.
Here is an example callback function which converts the window's x and y coordinates and prints the item closest to that position via the find_closest() method.
def callback(event):
canvas = event.widget
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
print canvas.find_closest(x, y)