Home > Back-end >  How to create a GUI in Python without advanced libraries?
How to create a GUI in Python without advanced libraries?

Time:11-07

I want to create a GUI, without Tkinter or any other library (preferably in Windows and CPython).

Basically, if you go into the files of Python and its libraries, you can find the functions of the libraries. An example is the randint function of the random library:

def randint(self, a, b):
    """Return random integer in range [a, b], including both end points.
    """
    
    return self.randrange(a, b 1)

Now, instead of using randint, I could just type what the function does in my programs.

This means that Tkinter must have something similar for creating a GUI (though I cannot find Tkinter's files), a made-up example:

def Tk(self, ...):
    """...
    """
    
    ...
    
    (simple library like "os" or "sys").create_window(...)
    
    ...

I want the create_window function to use instead of Tkinter, and I would like some documentation on those functions.

I have heard that Tkinter communicates directly to some C stuff, if that is the case, I want to know how to do it directly from my program instead of using Tkinter. And I do not care if it's more complicated, unnecessary, non-pythonic, or any of those, I simply need to create a GUI without any GUI libraries.

CodePudding user response:

An admirable goal! If you're doing this, I hope it's for your own interest and development, rather than any serious application, since libraries like tkinter are abstractions of abstractions, and do an awful lot of tedious boilerplate work for you. In the Real World, it's not usually appropriate to do this kind of legwork yourself since you'll re-make a lot of the mistakes that the Tcl developers made and fixed decades ago. It is, however, often interesting to look into how you might go about it.

In fact, the source code for tkinter is available on GitHub and you can go and read it yourself. In CPython, tkinter is implemented as a series of python bindings here, and a C module here. All of this is an interface into Tcl/Tk, which themselves are open-source tools. It's several thousand lines of both C and Python, so I'm afraid I can't easily summarise how it all works, but on Windows it boils down into direct calls to the Windows API, with functions like RegisterClassW and CreateWindowW.

If you'd like, you can directly interface with these APIs from your own Python code using ctypes - of course, this is still a library, but you'll be interacting at a more fundamental level than the Tcl interface. This question gives a simple starting point with the following code snippet:

import ctypes  # An included library with Python install.
ctypes.windll.user32.MessageBoxA(0, "Your text", "Your title", 1)

NB: based on the date of that question, it might be a Python2 answer, so on a modern Python implementation the call might instead be ctypes.windll.user32.MessageBoxA(0, b"Your text", b"Your title", 1) - I don't run Windows so I don't know.

The functions you'll need to call are all well documented on the relevant pages on the Microsoft website, so what you'd need to look for is a good tutorial on graphics programming using the Windows API, and then translating that into Python's ctypes interface. I can't vouch for it myself, but this tutorial looks OK.

It may be that you wanted to go even lower-level than that, and see what the Windows API actually does when you call a function like MessageBoxA. I'm curious, too, but Microsoft have elected to keep their API closed-source, so we're not able to look at the source code for this function. If you absolutely have to know, you'll need to use a reverse-engineering toolkit to open up the dll file that implements it. Depending on your jurisdiction, this might be in breach of local copyright law, so that's where this answer ends.

CodePudding user response:

okay ... that's too elaborate, all the functions to create and manipulate applications on windows are written in C and are stored in win32 api dlls on windows for example this is how you do it in C as stated in win32 docs

this program needs to be translated to python through ctypes, which will be calling C code from within python, you will be writing C code in python which is almost 4 times the code you would've written in C, and it will work 100 times slower and you have to have very good experience in C and python and win32 api to get a useful application working .... that can be advanced for a lot of senior developers out there.

so the track is:

learn C -> learn win32 api -> learn python -> learn ctypes -> learn python-C ABI -> make a simple hello world program

it's much easier (and faster) to write your code entirely in C then wrap it into a python module as done in tkinter and wxwidgets and QT and almost every other framework out there.

this is a gist of how it would be done in 32 bit python , but it won't work on 64 bit python because the types of variables have changed between 32 and 64 bit windows dlls. https://gist.github.com/mouseroot/6128651

this is another gist on 64 bit systems, which fixes the types problem. https://gist.github.com/syegulalp/7cf217677e893881a18d10020f2966e4

# Adapted for Python 3.6   64-bit Windows

from ctypes import *
from ctypes.wintypes import *

WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM)

WS_EX_APPWINDOW = 0x40000
WS_OVERLAPPEDWINDOW = 0xcf0000
WS_CAPTION = 0xc00000

SW_SHOWNORMAL = 1
SW_SHOW = 5

CS_HREDRAW = 2
CS_VREDRAW = 1

CW_USEDEFAULT = 0x80000000

WM_DESTROY = 2

WHITE_BRUSH = 0


class WNDCLASSEX(Structure):
    _fields_ = [("cbSize", c_uint),
                ("style", c_uint),
                ("lpfnWndProc", WNDPROCTYPE),
                ("cbClsExtra", c_int),
                ("cbWndExtra", c_int),
                ("hInstance", HANDLE),
                ("hIcon", HANDLE),
                ("hCursor", HANDLE),
                ("hBrush", HANDLE),
                ("lpszMenuName", LPCWSTR),
                ("lpszClassName", LPCWSTR),
                ("hIconSm", HANDLE)]


user32 = ctypes.WinDLL('user32', use_last_error=True)
user32.DefWindowProcW.argtypes = [HWND, c_uint, WPARAM, LPARAM]


def PyWndProcedure(hWnd, Msg, wParam, lParam):
    if Msg == WM_DESTROY:
        user32.PostQuitMessage(0)
        return 0
    return user32.DefWindowProcW(hWnd, Msg, wParam, lParam)


def main():
    WndProc = WNDPROCTYPE(PyWndProcedure)
    hInst = windll.kernel32.GetModuleHandleW(0)
    wclassName = u'My Python Win32 Class'
    wname = u'My test window'

    wndClass = WNDCLASSEX()
    wndClass.cbSize = sizeof(WNDCLASSEX)
    wndClass.style = CS_HREDRAW | CS_VREDRAW
    wndClass.lpfnWndProc = WndProc
    wndClass.cbClsExtra = 0
    wndClass.cbWndExtra = 0
    wndClass.hInstance = hInst
    wndClass.hIcon = 0
    wndClass.hCursor = 0
    wndClass.hBrush = windll.gdi32.GetStockObject(WHITE_BRUSH)
    wndClass.lpszMenuName = 0
    wndClass.lpszClassName = wclassName
    wndClass.hIconSm = 0

    regRes = windll.user32.RegisterClassExW(byref(wndClass))

    hWnd = windll.user32.CreateWindowExW(
        0, wclassName, wname,
        WS_OVERLAPPEDWINDOW | WS_CAPTION,
        CW_USEDEFAULT, CW_USEDEFAULT,
        300, 300, 0, 0, hInst, 0)

    if not hWnd:
        print('Failed to create window')
        exit(0)
    print('ShowWindow', windll.user32.ShowWindow(hWnd, SW_SHOW))
    print('UpdateWindow', windll.user32.UpdateWindow(hWnd))

    msg = MSG()
    lpmsg = pointer(msg)

    print('Entering message loop')
    while windll.user32.GetMessageA(lpmsg, 0, 0, 0) != 0:
        windll.user32.TranslateMessage(lpmsg)
        windll.user32.DispatchMessageA(lpmsg)

    print('done.')


if __name__ == "__main__":
    print("Win32 Application in python")
    main()

after all this mess with your code being too messy and slow, your code will still only work on your operating system, so you will still have to suffer 10 times the headache to port it to another operating system, so using a library to handle this stuff is preferred.

  • Related