Magic Mouse

This application demonstrates a complicated design with many options and several label frames. The overall theme is lumen. Other than the default styles, the following styles are applied directly to various widgets widgets:

Image Buttons

Link.TButton

License Number

primary.TLabel

../_images/magic_mouse.png
"""
    Author: Israel Dryer
    Modified: 2021-04-13
    Adapted for ttkbootstrap from: https://magicutilities.net/magic-mouse/features
"""
import tkinter
from tkinter import PhotoImage
from tkinter import ttk
from tkinter.messagebox import showinfo
from pathlib import Path
from ttkbootstrap import Style


class Application(tkinter.Tk):

    def __init__(self):
        super().__init__()
        self.title('Magic Mouse')
        self.style = Style('lumen')
        self.window = ttk.Frame(self)
        self.window.pack(fill='both', expand='yes')
        self.nb = ttk.Notebook(self.window)
        self.nb.pack(fill='both', expand='yes', padx=5, pady=5)
        mu = MouseUtilities(self.nb)
        self.nb.add(mu, text='Mouse 1')

        # add demo tabs
        self.nb.add(ttk.Frame(self.nb), text='Mouse 2')
        self.nb.add(ttk.Frame(self.nb), text='Mouse 3')


class MouseUtilities(ttk.Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        p = Path(__file__).parent
        self.images = {
            'reset': PhotoImage(name='reset', file=p/'assets/magic_mouse/icons8_reset_24px.png'),
            'reset-small': PhotoImage(name='reset-small', file=p/'assets/magic_mouse/icons8_reset_16px.png'),
            'submit': PhotoImage(name='submit', file=p/'assets/magic_mouse/icons8_submit_progress_24px.png'),
            'question': PhotoImage(name='question', file=p/'assets/magic_mouse/icons8_question_mark_16px.png'),
            'direction': PhotoImage(name='direction', file=p/'assets/magic_mouse/icons8_move_16px.png'),
            'bluetooth': PhotoImage(name='bluetooth', file=p/'assets/magic_mouse/icons8_bluetooth_2_16px.png'),
            'buy': PhotoImage(name='buy', file=p/'assets/magic_mouse/icons8_buy_26px_2.png'),
            'mouse': PhotoImage(name='mouse', file=p/'assets/magic_mouse/magic_mouse.png')
        }

        for i in range(3):
            self.columnconfigure(i, weight=1)
        self.rowconfigure(0, weight=1)

        # Column 1 =====================================================================================================
        col1 = ttk.Frame(self, padding=10)
        col1.grid(row=0, column=0, sticky='news')

        ## device info -------------------------------------------------------------------------------------------------
        dev_info = ttk.Labelframe(col1, text='Device Info', padding=10)
        dev_info.pack(side='top', fill='both', expand='yes')

        ### header
        dev_info_header = ttk.Frame(dev_info, padding=5)
        dev_info_header.pack(fill='x')
        ttk.Button(dev_info_header, image='reset', style='Link.TButton', command=self.callback).pack(side='left')
        ttk.Label(dev_info_header, text='Model 2009, 2xAA Batteries').pack(side='left', fill='x', padx=15)
        ttk.Button(dev_info_header, image='submit', style='Link.TButton', command=self.callback).pack(side='left')

        ### image
        ttk.Label(dev_info, image='mouse').pack(fill='x')

        ### progressbar
        pb = ttk.Progressbar(dev_info, value=66)  # also used as a container for the % complete label
        pb.pack(fill='x', pady=5, padx=5)
        ttk.Label(pb, text='66%', style='primary.Invert.TLabel').pack()

        ### progress message
        self.setvar('progress', 'Battery is discharging.')
        ttk.Label(dev_info, textvariable='progress', font='Helvetica 8', anchor='center').pack(fill='x')

        ## licence info ------------------------------------------------------------------------------------------------
        lic_info = ttk.Labelframe(col1, text='License Info', padding=20)
        lic_info.pack(side='top', fill='both', expand='yes', pady=(10, 0))
        lic_info.rowconfigure(0, weight=1)
        lic_info.columnconfigure(0, weight=2)
        lic_title = ttk.Label(lic_info, text='Trial Version, 28 days left', anchor='center')
        lic_title.pack(fill='x', pady=(0, 20))
        ttk.Label(lic_info, text='Mouse serial number:', anchor='center', font='Helvetica 8').pack(fill='x')
        self.setvar('license', 'dtMM2-XYZGHIJKLMN3')
        lic_num = ttk.Label(lic_info, textvariable='license', style='primary.TLabel', anchor='center')
        lic_num.pack(fill='x', pady=(0, 20))
        buy_now = ttk.Button(lic_info, image='buy', text='Buy now', compound='bottom', command=self.callback)
        buy_now.pack(padx=10, fill='x')

        # Column 2 =====================================================================================================
        col2 = ttk.Frame(self, padding=10)
        col2.grid(row=0, column=1, sticky='news')

        ## scrolling ---------------------------------------------------------------------------------------------------
        scrolling = ttk.Labelframe(col2, text='Scrolling', padding=(15, 10))
        scrolling.pack(side='top', fill='both', expand='yes')

        op1 = ttk.Checkbutton(scrolling, text='Scrolling', variable='op1')
        op1.pack(fill='x', pady=5)

        ### no horizontal scrolling
        op2 = ttk.Checkbutton(scrolling, text='No horizontal scrolling', variable='op2')
        op2.pack(fill='x', padx=(20, 0), pady=5)
        ttk.Button(op2, image='question', style='Link.TButton', command=self.callback).pack(side='right')

        ### inverse
        op3 = ttk.Checkbutton(scrolling, text='Inverse scroll directcion vertically', variable='op3')
        op3.pack(fill='x', padx=(20, 0), pady=5)
        ttk.Button(op3, image='direction', style='Link.TButton', command=self.callback).pack(side='right')

        ### Scroll only vertical or horizontal
        op4 = ttk.Checkbutton(scrolling, text='Scroll only vertical or horizontal', state='disabled')
        op4.configure(variable='op4')
        op4.pack(fill='x', padx=(20, 0), pady=5)

        ### smooth scrolling
        op5 = ttk.Checkbutton(scrolling, text='Smooth scrolling', variable='op5')
        op5.pack(fill='x', padx=(20, 0), pady=5)
        ttk.Button(op5, image='bluetooth', style='Link.TButton', command=self.callback).pack(side='right')

        ### scroll speed
        scroll_speed_frame = ttk.Frame(scrolling)
        scroll_speed_frame.pack(fill='x', padx=(20, 0), pady=5)
        ttk.Label(scroll_speed_frame, text='Speed:').pack(side='left')
        ttk.Scale(scroll_speed_frame, value=35, from_=1, to=100).pack(side='left', fill='x', expand='yes', padx=5)
        scroll_speed_btn = ttk.Button(scroll_speed_frame, image='reset-small', style='Link.TButton')
        scroll_speed_btn.configure(command=self.callback)
        scroll_speed_btn.pack(side='left')

        ### scroll sense
        scroll_sense_frame = ttk.Frame(scrolling)
        scroll_sense_frame.pack(fill='x', padx=(20, 0), pady=(5, 0))
        ttk.Label(scroll_sense_frame, text='Sense:').pack(side='left')
        ttk.Scale(scroll_sense_frame, value=50, from_=1, to=100).pack(side='left', fill='x', expand='yes', padx=5)
        scroll_sense_btn = ttk.Button(scroll_sense_frame, image='reset-small', style='Link.TButton')
        scroll_sense_btn.configure(command=self.callback)
        scroll_sense_btn.pack(side='left')

        ## 1 finger gestures -------------------------------------------------------------------------------------------
        finger_gest = ttk.Labelframe(col2, text='1 Finger Gestures', padding=(15, 10))
        finger_gest.pack(side='top', fill='both', expand='yes', pady=(10, 0))

        op6 = ttk.Checkbutton(finger_gest, text='Fast swipe left/right to navigate back/forward', variable='op6')
        op6.pack(fill='x', pady=5)
        ttk.Checkbutton(finger_gest, text='Swap swipe direction', variable='op7').pack(fill='x', padx=(20, 0), pady=5)

        ### gest sense
        gest_sense_frame = ttk.Frame(finger_gest)
        gest_sense_frame.pack(fill='x', padx=(20, 0), pady=(5, 0))

        ttk.Label(gest_sense_frame, text='Sense:').pack(side='left')

        ttk.Scale(gest_sense_frame, value=50, from_=1, to=100).pack(side='left', fill='x', expand='yes', padx=5)

        gest_sense_btn = ttk.Button(gest_sense_frame, image='reset-small', style='Link.TButton')
        gest_sense_btn.configure(command=self.callback)
        gest_sense_btn.pack(side='left')

        ## middle click ------------------------------------------------------------------------------------------------
        middle_click = ttk.Labelframe(col2, text='Middle Click', padding=(15, 10))
        middle_click.pack(side='top', fill='both', expand='yes', pady=(10, 0))

        cbo = ttk.Combobox(middle_click, values=['Any 2 finger click', 'Other 1', 'Other 2'])
        cbo.current(0)
        cbo.pack(fill='x')

        # Column 3 =====================================================================================================
        col3 = ttk.Frame(self, padding=10)
        col3.grid(row=0, column=2, sticky='news')

        ## two finger gestures -----------------------------------------------------------------------------------------
        two_finger_gest = ttk.Labelframe(col3, text='2 Finger Gestures', padding=10)
        two_finger_gest.pack(side='top', fill='both')

        op7 = ttk.Checkbutton(two_finger_gest, text='Fast swipe left/right to navigate back/forward', variable='op7')
        op7.pack(fill='x', pady=5)

        op8 = ttk.Checkbutton(two_finger_gest, text='Swap swipe direction', variable='op8')
        op8.pack(fill='x', padx=(20, 0), pady=5)

        ### gest sense
        gest_sense_frame = ttk.Frame(two_finger_gest)
        gest_sense_frame.pack(fill='x', padx=(20, 0), pady=(5, 0))

        ttk.Label(gest_sense_frame, text='Sense:').pack(side='left')

        ttk.Scale(gest_sense_frame, value=50, from_=1, to=100).pack(side='left', fill='x', expand='yes', padx=5)

        gest_sense_btn = ttk.Button(gest_sense_frame, image='reset-small', style='Link.TButton')
        gest_sense_btn.configure(command=self.callback)
        gest_sense_btn.pack(side='left')

        ### fast two finger swipe down
        ttk.Label(two_finger_gest, text='On fast 2 finger up/down swipe:').pack(fill='x', pady=(10, 5))

        op9 = ttk.Checkbutton(two_finger_gest, text='Swap swipe direction', variable='op9')
        op9.pack(fill='x', padx=(20, 0), pady=5)

        op10 = ttk.Checkbutton(two_finger_gest, text='Swap swipe direction', variable='op10')
        op10.pack(fill='x', padx=(20, 0), pady=5)

        two_finger_cbo = ttk.Combobox(two_finger_gest, values=['Cycle Task View | Normal | Desktop View'])
        two_finger_cbo.current(0)
        two_finger_cbo.pack(fill='x', padx=(20, 0), pady=5)

        ### two finger sense
        two_finger_sense_frame = ttk.Frame(two_finger_gest)
        two_finger_sense_frame.pack(fill='x', padx=(20, 0), pady=(5, 0))

        ttk.Label(two_finger_sense_frame, text='Sense:').pack(side='left')

        ttk.Scale(two_finger_sense_frame, value=50, from_=1, to=100).pack(side='left', fill='x', expand='yes', padx=5)

        two_finger_sense_btn = ttk.Button(two_finger_sense_frame, image='reset-small', style='Link.TButton')
        two_finger_sense_btn.configure(command=self.callback)
        two_finger_sense_btn.pack(side='left')

        ## mouse options -----------------------------------------------------------------------------------------------
        mouse_options = ttk.Labelframe(col3, text='2 Finger Gestures', padding=(15, 10))
        mouse_options.pack(side='top', fill='both', expand='yes', pady=(10, 0))

        op11 = ttk.Checkbutton(mouse_options, text='Ignore input if mouse if lifted', variable='op11')
        op11.pack(fill='x', pady=5)

        op12 = ttk.Checkbutton(mouse_options, text='Ignore input if mouse if lifted', variable='op12')
        op12.pack(fill='x', pady=5)

        op13 = ttk.Checkbutton(mouse_options, text='Ignore input if mouse if lifted', variable='op13')
        op13.pack(fill='x', pady=5)

        ### base speed
        base_speed_sense_frame = ttk.Frame(mouse_options)
        base_speed_sense_frame.pack(fill='x', padx=(20, 0), pady=(5, 0))

        ttk.Label(base_speed_sense_frame, text='Base speed:').pack(side='left')

        ttk.Scale(base_speed_sense_frame, value=50, from_=1, to=100).pack(side='left', fill='x', expand='yes', padx=5)

        base_speed_sense_btn = ttk.Button(base_speed_sense_frame, image='reset-small', style='Link.TButton')
        base_speed_sense_btn.configure(command=self.callback)
        base_speed_sense_btn.pack(side='left')

        # turn on all checkbuttons
        for i in range(1, 14):
            self.setvar(f'op{i}', 1)

        # turn off select buttons
        for j in [2, 9, 12, 13]:
            self.setvar(f'op{j}', 0)

    def callback(self):
        """Demo callback"""
        showinfo(title='Button callback', message="You pressed a button.")


if __name__ == '__main__':
    Application().mainloop()