Advertisement
Uno-Dan

Untitled

Jun 25th, 2018
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.89 KB | None | 0 0
  1. ########################################################################################################################
  2. #    File: menus.py
  3. # Purpose: Creates a menubar with submenus for Tkinter GUI
  4. #  Author: Dan Huckson, https://github.com/unodan
  5. #    Date: 2018-05-23
  6. ########################################################################################################################
  7. from os import path, getcwd
  8. from tkinter import Menu, PhotoImage
  9.  
  10. from importlib import import_module
  11.  
  12.  
  13. def sanitize_label(label):
  14.     if label is None:
  15.         return None, None
  16.  
  17.     position = label.find('&')
  18.     if position >= 0:
  19.         label = label[:position] + label[position + 1:]
  20.  
  21.     return label, position
  22.  
  23.  
  24. class MenuItem:
  25.     def __init__(self, parent, key, item, **kwargs):
  26.         self.__parent = parent
  27.         self.__attributes = {}
  28.         self.has_children = False
  29.  
  30.         self.__index = key
  31.         label = self.__attributes['label'] = item.get('label', None)
  32.  
  33.         index = kwargs.get('index', None)
  34.         state = self.__attributes['state'] = item.get('state', 'normal')
  35.         image = self.__attributes['image'] = item.get('image', None)
  36.         value = self.__attributes['value'] = item.get('value', None)
  37.         command = self.__attributes['command'] = item.get('command', None)
  38.         onvalue = self.__attributes['onvalue'] = item.get('onvalue', True)
  39.         offvalue = self.__attributes['offvalue'] = item.get('offvalue', False)
  40.         variable = self.__attributes['variable'] = item.get('variable', None)
  41.         compound = self.__attributes['compound'] = item.get('compound', 'left')
  42.         underline = self.__attributes['underline'] = item.get('underline', None)
  43.         accelerator = self.__attributes['accelerator'] = item.get('accelerator', None)
  44.  
  45.         activeforeground = self.__attributes['activeforeground'] = kwargs.get('activeforeground', None)
  46.         activebackground = self.__attributes['activebackground'] = kwargs.get('activebackground', None)
  47.  
  48.         if command:
  49.             cmd = command.split('.')
  50.             module = import_module(cmd[0])
  51.             command = getattr(module, cmd[1], None)
  52.  
  53.         if image:
  54.             image = self.image = PhotoImage(file=path.join(getcwd(), 'tkmenus', 'images', image))
  55.         else:
  56.             image = self.image = PhotoImage(file=path.join(getcwd(), 'tkmenus', 'images', 'blank-21x16.png'))
  57.  
  58.         widget_type = item.get('type', None)
  59.         if widget_type == 'separator':
  60.             func = parent.insert_separator if index else parent.add_separator
  61.             func(index)
  62.         elif widget_type == 'checkbutton':
  63.             func = parent.insert_checkbutton if index else parent.add_checkbutton
  64.             func(
  65.                 label=label,
  66.                 index=index,
  67.                 state=state,
  68.                 command=command,
  69.                 onvalue=onvalue,
  70.                 offvalue=offvalue,
  71.                 variable=variable,
  72.                 underline=underline,
  73.                 accelerator=accelerator,
  74.             )
  75.         elif widget_type == 'radiobutton':
  76.             func = parent.insert_radiobutton if index else parent.add_radiobutton
  77.             func(
  78.                 label=label,
  79.                 index=index,
  80.                 state=state,
  81.                 value=value,
  82.                 command=command,
  83.                 variable=variable,
  84.                 underline=underline,
  85.                 accelerator=accelerator,
  86.             )
  87.         else:
  88.             func = parent.insert_command if index else parent.add_command
  89.             func(
  90.                 label=label,
  91.                 index=index,
  92.                 state=state,
  93.                 image=image,
  94.                 command=command,
  95.                 compound=compound,
  96.                 underline=underline,
  97.                 accelerator=accelerator,
  98.                 activeforeground=activeforeground,
  99.                 activebackground=activebackground,
  100.             )
  101.  
  102.     def cget(self, index):
  103.         return self.__attributes.get(index, None)
  104.  
  105.     def config(self, **kwargs):
  106.         fg = kwargs.get('fg', None)
  107.         if fg:
  108.             self.__attributes['fg'] = fg
  109.  
  110.         bg = kwargs.get('bg', None)
  111.         if bg:
  112.             self.__attributes['bg'] = bg
  113.  
  114.         font = kwargs.get('font', None)
  115.         if font:
  116.             self.__attributes['font'] = font
  117.  
  118.         label = kwargs.get('label', None)
  119.         if label:
  120.             self.__attributes['label'] = label
  121.  
  122.         image = kwargs.get('image', None)
  123.         if image:
  124.             self.__attributes['image'] = image
  125.  
  126.         state = kwargs.get('state', None)
  127.         if state:
  128.             self.__attributes['state'] = state
  129.  
  130.         underline = kwargs.get('underline', None)
  131.         if underline:
  132.             self.__attributes['underline'] = underline
  133.  
  134.         accelerator = kwargs.get('accelerator', None)
  135.         if accelerator:
  136.             self.__attributes['accelerator'] = accelerator
  137.  
  138.         activeforeground = kwargs.get('activeforeground', None)
  139.         if activeforeground:
  140.             self.__attributes['activeforeground'] = activeforeground
  141.  
  142.         activebackground = kwargs.get('activebackground', '#000000')
  143.         if activebackground:
  144.             self.__attributes['activebackground'] = activebackground
  145.  
  146.         print(kwargs)
  147.  
  148.         self.__parent.entryconfig(
  149.             self.__index,
  150.             font=font,
  151.             label=label,
  152.             image=image,
  153.             state=state,
  154.             compound='left',
  155.             foreground=fg,
  156.             background=bg,
  157.             underline=underline,
  158.             accelerator=accelerator,
  159.             activeforeground=activeforeground,
  160.             activebackground=activebackground,
  161.         )
  162.  
  163.  
  164. class Menus(Menu):
  165.     def __init__(self, parent, *args, **kwargs):
  166.         super().__init__(parent, *args, **kwargs)
  167.  
  168.         self.items = {}
  169.         self.__parent = parent
  170.         self.__index = kwargs.pop('index', None)
  171.  
  172.     def menu(self, uri):
  173.  
  174.         def get_deep(menu, uri_part):
  175.             parts = uri_part.split('/', 1)
  176.             member = menu.get_child(parts[0])
  177.  
  178.             if len(parts) > 1:
  179.                 member = get_deep(member, parts[1])
  180.  
  181.             return member
  182.  
  183.         uri = uri.replace('\\', '/')
  184.  
  185.         if '/' in uri:
  186.             return get_deep(self, uri)
  187.  
  188.         return self.items.get(path, None)
  189.  
  190.     def populate(self, children):
  191.         for key, child in children.items():
  192.             self.add_child(key, child)
  193.  
  194.         return self
  195.  
  196.     def add_child(self, key, child, **kwargs):
  197.         bd = kwargs.pop('bd', self.cget('bd'))
  198.         fg = kwargs.pop('fg', self.cget('fg'))
  199.         bg = kwargs.pop('bg', self.cget('bg'))
  200.         font = kwargs.pop('font', self.cget('font'))
  201.         index = kwargs.pop('index', self.__index)
  202.         tearoff = kwargs.pop('tearoff', self.cget('tearoff'))
  203.         activeforeground = kwargs.pop('activeforeground', self.cget('activeforeground'))
  204.         activebackground = kwargs.pop('activebackground', self.cget('activebackground'))
  205.  
  206.         func = SubMenu if 'children' in child else MenuItem
  207.         self.items[key] = func(
  208.             self,
  209.             key,
  210.             child,
  211.             bd=bd,
  212.             fg=fg,
  213.             bg=bg,
  214.             font=font,
  215.             index=index,
  216.             tearoff=tearoff,
  217.             activeforeground=activeforeground,
  218.             activebackground=activebackground,
  219.         )
  220.  
  221.     def get_child(self, uri):
  222.  
  223.         def get_deep(menu, uri_part):
  224.             if not menu:
  225.                 return None
  226.  
  227.             parts = uri_part.split('/', 1)
  228.             member = menu.get_child(parts[0])
  229.  
  230.             if len(parts) > 1:
  231.                 member = get_deep(member, parts[1])
  232.  
  233.             return member
  234.  
  235.         uri = uri.replace('\\', '/')
  236.  
  237.         if '/' in uri:
  238.             return get_deep(self, uri)
  239.  
  240.         return self.items.get(uri, None)
  241.  
  242.     def get_children(self):
  243.         return self.items
  244.  
  245.     def has_children(self):
  246.         return len(self.items)
  247.  
  248.     def config_children(self, **kwargs):
  249.         bd = kwargs.pop('bd', None)
  250.         fg = kwargs.pop('fg', None)
  251.         bg = kwargs.pop('bg', None)
  252.         font = kwargs.pop('font', None)
  253.         relief = kwargs.pop('relief', None)
  254.         activeforeground = kwargs.pop('activeforeground', None)
  255.         activebackground = kwargs.pop('activebackground', None)
  256.  
  257.         def process(children):
  258.             for index, child in children.items():
  259.  
  260.                 if isinstance(child, MenuItem) and child.cget('widget_type') == 'separator':
  261.                     continue
  262.  
  263.                 child.config(
  264.                     bd=bd,
  265.                     fg=fg,
  266.                     bg=bg,
  267.                     font=font,
  268.                     relief=relief,
  269.                     activeforeground=activeforeground,
  270.                     activebackground=activebackground,
  271.                 )
  272.  
  273.                 if isinstance(child, SubMenu):
  274.                     process(child.get_children())
  275.  
  276.         process(self.get_children())
  277.  
  278.  
  279. class SubMenu(Menus):
  280.     def __init__(self, parent, key, menu, *args, **kwargs):
  281.         index = kwargs.pop('index', None)
  282.         super().__init__(parent, *args, **kwargs)
  283.  
  284.         fg = kwargs.pop('fg', self.cget('fg'))
  285.         bg = kwargs.pop('bg', self.cget('bg'))
  286.         bd = kwargs.pop('bd', self.cget('bd'))
  287.         font = kwargs.pop('font', self.cget('font'))
  288.         tearoff = kwargs.pop('tearoff', self.cget('tearoff'))
  289.         activeforeground = kwargs.pop('activeforeground', self.cget('activeforeground'))
  290.         activebackground = kwargs.pop('activebackground', self.cget('activebackground'))
  291.  
  292.         label = menu.get('label', None)
  293.         image = menu.get('image', None)
  294.         state = menu.get('state', None)
  295.         children = menu.get('children', None)
  296.         compound = menu.get('compound', 'left')
  297.         underline = menu.get('underline', None)
  298.  
  299.         if image:
  300.             image = self.image = PhotoImage(file=path.join(getcwd(), 'tkmenus', 'images', image))
  301.         elif isinstance(parent, SubMenu):
  302.             image = self.image = PhotoImage(file=path.join(getcwd(), 'tkmenus', 'images', 'blank-21x16.png'))
  303.  
  304.         func = parent.add_cascade if index is None else parent.insert_cascade
  305.         func(index, label=label, image=image, compound=compound, menu=self, state=state, underline=underline)
  306.  
  307.         for key, child in children.items():
  308.             func = SubMenu if 'children' in child else MenuItem
  309.             self.items[key] = func(
  310.                 self,
  311.                 key,
  312.                 child,
  313.                 bd=bd,
  314.                 fg=fg,
  315.                 bg=bg,
  316.                 font=font,
  317.                 tearoff=tearoff,
  318.                 activeforeground=activeforeground,
  319.                 activebackground=activebackground,
  320.             )
  321.  
  322.  
  323. class MenuBar(Menus):
  324.     def __init__(self, parent, *args, **kwargs):
  325.         super().__init__(parent, *args, **kwargs)
  326.  
  327.         self.font = kwargs.get('font', None)
  328.  
  329.         self.items = {}
  330.         self.menus = self.get_children()
  331.  
  332.  
  333. class PopupMenu(Menus):
  334.     def __init__(self, parent, *args, **kwargs):
  335.         super().__init__(parent, *args, **kwargs)
  336.         tearoff = kwargs.pop('tearoff', 0)
  337.  
  338.         self.items = {}
  339.         self.__leave = False
  340.         self.__delay_destroy = 1000
  341.         self.bind("<Enter>", self.on_enter)
  342.         self.bind("<Leave>", self.on_leave)
  343.         self.bind("<Escape>", self.on_escape)
  344.         self.config(tearoff=tearoff)
  345.  
  346.     def on_enter(self, event):
  347.         if event:
  348.             self.__leave = False
  349.  
  350.     def on_leave(self, event):
  351.         self.__leave = True
  352.  
  353.         def leave():
  354.             if self.__leave:
  355.                 self.on_escape(event)
  356.  
  357.         self.after(self.__delay_destroy, leave)
  358.  
  359.     def on_escape(self, event):
  360.         if event:
  361.             self.unpost()
  362.             self.__leave = False
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement