-1

for my application I designed a simple on screen keyboard (OSK), it is needed on touchscreens but may be used also with the mouse. Initially the happlication had to run on 1920x1080 screens only, but now I have many requests to port on different resolutions. The problem is that the OSK was designed in the simplest way to fulfill the 1920x1080 screen so I adapted the font to obtain the right width for the buttons set to 4, where 4 is the number of chars may be shown in the button. But now, to adapt the OSK to every resolution I should calculate the width of the buttons. The OSK has 14 columns, special buttons as Shift, Enter, Backspace, Tab etc. have a width of 2 columns (they extend on 2 columns) and there is an horizontal padding of 10 piels between the buttons and the external limit of the OSK, so there are 15 "spacers" for 14 columns of buttons. I am able to compute the buttons width in pixels but I am not able to set the buttons width in pixels... only in number of characters. Is there anything I missed? Or does exist a workaround? If may help I can set the font to a common non proprotional one. I made a little test program to see if is possible to set the width in pixels of a ttk.Button, seems I am not able to do it:

    def testButtonsType():
    def select(choice):
        choice = choice
        if choice=="yes":
            print("yes")
        else:
            print("no")
    #---------------------------------------------------------------------------
    mainWin = Tk()
    mainWin.title("Main Window")
    mainWin.geometry('400x250')
    pixel = tk.PhotoImage(width=1, height=1)
    s = ttk.Style()
    s.configure("osk.TButton",foreground="black",
                                     background="white",
                                     height=50, 
                                     # padding=[10, 10, 10, 10],
                                     font = "None 14 bold")


    btnYes = ttk.Button(mainWin, text='SI', image=pixel, compound='c', width=200, style="osk.TButton", command=lambda: select("yes"))
    btnYes.grid(pady=(20,30), sticky='n', column=1, row=0)
    btnNo = tk.Button(mainWin, text='NO', image=pixel, compound='c', width=100, height=50, font = ("None 14 bold"), command=lambda: select("no"))
    btnNo.grid(padx=(20,20), pady=(20,30), sticky='n', column=0, row=0)
    

    mainWin.mainloop()
#-------------------------------------------------------------------------

This is a simplified implementation of OSK class, it manages only the first row of buttons and assumes they have all the same width. The OSK is done for a 1600x900 screen but the width of the button is larger so only 11 and one third of the 12st button are visible. Obviously something is wrong but I do not understand what is wrong.

    class OSK(Toplevel):
    __metaclass__ = Singleton

    def __init__(self, attach, rootWin, flgCloseOnEnter=True):
        super().__init__()
        self.title('On Screen Keyboard')
        self.flgCloseOnEnter = flgCloseOnEnter
        
        rW = 1600
        rH = 900
        oskHeight = 260
        keyColsCount = 14
        keysRowCount = 5
        padXleft = 10
        padYleft = 10
        self.btnW = 5
        self.btnH = 32
        self.style = ttk.Style()
        self.theme = "dark"
        self.Theme()
        self.geometry('%dx%d+%d+%d' % (rW, oskHeight, 0, rH-oskHeight))
        self.frmTop = Frame(self, style="osk.TFrame")
        self.grid_columnconfigure(0,weight=0)
        # self.frmTop.grid(padx=(padXleft,padXleft), pady=(padYleft,padYleft), sticky='nwse')
        self.frmTop.grid(sticky='nwse')
        for i in range(15):
            self.frmTop.grid_columnconfigure(i, minsize=self.btnW, weight=0)
        for i in range(5):
            self.frmTop.grid_rowconfigure(i, minsize=self.btnH)
        self.mainWin = self
        self.btnPadX = 10
        self.btnPadY = 4
        # showing all data in display
        self.exp = " "
        self.is_shift = False
        self.attach = attach
        self.overrideredirect(True)
        tick = Button(self.frmTop, text='`', command=lambda: self.press('`'), style="osk.TButton")
        tick.grid(row=0, column=0, sticky='nwes')
        num1 = Button(self.frmTop, text='1', command=lambda: self.press('1'), style="osk.TButton")
        num1.grid(row=0, column=1, sticky='nwes')
        num2 = Button(self.frmTop, text='2', command=lambda: self.press('2'), style="osk.TButton")
        num2.grid(row=0, column=2, sticky='nwes')
        num3 = Button(self.frmTop, text='3', command=lambda: self.press('3'), style="osk.TButton")
        num3.grid(row=0, column=3, sticky='nwes')
        num4 = Button(self.frmTop, text='4', command=lambda: self.press('4'), style="osk.TButton")
        num4.grid(row=0, column=4, sticky='nwes')
        num5 = Button(self.frmTop, text='5', command=lambda: self.press('5'), style="osk.TButton")
        num5.grid(row=0, column=5, sticky='nwes')
        num6 = Button(self.frmTop, text='6', command=lambda: self.press('6'), style="osk.TButton")
        num6.grid(row=0, column=6, sticky='nwes')
        num7 = Button(self.frmTop, text='7', command=lambda: self.press('7'), style="osk.TButton")
        num7.grid(row=0, column=7, sticky='nwes')
        num8 = Button(self.frmTop, text='8', command=lambda: self.press('8'), style="osk.TButton")
        num8.grid(row=0, column=8, sticky='nwes')
        num9 = Button(self.frmTop, text='9', command=lambda: self.press('9'), style="osk.TButton")
        num9.grid(row=0, column=9, sticky='nwes')
        numA = Button(self.frmTop, text='A', command=lambda: self.press('A'), style="osk.TButton")
        numA.grid(row=0, column=10, sticky='nwes')
        numB = Button(self.frmTop, text='B', command=lambda: self.press('B'), style="osk.TButton")
        numB.grid(row=0, column=11, sticky='nwes')
        numC = Button(self.frmTop, text='C', command=lambda: self.press('C'), style="osk.TButton")
        numC.grid(row=0, column=12, sticky='nwes')
        numD = Button(self.frmTop, text='D', command=lambda: self.press('D'), style="osk.TButton")
        numD.grid(row=0, column=13, sticky='nwes')
        numE = Button(self.frmTop, text='E', command=lambda: self.press('E'), style="osk.TButton")
        numE.grid(row=0, column=14, sticky='nwes')
    #---------------------------------------------------------------------------
            
        
# Needed functions

    def getAttach(self):
        return self.attach
    #---------------------------------------------------------------------------
    
    def setAttach(self, org):
        self.attach = org
    #---------------------------------------------------------------------------

    def press(self, num):
        print(num)
    #---------------------------------------------------------------------------

    def Backspace(self):
        n = len( self.attach.get() )
        self.attach.delete(n-1)
    #---------------------------------------------------------------------------
    
    def Enter(self):
        if self.flgCloseOnEnter == True:
            self.destroy()
        else:
            self.attach.insert(END, '\n')
    #---------------------------------------------------------------------------

    def Shift(self):
        if True:
            self.is_shift = not self.is_shift
            self.display()
        else:
            self.is_shift = not self.is_shift
            self.display()
    #---------------------------------------------------------------------------

    def Clear(self):
        self.attach.delete(0, END)
    #---------------------------------------------------------------------------


    def Theme(self):
        font = (None, 16)
        if True:
            if self.theme == "dark":
                self.configure(bg='gray27')
                self.style.configure('osk.TButton', background='gray21', foreground='black', font=font)
                self.style.configure('osk.TEntry', background='gray21', foreground='black')
                self.style.configure('osk.TFrame', bg='gray27')           
                self.theme = "light"
            elif self.theme == "light":
                self.configure(bg='gray99')
                self.style.configure('osk.TButton', background='azure', foreground='black', font=font)
                self.style.configure('osk.TEntry', background='azure', foreground='black')
                self.style.configure('osk.TFrame', bg='gray99')           
                self.theme = "dark"
    #---------------------------------------------------------------------------

#---------------------------------------------------------------------------
#---------------------------------------------------------------------------


if __name__ == '__main__':

    def testMinOSK():
        mainWin = Tk()
        top = OSK(None, mainWin)
        mainWin.mainloop()
    #-------------------------------------------------------------------------

        
    testMinOSK()
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
Massimo Manca
  • 385
  • 1
  • 2
  • 15
  • If you have used `grid()` on those `ttk.Button`, you can set the `minsize` option of `.columnconfigure()` to the required width and `.rowconfigure()` to the required height. Then set `sticky="nsew"` when calling `.grid()` on those buttons. – acw1668 Mar 13 '23 at 12:30
  • I am not able to setup the grid cells width to the width I want for my buttons. May be the error is stupid but this is the situation. I have a form with inside a frame that fulfills it and inside the frame I want to have a matrix of 14 columns and 5 rows, each cell accepts a button, same big buttons occupy 2 cells and space tab more. I put a simplified OSK class and test code in the original post, please look at it. – Massimo Manca Mar 14 '23 at 00:38
  • It is because the final width of those buttons is bigger than `minsize` (I wonder why you set it to 5, but not `rW // keyColsCount`). Try set `width=0` in `self.style.configure('osk.TButton', ...)` inside `Theme()`. – acw1668 Mar 14 '23 at 01:13
  • You are right, my mistake, I was testing the limits and I wrongly copied the number of rows in self.btnW. But without setting width=0 in the Theme for the button it does not work so it is your suggestion that solves the problem. The important thing is that the grid column min width has to be larger than the button width. Now I have to play a little bit with padding but I think the problem is solved. – Massimo Manca Mar 14 '23 at 09:03

1 Answers1

0

If you give the button an image, the width and height parameters are treated as pixels rather than a number of characters. So, you can create a 1x1 transparent pixel (eg: tk.PhotoImage(width=1, height=1)) and add that to a button along with the text (and setting compound to something reasonable). That will let you define the size of a button in pixels.

Alternately, you can add a single button to a frame with no borders, and use place with options to cause it to fully fill the frame. You can then set the frame width and height in pixels, and the button will fill that frame.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Yuour 1st solution works with tkinter Button not with ttk Button. I will update my question with some test code. – Massimo Manca Mar 13 '23 at 09:40
  • @MassimoManca Then use the second solution. – acw1668 Mar 13 '23 at 10:53
  • well, all the application is designed using ttk and the ttk Button looks better than tk Button, I am testing your idea using a frame for each button. – Massimo Manca Mar 13 '23 at 10:57
  • I am not able to setup the grid cells width to the width I want for my buttons. May be the error is stupid but this is the situation. I have a form with inside a frame that fulfills it and inside the frame I want to have a matrix of 14 columns and 5 rows, each cell accepts a button, same big buttons occupy 2 cells and space tab more. I put a simplified OSK class and test code in the original post, please look at it. – Massimo Manca Mar 14 '23 at 00:26