1
+ import tkinter
2
+ import sys
3
+
4
+
5
+ class TkinterCustomButton (tkinter .Frame ):
6
+ """ tkinter custom button with border, rounded corners and hover effect
7
+ Arguments: master= where to place button
8
+ bg_color= background color, None is standard,
9
+ fg_color= foreground color, blue is standard,
10
+ hover_color= foreground color, lightblue is standard,
11
+ border_color= foreground color, None is standard,
12
+ border_width= border thickness, 0 is standard,
13
+ command= callback function, None is standard,
14
+ width= width of button, 110 is standard,
15
+ height= width of button, 35 is standard,
16
+ corner_radius= corner radius, 10 is standard,
17
+ text_font= (<Name>, <Size>),
18
+ text_color= text color, white is standard,
19
+ text= text of button,
20
+ hover= hover effect, True is standard,
21
+ image= PIL.PhotoImage, standard is None"""
22
+
23
+ def __init__ (self ,
24
+ bg_color = None ,
25
+ fg_color = "#2874A6" ,
26
+ hover_color = "#5499C7" ,
27
+ border_color = None ,
28
+ border_width = 0 ,
29
+ command = None ,
30
+ width = 120 ,
31
+ height = 40 ,
32
+ corner_radius = 10 ,
33
+ text_font = None ,
34
+ text_color = "white" ,
35
+ text = "CustomButton" ,
36
+ hover = True ,
37
+ image = None ,
38
+ * args , ** kwargs ):
39
+ super ().__init__ (* args , ** kwargs )
40
+
41
+ if bg_color is None :
42
+ self .bg_color = self .master .cget ("bg" )
43
+ else :
44
+ self .bg_color = bg_color
45
+
46
+ self .fg_color = fg_color
47
+ self .hover_color = hover_color
48
+ self .border_color = border_color
49
+
50
+ self .width = width
51
+ self .height = height
52
+
53
+ if corner_radius * 2 > self .height :
54
+ self .corner_radius = self .height / 2
55
+ elif corner_radius * 2 > self .width :
56
+ self .corner_radius = self .width / 2
57
+ else :
58
+ self .corner_radius = corner_radius
59
+
60
+ self .border_width = border_width
61
+
62
+ if self .corner_radius >= self .border_width :
63
+ self .inner_corner_radius = self .corner_radius - self .border_width
64
+ else :
65
+ self .inner_corner_radius = 0
66
+
67
+ self .text = text
68
+ self .text_color = text_color
69
+ if text_font is None :
70
+ if sys .platform == "darwin" : # macOS
71
+ self .text_font = ("Avenir" , 13 )
72
+ elif "win" in sys .platform : # Windows
73
+ self .text_font = ("Century Gothic" , 11 )
74
+ else :
75
+ self .text_font = ("TkDefaultFont" )
76
+ else :
77
+ self .text_font = text_font
78
+
79
+ self .image = image
80
+
81
+ self .function = command
82
+ self .hover = hover
83
+
84
+ self .configure (width = self .width , height = self .height )
85
+
86
+ if sys .platform == "darwin" and self .function is not None :
87
+ self .configure (cursor = "pointinghand" )
88
+
89
+ self .canvas = tkinter .Canvas (master = self ,
90
+ highlightthicknes = 0 ,
91
+ background = self .bg_color ,
92
+ width = self .width ,
93
+ height = self .height )
94
+ self .canvas .place (x = 0 , y = 0 )
95
+
96
+ if self .hover is True :
97
+ self .canvas .bind ("<Enter>" , self .on_enter )
98
+ self .canvas .bind ("<Leave>" , self .on_leave )
99
+
100
+ self .canvas .bind ("<Button-1>" , self .clicked )
101
+ self .canvas .bind ("<Button-1>" , self .clicked )
102
+
103
+ self .canvas_fg_parts = []
104
+ self .canvas_border_parts = []
105
+ self .text_part = None
106
+ self .text_label = None
107
+ self .image_label = None
108
+
109
+ self .draw ()
110
+
111
+ def draw (self ):
112
+ self .canvas .delete ("all" )
113
+ self .canvas_fg_parts = []
114
+ self .canvas_border_parts = []
115
+ self .canvas .configure (bg = self .bg_color )
116
+
117
+ # border button parts
118
+ if self .border_width > 0 :
119
+
120
+ if self .corner_radius > 0 :
121
+ self .canvas_border_parts .append (self .canvas .create_oval (0 ,
122
+ 0 ,
123
+ self .corner_radius * 2 ,
124
+ self .corner_radius * 2 ))
125
+ self .canvas_border_parts .append (self .canvas .create_oval (self .width - self .corner_radius * 2 ,
126
+ 0 ,
127
+ self .width ,
128
+ self .corner_radius * 2 ))
129
+ self .canvas_border_parts .append (self .canvas .create_oval (0 ,
130
+ self .height - self .corner_radius * 2 ,
131
+ self .corner_radius * 2 ,
132
+ self .height ))
133
+ self .canvas_border_parts .append (self .canvas .create_oval (self .width - self .corner_radius * 2 ,
134
+ self .height - self .corner_radius * 2 ,
135
+ self .width ,
136
+ self .height ))
137
+
138
+ self .canvas_border_parts .append (self .canvas .create_rectangle (0 ,
139
+ self .corner_radius ,
140
+ self .width ,
141
+ self .height - self .corner_radius ))
142
+ self .canvas_border_parts .append (self .canvas .create_rectangle (self .corner_radius ,
143
+ 0 ,
144
+ self .width - self .corner_radius ,
145
+ self .height ))
146
+
147
+ # inner button parts
148
+
149
+ if self .corner_radius > 0 :
150
+ self .canvas_fg_parts .append (self .canvas .create_oval (self .border_width ,
151
+ self .border_width ,
152
+ self .border_width + self .inner_corner_radius * 2 ,
153
+ self .border_width + self .inner_corner_radius * 2 ))
154
+ self .canvas_fg_parts .append (self .canvas .create_oval (self .width - self .border_width - self .inner_corner_radius * 2 ,
155
+ self .border_width ,
156
+ self .width - self .border_width ,
157
+ self .border_width + self .inner_corner_radius * 2 ))
158
+ self .canvas_fg_parts .append (self .canvas .create_oval (self .border_width ,
159
+ self .height - self .border_width - self .inner_corner_radius * 2 ,
160
+ self .border_width + self .inner_corner_radius * 2 ,
161
+ self .height - self .border_width ))
162
+ self .canvas_fg_parts .append (self .canvas .create_oval (self .width - self .border_width - self .inner_corner_radius * 2 ,
163
+ self .height - self .border_width - self .inner_corner_radius * 2 ,
164
+ self .width - self .border_width ,
165
+ self .height - self .border_width ))
166
+
167
+ self .canvas_fg_parts .append (self .canvas .create_rectangle (self .border_width + self .inner_corner_radius ,
168
+ self .border_width ,
169
+ self .width - self .border_width - self .inner_corner_radius ,
170
+ self .height - self .border_width ))
171
+ self .canvas_fg_parts .append (self .canvas .create_rectangle (self .border_width ,
172
+ self .border_width + self .inner_corner_radius ,
173
+ self .width - self .border_width ,
174
+ self .height - self .inner_corner_radius - self .border_width ))
175
+
176
+ for part in self .canvas_fg_parts :
177
+ self .canvas .itemconfig (part , fill = self .fg_color , width = 0 )
178
+
179
+ for part in self .canvas_border_parts :
180
+ self .canvas .itemconfig (part , fill = self .border_color , width = 0 )
181
+
182
+ # no image given
183
+ if self .image is None :
184
+ # create tkinter.Label with text
185
+ self .text_label = tkinter .Label (master = self ,
186
+ text = self .text ,
187
+ font = self .text_font ,
188
+ bg = self .fg_color ,
189
+ fg = self .text_color )
190
+ self .text_label .place (relx = 0.5 , rely = 0.5 , anchor = tkinter .CENTER )
191
+
192
+ # bind events the the button click and hover events also to the text_label
193
+ if self .hover is True :
194
+ self .text_label .bind ("<Enter>" , self .on_enter )
195
+ self .text_label .bind ("<Leave>" , self .on_leave )
196
+
197
+ self .text_label .bind ("<Button-1>" , self .clicked )
198
+ self .text_label .bind ("<Button-1>" , self .clicked )
199
+
200
+ self .set_text (self .text )
201
+
202
+ # use the given image
203
+ else :
204
+ # create tkinter.Label with image on it
205
+ self .image_label = tkinter .Label (master = self ,
206
+ image = self .image ,
207
+ bg = self .fg_color )
208
+
209
+ self .image_label .place (relx = 0.5 ,
210
+ rely = 0.5 ,
211
+ anchor = tkinter .CENTER )
212
+
213
+ # bind events the the button click and hover events also to the image_label
214
+ if self .hover is True :
215
+ self .image_label .bind ("<Enter>" , self .on_enter )
216
+ self .image_label .bind ("<Leave>" , self .on_leave )
217
+
218
+ self .image_label .bind ("<Button-1>" , self .clicked )
219
+ self .image_label .bind ("<Button-1>" , self .clicked )
220
+
221
+ def configure_color (self , bg_color = None , fg_color = None , hover_color = None , text_color = None ):
222
+ if bg_color is not None :
223
+ self .bg_color = bg_color
224
+ else :
225
+ self .bg_color = self .master .cget ("bg" )
226
+
227
+ if fg_color is not None :
228
+ self .fg_color = fg_color
229
+
230
+ # change background color of image_label
231
+ if self .image is not None :
232
+ self .image_label .configure (bg = self .fg_color )
233
+
234
+ if hover_color is not None :
235
+ self .hover_color = hover_color
236
+
237
+ if text_color is not None :
238
+ self .text_color = text_color
239
+ if self .text_part is not None :
240
+ self .canvas .itemconfig (self .text_part , fill = self .text_color )
241
+
242
+ self .draw ()
243
+
244
+ def set_text (self , text ):
245
+ if self .text_label is not None :
246
+ self .text_label .configure (text = text )
247
+
248
+ def on_enter (self , event = 0 ):
249
+ for part in self .canvas_fg_parts :
250
+ self .canvas .itemconfig (part , fill = self .hover_color , width = 0 )
251
+
252
+ if self .text_label is not None :
253
+ # change background color of image_label
254
+ self .text_label .configure (bg = self .hover_color )
255
+
256
+ if self .image_label is not None :
257
+ # change background color of image_label
258
+ self .image_label .configure (bg = self .hover_color )
259
+
260
+ def on_leave (self , event = 0 ):
261
+ for part in self .canvas_fg_parts :
262
+ self .canvas .itemconfig (part , fill = self .fg_color , width = 0 )
263
+
264
+ if self .text_label is not None :
265
+ # change background color of image_label
266
+ self .text_label .configure (bg = self .fg_color )
267
+
268
+ if self .image_label is not None :
269
+ # change background color of image_label
270
+ self .image_label .configure (bg = self .fg_color )
271
+
272
+ def clicked (self , event = 0 ):
273
+ if self .function is not None :
274
+ self .function ()
275
+ self .on_leave ()
0 commit comments