The TkXUI Python library allows you to create frameless, modern and fully customizable GUIs using the simplicity of the Tkinter API.
The library also implements a JSON loader to define your GUIs (windows, buttons, labels, etc.) with JSON for a fast and reliable UI design.
It supports all kind of widgets (both ttk and tkinter widgets), styling and theming like in CSS with id-class system.
If you are familiar with installing Python packages, you can use the following command:
pip install tkxui
Otherwise, you can simply download the source code from github and paste the tkxui
folder in your project root directory.
In order to create your window, you have to import the tkxui library and implement a Tk
object.
If you want a frameless window, pass FRAMELESS
to the Tk's display parameter, otherwise it will create a normal window. If you want a system-like window pass NATIVE
to the Tk's display parameter (default value).
import tkxui
import tkinter
# Setting up the window with frameless attribute
root = tkxui.Tk(display=tkxui.FRAMELESS)
# Setting window attributes
root.config(bg="#2A2A2A")
root.geometry("500x300")
root.title("My First TkXUI window !")
# Set the resizer's background color
root.resizer_bg("#2A2A2A")
# Create a label
label = tkinter.Label(root, bg="#2A2A2A", text="Hello TkXUI !", fg="white")
label.pack(fill=tkinter.BOTH, expand=1)
root.mainloop()
You can choose not to have a default border to your frameless window by passing False
to the Tk's defaultBorder parameter (In this case, you would have to create your own border, there is a section down below that explains how to do it).
You can define your GUI in a JSON file, like this :
{
"doctype":"ui-definition",
"main":{
"tk.Frame":{
"bg":"#2A2A2A",
"name":"frame1",
"pack_options":{
"fill":"both",
"expand":"1"
},
"tk.Label":{
"text":"Hello TkXUI !",
"bg":"#2A2A2A",
"fg":"white",
"name":"hello_label"
},
"ttk.Button":{
"text":"Click me !!!",
"name":"clickme_button"
}
}
},
"border":{
"tk.Label":{
"text":"TkXUI Window",
"name":"window_title",
"pack_options":{
"side":"left",
"padx":"(20, 0)"
}
}
}
}
Here, we define what is in the window - main
- (a label and a button) and what is in the border
(a label) in a ui-definition
file.
Don't forget to add this line on top of your file : "doctype":"ui-definition"
.
Each widget has a name so that we can access them in our code.
Then, we need to load it on our window. To do that, we need to create a JSONLoader
object that takes the window as parameter :
import tkxui
import tkinter
# Setting up the window with frameless attribute
root = tkxui.Tk(display=tkxui.FRAMELESS)
# Setting window attributes
root.config(bg="#2A2A2A")
root.geometry("500x300")
root.title("My First TkXUI window !")
# Set the resizer's background color
root.resizer_bg("#2A2A2A")
# Load the JSON file
json_loader = tkxui.JSONLoader(root) # Create a JSONLoader object
json_loader.generate("ui.json") # Load our JSON file and generate our UI.
# We can access our widgets directly with their names
root.clickme_button.config(
command=lambda:root.hello_label.config(text="Button clicked!")
)
root.mainloop()
You can create your own widgets by defining them on your JSON file. Here is an example on how to use this feature :
{
"doctype":"ui-definition",
"widget:MyCustomWidget":{
"tk.Frame":{
"bg":"#2A2A2A",
"name":"frame",
"pack_options":{
"fill":"both",
"expand":"1"
},
"tk.Entry":{
"name":"entry"
},
"tk.Button":{
"text":"Submit",
"border":"0",
"name":"submit_button"
}
}
},
"main":{
"@MyCustomWidget":{
"name":"my_widget1"
},
"@MyCustomWidget":{
"name":"my_widget2"
}
}
}
You can define your widget with widget:<YourWidgetName>
and you can use it by typing @
plus the name of the widget wherever you want in the window's ui definition file.
This is going to create the same widget twice but all subwidgets will have a different parent so that you can access them explicitly.
For instance if you want to change the background color of the first custom widget's button you would call it using the name you gave for the custom widget instance (it means <window>.<widget name>.<subwidget>)
, e.g. :
# Change the color of the first custom widget's button
root.my_widget1.submit_button.config(bg="green")
The TkXUI library allows you to apply themes and styles to your window.
In order to do that, we need to create another JSON file that is going to contain the theme definition (like a style.css
file in CSS) :
{
"doctype":"theme",
"tk.Button":{
"bg":"lightblue"
},
"tk.Entry":{
"bg":"red"
}
}
Don't forget to add "doctype":"theme"
on top of your file. Here, we define that all buttons and entries, by default, have a background color of lightblue
for the buttons and red
for the entries.
In order to apply this theme, we need to generate the file with our JSONLoader
object:
import tkxui
# Setting up the window with frameless attribute
root = tkxui.Tk(display=tkxui.FRAMELESS)
# Setting window attributes
root.config(bg="#2A2A2A")
root.geometry("500x300")
root.title("My First TkXUI window !")
# Set the resizer's background color
root.resizer_bg("#2A2A2A")
# Load the JSON file
json_loader = tkxui.JSONLoader(root) # Create a JSONLoader object
json_loader.generate("ui.json") # Load our JSON file and generate our UI.
json_loader.generate("theme.json") # Load our JSON file and apply the theme.
Here is our ui definition file :
{
"doctype":"ui-definition",
"main":{
"tk.Frame":{
"bg":"#2A2A2A",
"name":"frame1",
"pack_options":{
"fill":"both",
"expand":"1"
},
"tk.Entry":{
"name":"entry1"
},
"tk.Button":{
"text":"Click me !!!",
"border":"0",
"name":"clickme_button"
}
}
}
}
And this is what we get with the theme applied :
As you can see, even the widgets of the border are affected by the theme.
You can also set attributes to a specific widget using its name or its class attribute.
ui.json
:
{
"doctype":"ui-definition",
"main":{
"tk.Frame":{
"bg":"#2A2A2A",
"name":"frame1",
"pack_options":{
"fill":"both",
"expand":"1"
},
"tk.Entry":{
"name":"entry1",
"class":"my_class"
},
"tk.Button":{
"text":"Click me !!!",
"border":"0",
"name":"clickme_button",
"class":"my_class"
}
}
}
}
theme.json
:
{
"doctype":"theme",
".my_class":{
"bg":"blue",
"fg":"white"
},
"#frame1":{
"bg":"green"
}
}
This is the final result, once we generated both files :
To change the window attributes, use window
instead of the widget's name.
There are several options to customize the window's border.
You can set a theme to the border in your JSON theme file using the border
keyword and by specifying attributes for each border's subwidgets :
{
"doctype":"theme",
"border":{
"border":{
"bg":"green"
},
"maximize": {
"fg": "white",
"hoverfg": "white",
"hoverbg": "darkgreen",
"bg": "green"
},
"minimize": {
"fg": "white",
"hoverfg": "white",
"hoverbg": "darkgreen",
"bg": "green"
},
"close": {
"fg": "white",
"hoverfg": "white",
"hoverbg": "red",
"bg": "green"
}
}
}
You can also set those attributes using the config_border
border method of your Tk
object by passing a dictionnary as parameter.
This is what we get:
The last way to customize the window's border is to create your own border.
Here is an example of a custom window's border with only one close button and a title:
{
"doctype":"ui-definition",
"main":{
"tk.Frame":{
"bg":"#2A2A2A",
"name":"border",
"pack_options":{
"fill":"x",
"side":"top"
},
"tk.Label":{
"name":"window_title",
"pack_options":{
"side":"left",
"padx":"(10, 0)"
},
"bg":"#2A2A2A",
"fg":"white",
"text":"My custom border"
},
"tk.Button":{
"name":"close",
"pack_options":{
"side":"right"
},
"bg":"red",
"border":"0",
"fg":"white",
"text":"X",
"font":"Consolas, 15"
}
}
}
}
Because we create our own border, we may not display the default window's border. It is very easy, just set defaultBorder
to False
when you create your Tk
object :
root = tkxui.Tk(display=tkxui.FRAMELESS, defaultBorder=False)
Then, we need to make the window draggable, simply create a FramelessDragBehaviour
object on top of our window and our draggable widget, the border frame :
root = tkxui.Tk(display=tkxui.FRAMELESS, defaultBorder=False) # Create the window
json_loader = tkxui.JSONLoader(root) # Create the JSON file loader
json_loader.generate("ui.json") # Now we have access to our border widget
custom_border_draggable_behaviour = tkxui.FramelessDragBehaviour(root, root.border) # Now the window is draggable with the border frame
Finally, you need to make sure the close button can close the window, simply add a command to it :
root.close.config(
command=lambda:root.destroy()
)
By default a frameless window is reproducing Windows border events (for instance, when you drag a window to the top it will go fullscreen). You can disable that option by calling the disable_winbehaviours
method on your window's object :
root.disable_winbehaviours()
To get back to default border, simply do this :
root.enable_winbehaviours()