Welcome, guest | Sign In | My Account | Store | Cart
# Author: Miguel Martinez Lopez

try:
    import Tkinter as tk
    from Tkconstants import *    
except ImportError:
    import tkinter as tk
    from tkinter.constants import *

import base64

# python 2 and 3 compatibility
try:
  basestring
except NameError:
  basestring = str

def center_toplevel(toplevel):
    x = (toplevel.winfo_screenwidth() - toplevel.winfo_reqwidth())/2
    y = (toplevel.winfo_screenheight() - toplevel.winfo_height())/2

    toplevel.geometry("+%d+%d" % (x, y))

class Draggable_Window(tk.Toplevel):

    def __init__(self, master=None, disable_dragging =False, release_command = None):
        tk.Toplevel.__init__(self, master)

        if disable_dragging == False:
            self.bind('<Button-1>', self.initiate_motion)
            self.bind('<ButtonRelease-1>', self.release_dragging)

        self.release_command = release_command

    def initiate_motion(self, event):
        # This is another possibility:
        #   OriX, OriY = self.window_position()
        #   self.deltaX = event.x_root - OriX
        #   self.deltaY = event.y_root - OriY
        #
        self.deltaX = event.x_root - self.winfo_x()
        self.deltaY = event.y_root - self.winfo_y()

        self.bind('<Motion>', self.drag_window)

    def drag_window(self, event):
        new_x = event.x_root - self.deltaX
        new_y = event.y_root - self.deltaY

        if new_x < 0 :
            new_x = 0

        if new_y < 0 :
            new_y = 0

        self.wm_geometry("+%s+%s" % (new_x, new_y))

    def release_dragging(self, event):
        self.unbind('<Motion>')

        if self.release_command != None :
            self.release_command()

    def disable_dragging(self) :
        self.unbind('<Button-1>')
        self.unbind('<ButtonRelease-1>')
        self.unbind('<Motion>')

    def enable_dragging(self):
        self.bind('<Button-1>', self.initiate_motion)
        self.bind('<ButtonRelease-1>', self.release_dragging)

class Control_Button(tk.Label):
    def __init__(self, master, active_background=None, active_foreground=None, command=None, **kwargs):
        tk.Label.__init__(self, master, **kwargs)
        
        self._active_background = active_background
        self._active_foreground = active_foreground
        self._background = self.cget("background")
        self._foreground = self.cget("foreground")
        
        self.bind("<Enter>", self._on_enter)
        self.bind("<Leave>", self._on_leave)
            
        if command is not None:
            self.bind("<1>", lambda event: command())
            
    def _on_enter(self, event):
        self.configure(background=self._active_background, foreground=self._active_foreground)

    def _on_leave(self, event):
        self.configure(background=self._background, foreground=self._foreground)

class Metro_Dialog(Draggable_Window):
    WINDOW_ICON = "I2RlZmluZSBpbWFnZV93aWR0aCAxOQojZGVmaW5lIGltYWdlX2hlaWdodCAxOQpzdGF0aWMgY2hhciBpbWFnZV9iaXRzW10gPSB7CjB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAyLDB4MDAsMHhmYywweDAzLDB4NzgsMHhmZiwweDAzLDB4N2YsMHhmZiwweDAzLAoweDdmLDB4ZmYsMHgwMywweDdmLDB4ZmYsMHgwMywweDdmLDB4ZmYsMHgwMywweDdmLDB4ZmYsMHgwMywweDdmLDB4ZmYsMHgwMywKMHgwMCwweDAwLDB4MDAsMHg3ZiwweGZmLDB4MDMsMHg3ZiwweGZmLDB4MDMsMHg3ZiwweGZmLDB4MDMsMHg3ZiwweGZmLDB4MDMsCjB4N2YsMHhmZiwweDAzLDB4N2YsMHhmZiwweDAzLDB4NDAsMHhmZiwweDAzLDB4MDAsMHhlMCwweDAzCn07"
    CLOSE_ICON = "I2RlZmluZSB4Ml93aWR0aCA0NQojZGVmaW5lIHgyX2hlaWdodCAyMApzdGF0aWMgY2hhciB4Ml9iaXRzW10gPSB7CiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgxOCwgMHgwNiwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgzMCwgMHgwMywgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHhFMCwgMHgwMSwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHhDMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHhFMCwgMHgwMSwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgzMCwgMHgwMywgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgxOCwgMHgwNiwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgfTsK"
    MAXIMIZE_ICON = "I2RlZmluZSBtYXhpbWl6ZV93aWR0aCAyMAojZGVmaW5lIG1heGltaXplX2hlaWdodCAyMApzdGF0aWMgY2hhciBtYXhpbWl6ZV9iaXRzW10gPSB7CiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHhFMCwgMHg3RiwgMHgwMCwgMHhFMCwgMHg3RiwgMHgwMCwgCiAgMHgyMCwgMHg0MCwgMHgwMCwgMHgyMCwgMHg0MCwgMHgwMCwgMHgyMCwgMHg0MCwgMHgwMCwgMHgyMCwgMHg0MCwgMHgwMCwgCiAgMHgyMCwgMHg0MCwgMHgwMCwgMHhFMCwgMHg3RiwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgfTsK"
    MINIMIZE_ICON = "I2RlZmluZSBtaW5pbWl6ZV93aWR0aCAyMAojZGVmaW5lIG1pbmltaXplX2hlaWdodCAyMApzdGF0aWMgY2hhciBtaW5pbWl6ZV9iaXRzW10gPSB7CiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHhDMCwgMHgzRiwgMHgwMCwgMHhDMCwgMHgzRiwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgfTsK"
    RESTORE_ICON = "I2RlZmluZSByZXN0b3JlX3dpZHRoIDIwCiNkZWZpbmUgcmVzdG9yZV9oZWlnaHQgMjAKc3RhdGljIGNoYXIgcmVzdG9yZV9iaXRzW10gPSB7CiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHg4MCwgMHg3RiwgMHgwMCwgMHgwMCwgMHg0MCwgMHgwMCwgMHhFMCwgMHg1RiwgMHgwMCwgCiAgMHhFMCwgMHg1RiwgMHgwMCwgMHgyMCwgMHg1OCwgMHgwMCwgMHgyMCwgMHg1MCwgMHgwMCwgMHgyMCwgMHg1MCwgMHgwMCwgCiAgMHgyMCwgMHg1MCwgMHgwMCwgMHgyMCwgMHgxMCwgMHgwMCwgMHhFMCwgMHgxRiwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgCiAgfTsK"

    HEADER_BACKGROUND = "brown"
    HEADER_FOREGROUND = "white"
    ICON_FOREGROUND = "white"
    BUTTON_ACTIVEBACKGROUND = "#CDE6F7"
    BUTTON_ACTIVEFOREGROUND = "#2A8DD4"
    BUTTON_BACKGROUND = "white"
    BUTTON_FOREGROUND = "#777777"
    BODY_BACKGROUND = "white"
    BORDERWIDTH = 2

    def __init__(self, master, title, width=None, height=None, header_background=None, header_foreground=None, icon_foreground=None, button_activebackground=None, button_activeforeground=None, button_background=None, button_foreground=None ,body_background=None, borderwidth=None, maximize_button=True, minimize_button=True, close_button=True, icon=None, center_on_screen=False):
        if header_background is None:
            header_background = self.HEADER_BACKGROUND
        
        if header_foreground is None:
            header_foreground = self.HEADER_FOREGROUND
            
        if icon_foreground is None:
            icon_foreground = self.ICON_FOREGROUND
            
        if button_activebackground is None:
           button_activebackground = self.BUTTON_ACTIVEBACKGROUND
        
        if button_activeforeground is None:
            button_activeforeground = self.BUTTON_ACTIVEFOREGROUND
        
        if button_background is None:
            button_background = self.BUTTON_BACKGROUND
        
        if button_foreground is None:
            button_foreground = self.BUTTON_FOREGROUND
        
        if body_background is None:
            body_background = self.BODY_BACKGROUND
            
        if borderwidth is None:
            borderwidth = self.BORDERWIDTH

        Draggable_Window.__init__(self, master)

        self.wm_attributes('-topmost', True)
        self.overrideredirect(True)
        
        outer_frame = tk.Frame(self, highlightbackground=header_background, highlightcolor=header_background, highlightthickness=borderwidth)
        outer_frame.pack(expand=True, fill=BOTH)

        self._header = tk.Frame(outer_frame, background=header_background, padx=6, pady=6)
        self._header.pack(fill=X)

        if icon is None:
            if isinstance(Metro_Dialog.WINDOW_ICON, basestring):
                data = base64.b64decode(Metro_Dialog.WINDOW_ICON)
                Metro_Dialog.WINDOW_ICON = tk.BitmapImage(data=data)
                
            icon = Metro_Dialog.WINDOW_ICON

        if isinstance(icon, tk.BitmapImage):
            icon.configure(foreground=icon_foreground)

        if close_button:
            if isinstance(Metro_Dialog.CLOSE_ICON, basestring):
                data = base64.b64decode(Metro_Dialog.CLOSE_ICON)
                Metro_Dialog.CLOSE_ICON = tk.BitmapImage(data=data, foreground=button_foreground)
    
            self._close_button = Control_Button(self._header, foreground=button_foreground, background=button_background, active_background=button_activebackground, image=Metro_Dialog.CLOSE_ICON, command=self.close)
            self._close_button.pack(side=RIGHT, padx=(1,0))

        if maximize_button:
            if isinstance(Metro_Dialog.MAXIMIZE_ICON, basestring):
                data = base64.b64decode(Metro_Dialog.MAXIMIZE_ICON)
                Metro_Dialog.MAXIMIZE_ICON = tk.BitmapImage(data=data, foreground=button_foreground)
            
            self._maximize_button = Control_Button(self._header, foreground=button_foreground, background=button_background, active_background=button_activebackground, image=Metro_Dialog.MAXIMIZE_ICON, command=self.maximize)
            self._maximize_button.pack(side=RIGHT, padx=(1,0))

        if minimize_button:
            if isinstance(Metro_Dialog.MINIMIZE_ICON, basestring):
                data = base64.b64decode(Metro_Dialog.MINIMIZE_ICON)
                Metro_Dialog.MINIMIZE_ICON = tk.BitmapImage(data=data, foreground=button_foreground)

            self._minimize_button = Control_Button(self._header, foreground=button_foreground, background=button_background, active_background=button_activebackground, active_foreground=button_activeforeground, image=Metro_Dialog.MINIMIZE_ICON, command=self.minimize)
            self._minimize_button.pack(side=RIGHT, padx=(1,0))

        self._borderwidth = borderwidth

        self._icon_label = tk.Label(self._header, image=icon, background=header_background)
        self._icon_label.pack(side=LEFT)

        self._title = title
        self._title_label = tk.Label(self._header, text=title, foreground=header_foreground, background=header_background)
        self._title_label.pack(side=LEFT, padx=6)
        
        self.body = tk.Frame(outer_frame, padx=4, pady=4, background = body_background)
        self.body.pack(expand=True, fill=BOTH)

        self._width = width
        self._height = height
        
        self._is_maximized = True

        if center_on_screen:
            self.center_on_screen()

    def close(self):
        self.destroy()

    def maximize(self):
        if self._is_maximized: return

        self.body.pack(expand=True, fill=BOTH)
        self.geometry("%dx%d"%(self._width, self._height))
        
        self._is_maximized = True

    def minimize(self):
        if not self._is_maximized: return

        if self._width is None:
            self._width = self.winfo_width()
            
        if self._height is None:
            self._height = self.winfo_height()

        width = self._width
        height = self._header.winfo_reqheight() + 2*self._borderwidth

        self.body.pack_forget()
        self.geometry("%dx%d"%(width,height))
        
        self._is_maximized = False

    def set_position(self, x, y):
        self.geometry("+%d+%d" % (x, y))

    def center_on_screen(self):
        center_toplevel(self)

if __name__ == "__main__":
    try:
        from Tkinter import Tk
    except ImportError:
        from tkinter import Tk

    root = Tk()
    dialog = Metro_Dialog(root, title="This is my title", center_on_screen=True)
    tk.Label(dialog.body, background="white", text="this is a label").pack()

    root.mainloop()

Diff to Previous Revision

--- revision 4 2017-04-10 22:30:33
+++ revision 5 2017-04-11 11:23:46
@@ -106,8 +106,9 @@
     BUTTON_BACKGROUND = "white"
     BUTTON_FOREGROUND = "#777777"
     BODY_BACKGROUND = "white"
-
-    def __init__(self, master, title, width=None, height=None, header_background=None, header_foreground=None, icon_foreground=None, button_activebackground=None, button_activeforeground=None, button_background=None, button_foreground=None ,body_background=None, maximize_button=True, minimize_button=True, close_button=True, icon=None, center_on_screen=False):
+    BORDERWIDTH = 2
+
+    def __init__(self, master, title, width=None, height=None, header_background=None, header_foreground=None, icon_foreground=None, button_activebackground=None, button_activeforeground=None, button_background=None, button_foreground=None ,body_background=None, borderwidth=None, maximize_button=True, minimize_button=True, close_button=True, icon=None, center_on_screen=False):
         if header_background is None:
             header_background = self.HEADER_BACKGROUND
         
@@ -131,13 +132,16 @@
         
         if body_background is None:
             body_background = self.BODY_BACKGROUND
+            
+        if borderwidth is None:
+            borderwidth = self.BORDERWIDTH
 
         Draggable_Window.__init__(self, master)
 
         self.wm_attributes('-topmost', True)
         self.overrideredirect(True)
         
-        outer_frame = tk.Frame(self, highlightbackground=header_background, highlightcolor=header_background, highlightthickness=1,)
+        outer_frame = tk.Frame(self, highlightbackground=header_background, highlightcolor=header_background, highlightthickness=borderwidth)
         outer_frame.pack(expand=True, fill=BOTH)
 
         self._header = tk.Frame(outer_frame, background=header_background, padx=6, pady=6)
@@ -159,7 +163,7 @@
                 Metro_Dialog.CLOSE_ICON = tk.BitmapImage(data=data, foreground=button_foreground)
     
             self._close_button = Control_Button(self._header, foreground=button_foreground, background=button_background, active_background=button_activebackground, image=Metro_Dialog.CLOSE_ICON, command=self.close)
-            self._close_button.pack(side=RIGHT, padx=(2,0))
+            self._close_button.pack(side=RIGHT, padx=(1,0))
 
         if maximize_button:
             if isinstance(Metro_Dialog.MAXIMIZE_ICON, basestring):
@@ -167,7 +171,7 @@
                 Metro_Dialog.MAXIMIZE_ICON = tk.BitmapImage(data=data, foreground=button_foreground)
             
             self._maximize_button = Control_Button(self._header, foreground=button_foreground, background=button_background, active_background=button_activebackground, image=Metro_Dialog.MAXIMIZE_ICON, command=self.maximize)
-            self._maximize_button.pack(side=RIGHT, padx=(2,0))
+            self._maximize_button.pack(side=RIGHT, padx=(1,0))
 
         if minimize_button:
             if isinstance(Metro_Dialog.MINIMIZE_ICON, basestring):
@@ -175,7 +179,9 @@
                 Metro_Dialog.MINIMIZE_ICON = tk.BitmapImage(data=data, foreground=button_foreground)
 
             self._minimize_button = Control_Button(self._header, foreground=button_foreground, background=button_background, active_background=button_activebackground, active_foreground=button_activeforeground, image=Metro_Dialog.MINIMIZE_ICON, command=self.minimize)
-            self._minimize_button.pack(side=RIGHT, padx=(2,0))
+            self._minimize_button.pack(side=RIGHT, padx=(1,0))
+
+        self._borderwidth = borderwidth
 
         self._icon_label = tk.Label(self._header, image=icon, background=header_background)
         self._icon_label.pack(side=LEFT)
@@ -216,7 +222,7 @@
             self._height = self.winfo_height()
 
         width = self._width
-        height = self._header.winfo_reqheight()
+        height = self._header.winfo_reqheight() + 2*self._borderwidth
 
         self.body.pack_forget()
         self.geometry("%dx%d"%(width,height))

History