TurboC
TurboC

Reputation: 918

Tkinter - How can I get the system display settings (scale option) in order to adjust the scale of my icons properly?

In these days I realized that there is an issue with my GUI created with Tkinter and the system display settings (scale option).

If you change the display resolution, it's ok, because the pixel are always the same, but if you change the scale, it affects only the widgets that use text inside them. Imagines can talk better than thousand words:

enter image description here

Below the code:

import tkinter as tk

import io
import tksvg
from lxml import etree

import ctypes
from sys import platform

# resolution fix:
if platform == "win32":
    try:
        ctypes.windll.shcore.SetProcessDpiAwareness(2) # windows >= 8.1
    except:
        ctypes.windll.user32.SetProcessDPIAware() # windows <= 8.0

# my free svg icon:
dic_tool_bar={"start":'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>'}

# function to use svg icon in Tkinter:
def svg_to_image(source, fill=None, scale_to_width=None, scale_to_height=None, scale=1):

    # parse xml data
    root = etree.fromstring(source)
    tree = etree.ElementTree(root)

    # set path fill color if provided
    if fill is not None:
        root.attrib["fill"] = fill

    imgdata = io.BytesIO()
    tree.write(imgdata)
    kw = {"data": imgdata.getvalue()}
    if scale_to_width:
        kw["scaletowidth"] = scale_to_width
    if scale_to_height:
        kw["scaletoheight"] = scale_to_height
    if scale != 1:
        kw["scale"] = scale

    return tksvg.SvgImage(**kw)

class MainWindow:
    def __init__(self):

        self.parent=tk.Tk()
        self.parent.title("TEST")
        self.parent.minsize(350, 300)
        self.icon_color="#484848"
        self.default_font=("Segoe UI",9, "bold")
        
        self.obj_tool_bar=ToolBar(self)
        self.obj_tool_bar.pack(fill=tk.BOTH)
        
        self.parent.mainloop()

class ToolBar(tk.Frame):
    def __init__(self, mw):
        super().__init__(mw.parent)

        self["background"]="white"
        self.default_font=mw.default_font
        self.icon_color=mw.icon_color

        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        
        self.name=tk.Label(self, anchor="w", background="white", text="TEST!!", font=(self.default_font))
        self.border_up=tk.Frame(self, background="#bfbfbf", highlightthickness=0)
        self.border_down=tk.Frame(self, background="#bfbfbf", highlightthickness=0)

        self.start_logo=svg_to_image(dic_tool_bar["start"], fill=self.icon_color, scale=0.035)
        self.start=tk.Button(self, relief="flat", borderwidth=0, background="white", activebackground="white", image=self.start_logo)

        self.border_up.grid(column=0, row=0, sticky="new")
        self.name.grid(column=0, row=1, sticky="nsw", padx=(4,4), pady=(2,2))
        self.start.grid(column=0, row=1, sticky="ens", ipadx=4)
        self.border_down.grid(column=0, row=2, sticky="sew")

# start
app=MainWindow()

How can I solve this issue? I saw other softwares (not written in Python) with a toolbar, and in all of them, with different kind of system scales, the icons resolution changed always properly. How can I reach the same behaviour with Tkinter?

In my example, I was thinking about to create a custom function to change the resolution of the self.start_logo in according with the system scale option, but.. How? To do that, I need to get the system scale value or something like that, .. how can I do that? Do you have some advices?

Upvotes: 1

Views: 154

Answers (1)

v0rtex20k
v0rtex20k

Reputation: 1121

Currently, the user can set the icon’s size manually using this command:

self.start_logo=svg_to_image(dic_tool_bar["start"], fill=self.icon_color, scale=0.035)

To automate the process, we can use the following function to determine the current DPI:

from PyQt5.QtWidgets import QApplication as QApp

def get_dpi():
    dpi = 0
    with QApp([]) as app:
        dpi = app.screens()[0].physicalDotsPerInch()
    return dpi
    

You can read more about QApplication in the docs and here - note that many examples use sys.argv, but a simple empty list (as shown above) works just fine.

Then you can update your call to use the dpi to figure out the scale. Assuming the value from your question, 0.035, corresponds to normal dpi, you could scale this value by the computed dpi as a fraction of the baseline.

Upvotes: 0

Related Questions