Tick Marks not Displaying - Tkinter Scale - python

None of the tkinter Scales I've made show tickmarks.
I tried pasting other people's code into my editor and same result, no ticks. The example is utterly simple so I couldn't think of anything to try except different values for the tickinterval option. I'm using tkinter 8.6 with python 3.5. Thanks for any assistance.
import tkinter as tk
root = tk.Tk()
flt = tk.Scale(
root,
label="No Ticks",
from_=50.0,
to=200.0,
length=900,
orient="horizontal",
resolution=25,
tickinterval=50.0)
flt.grid()
root.mainloop()
There are no error messages and everything else seems to work.

The option tickinterval does not refer to tick marks but to the numbers displayed along the Scale.
Once this was pointed out by Bryan Oakley in the comments, I was able to glean the following information from running simple examples, which I had not found in the docs. Basically, the resolution option and the tickinterval option values need to be considered together for both to work as desired.
tickinterval
Using the default value of zero, the only number shown will be the one which names the position of the slider. Otherwise the tickinterval will be the distance apart of numbers marking positions on the Scale. To be sure tickintervals will be displayed as desired, this should be set to an even multiple of resolution e.g. if resolution = 100, tickinterval should be 200
or 300 etc. If resolution is set to 0 or -1, tickinterval will display at any increment desired.

Related

Tkinter message wrapping for text unknown reason

I have run into a strange behaviour of Tkinter's Message widget. It worked fine before, but now it is wrapping (Date and time on left image, only date on right image) when it shouldn't. I noticed time wrapped for some seconds and others not, because not all characters are the same length.
Note that the images are cropped, but the widget actually continues about 200 more pixels. I have not set wraplength (which weirdly enough wasn't even recognised as an option, even though it is in the docs) so that is still at its default 0 (no wrapping). I have played around a bunch but I can't figure it out.
The text is generated from a dictionary, roughly as follows:
import tkinter as tk
root = tk.Tk()
root.geometry('500x500+0+0')
root.configure(bg='white')
desc = tk.Message(root)
desc.configure(bg='white')
desc.pack()
description={}
description['Date'] = '11 June 2020'
description['Time'] = '21:26:26'
description['Duration'] = 0
description['Overlay'] = str(False)
descToLines = [f'{key}:\t {val}' for key, val in description.items()]
linesToString = '\n'.join(descToLines)
desc.configure(text=linesToString)
root.mainloop()
In fact, while verifying this minimal code, the time line 21:26:38 is now inline with Time, but the date still isn't. Any help is welcome.
And here is how you can see what is missing: change the background color of the Message box to another color. You'll immediately see that its width is too small. Try:
desc.configure(bg='#FF0000')
And then:
desc.configure(bg='#00FF00', width=200)
Which gives:
As per suggestion I set the Message's width manually and it resolved the problem. What is confusing me is that I thought it would already be set using the line:
desc.place(x=0, y=0, height=0, width=300), but apparently not. At least not so far as to override the aspect ratio.
Tl;dr: Adding desc.configure(width=300) at the end fixes the problem.

Detect DPI/scaling factor in Python TkInter application

I'd like my application to be able to detect if it's running on a HiDPI screen, and if so, scale itself up so as to be usable. As said in this question, I know I need to set a scaling factor, and that this factor should be my DPI divided by 72; my trouble is in getting my DPI. Here's what I have:
def get_dpi(window):
MM_TO_IN = 1/25.4
pxw = window.master.winfo_screenwidth()
inw = window.master.winfo_screenmmwidth() * MM_TO_IN
return pxw/inw
root = Tk()
root.tk.call('tk', 'scaling', get_dpi(root)/72)
This doesn't work (testing on my 4k laptop screen). Upon further inspection, I realized get_dpi() was returning 96.0, and that winfo_screenmmwidth() was returning 1016! (Thankfully, my laptop is not over a meter wide).
I assume that TkInter is here calculating the width in mm from some internally-detected DPI, wrongly detected as 96, but I'm not sure where it's getting this; I'm currently on Linux, and xrdb -query returns a DPI of 196, so it's not getting the DPI from the X server.
Does anyone know a cross-platform way to get my screen DPI, or to make TkInter be able to get it properly? Or, more to the point: how can I make TkInter play nice with HiDPI screens and also work fine on normal ones? Thanks!
This answer is from this link and left as a comment above, but it took hours of searching to find. I have not had any issues with it yet, but please let me know if it does not work on your system!
import tkinter
root = tkinter.Tk()
dpi = root.winfo_fpixels('1i')
The documentation for this says:
winfo_fpixels(number)
# Return the number of pixels for the given distance NUMBER (e.g. "3c") as float
A distance number is a digit followed by a unit, so 3c means 3 centimeters, and the function gives the number of pixels on 3 centimeters of the screen (as found here).
So to get dpi, we ask the function for the number of pixels in 1 inch of screen ("1i").
I know I'm answering this question late, but I'd like to expand upon #Andrew Pye 's idea. You are right, GUI's with tkinter look different across different monitors with different DPI's anytime you use a 'width' or 'height' or 'pady' or anything that is measured in pixels. I noticed this when I made a GUI on my desktop, but then later ran the same GUI on my 4K laptop (The window and the widgets appeared much smaller on the laptop). This is what I did to fix it, and it worked for me.
from tkinter import *
ORIGINAL_DPI = 240.23645320197045 # This is the DPI of the computer you're making/testing the script on.
def get_dpi():
screen = Tk()
current_dpi = screen.winfo_fpixels('1i')
screen.destroy()
return current_dpi
SCALE = get_dpi()/ORIGINAL_DPI # Now this is the appropriate scale factor you were mentioning.
# Now every time you use a dimension in pixels, replace it with scaled(*pixel dimension*)
def scaled(original_width):
return round(original_width * SCALE)
if __name__ == '__main__':
root = Tk()
root.geometry(f'{scaled(500)}x{scaled(500)}') # This window now has the same size across all monitors. Notice that the scaled factor is one if the script is being run on a the same computer with ORIGINAL_DPI.
root.mainloop()
I'm using TclTk, not TkInter, and the only way I know how to do this is to work it out from the font metrics...
% font metrics Tk_DefaultFont
-ascent 30 -descent 8 -linespace 38 -fixed 0
The linespace is approximately 0.2x the DPI (currently set to 192 here)

Tkinter: nested LabelFrame (s) not shown

I want to split a LabelFrame into two label frames. So first, I created an other LabelFrame and tested if it displays well. But no, it is not displayed.
But when I change childLabelFrame to a simple Label or a simple Frame I see it displayed well.
I read some similar questions such as this one, but I did not do those errors in my case.
mainLabelFrame=LabelFrame(parent,text="Description:",padx=20,pady=20,200, width=400,relief=RIDGE)
childLabelFrame=LabelFrame(mainLabelFrame,text="Help",relief=RIDGE)
childLabelFrame.grid(row=0,column=0)
mainLabelFrame.grid(row=3,column=0,columnspan=3,sticky=E+W)
How to resolve this ?
It seems like childLabelFrame has zero size and thus is not drawn. Indeed, both childLabelFrame.winfo_width() and childLabelFrame.winfo_height() return 1.
It is drawn correctly if
you specify a size, like childLabelFrame = LabelFrame(mainLabelFrame, text="Help", height=100, width=200), or
you add something inside the child label frame, e.g. Label(childLabelFrame, text="label").grid().

Default Image sizes in ttk.Label

Currently I'm using this snippet of code which seems pretty easy:
label = ttk.Label(mainframe)
image1 = PhotoImage(file='my_image.gif')
label['image'] = image1
label.grid(column=1, row=0)
However, if I edit the size of my_image.gif in photoshop, then run it again, the image gets stretched to the same size, and this seems to continue no matter how small I make the base image. This seems to suggest to me that the PhotoImage or something above it enforces a default size or specific minimum size. I cannot find any documentation to suggest that this is the case.
From here I found the help(PhotoImage) suggestion which I used. When in the python interpreter I run the help(PhotoImage) command and I found this:
height(self)
Return the height of the image.
type(self)
Return the type of the imgage, e.g. "photo" or "bitmap".
width(self)
Return the width of the image.
But it doesn't seem to provide me with an image sizing of any type either.
After searching all over and seeing no reference at all, i'm beginning to suspect that using images in a Label is for a specific purpose and I'm approaching this all wrong. All i'm trying to do is place a logo at the top of the window, but I want the logo to be limited in size so it doesn't take over the whole window.
Also of note is this question which seems to be lacking an answer but I too am curious if there is some documentation on it. Maybe I'm missing something obvious but I did check the python documentation and the http://www.tkdocs.com site for more information.
Apparently I made an error, but I have no clue what it was. In the end this was this code that did it for me:
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("ImageTest")
label = ttk.Label(root)
image1 = PhotoImage(file='my_image.gif')
label['image'] = image1
label.grid(column=1, row=0)
root.mainloop()
It's all working now as expected.

Drawing window border in Python xlib

I'm working on a window manager written using python's xlib bindings and I'm (initially) attempting to mimic dwm's behavior in a more pythonic way. I've gotten much of what I need, but I'm having trouble using X's built in window border functionality to indicate window focus.
Assuming I've got an instance of Xlib's window class and that I'm reading the documentation correctly, this should do what I want to do (at least for now) - set the window border of a preexisting window to a garish color and set the border width to 2px.
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(\
"#ff00ff").pixel
window.change_attributes(None,border_pixel=border_color,
border_width = 2 )
self.dpy.sync()
However, I get nothing from this - I can add print statements to prove that my program is indeed running the callback function that I associated with the event, but I get absolutely no color change on the border. Can anyone identify what exactly I'm missing here? I can pastebin a more complete example, if it will help. I'm not exactly sure it will though as this is the only bit that handles the border.
Looks like this was complete PEBKAC. I've found an answer. Basically, I was doing this:
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(
"#ff00ff"
).pixel
window.configure(border_width=2)
window.change_attributes(
None,
border_pixel=border_color,
border_width=2)
self.dpy.sync()
Apparently this was confusing X enough that it was doing nothing. The solution that I've stumbled upon was to remove the border_width portion from the window.change_attributes() call, like so:
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(
"#ff00ff"
).pixel
window.configure(border_width=2)
window.change_attributes(
None,
border_pixel=border_color
)
self.dpy.sync()
I hope this helps someone later on down the road!

Categories