Source code for powermolegui.lib.frames

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: frames.py
#
# Copyright 2021 Vincent Schouten
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to
#  deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#  DEALINGS IN THE SOFTWARE.
#

"""
Main code for creating tkinter frames.

The frames are used as a container for other widgets.

.. _Google Python Style Guide:
   http://google.github.io/styleguide/pyguide.html

"""

import tkinter as tk
from tkinter import ttk  # allow using Tk themed widget set

__author__ = '''Vincent Schouten <powermole@protonmail.com>'''
__docformat__ = '''google'''
__date__ = '''06-12-2019'''
__copyright__ = '''Copyright 2021, Vincent Schouten'''
__credits__ = ["Vincent Schouten"]
__license__ = '''MIT'''
__maintainer__ = '''Vincent Schouten'''
__email__ = '''<powermole@protonmail.com>'''
__status__ = '''Development'''  # "Prototype", "Development", "Production".

# Constants
BACKGROUND_CANVAS = '#232729'


# Bryan Oakley: I prefer inheriting from tk.Frame just because I typically
# start by creating a frame, but it is by no means necessary.
[docs]class MainFrame(tk.Frame): """Constructs an outer frame that contains a canvas and a log frame.""" def __init__(self, parent, scale, *args, **kwargs): # parent = MainWindow """Instantiates the MainFrame object.""" tk.Frame.__init__(self, parent, *args, **kwargs) self.parent = parent # what's the use of parent actually? self.scale = scale # IN DEVELOPMENT self.canvas_frame = CanvasFrame(self) self.log_frame = LogFrame(self) self.canvas_frame.pack(fill='both', expand=True) self.log_frame.pack(fill='both', expand=True)
# Bryan Oakley: As a general rule of thumb, if you have a class that inherits from # a widget and it creates other widgets, those widgets should always be children of # self or one of its descendants, never its own parent. Having a class put child # widgets in its parent completely defeats the purpose of inheriting from tk.Frame.
[docs]class CanvasFrame(tk.Frame): """Constructs a frame for the canvas widget.""" def __init__(self, parent): # what's the use of parent here? """Instantiates the CanvasFrame object.""" tk.Frame.__init__(self, parent) self.parent = parent # what's the use of parent actually? self.canvas_landscape = self._canvas() self.canvas_status = self._status() self._scrollbar() self._scroll_bind() # self._menu = self._pop_menu() self._popup_menu() def _canvas(self): canvas = tk.Canvas(master=self, background=BACKGROUND_CANVAS, height=140, borderwidth=0, highlightthickness=0) canvas.pack(fill="both", expand=True) return canvas def _status(self): status = tk.Canvas(master=self, background=BACKGROUND_CANVAS, height=40, borderwidth=0, highlightthickness=0) status.pack(fill="both", expand=True) return status def _scrollbar(self): scrollbar = ttk.Scrollbar(master=self, orient=tk.HORIZONTAL, command=self.canvas_landscape.xview) scrollbar.pack(fill="x", expand=False) self.canvas_landscape.config(xscrollcommand=scrollbar.set) def _scroll_bind(self): # enable scrolling with the mouse self.canvas_landscape.bind("<ButtonPress-1>", self._scroll_start) self.canvas_status.bind("<ButtonPress-1>", self._scroll_start) self.canvas_landscape.bind("<B1-Motion>", self._scroll_move) self.canvas_status.bind("<B1-Motion>", self._scroll_move) def _scroll_start(self, event): self.canvas_landscape.scan_mark(event.x, event.y) def _scroll_move(self, event): # parameter 'gain' tells "scan_dragto" how many pixels to move for each pixel the mouse moves self.canvas_landscape.scan_dragto(event.x, event.y, gain=1) # def do_pop(self, event): # try: # self._menu.tk_popup(event.x_root, event.y_root) # finally: # self._menu.grab_release() # # def _pop_menu(self): # menu = tk.Menu(self, tearoff=0) # menu.add_command(label="Info") # self.canvas.bind("<Button-2>", self.do_pop) # return menu def _popup_menu(self): # otherwise known as "contextual menus" menu = tk.Menu(self, tearoff=0) menu.add_command(label="Info") if self.tk.call('tk', 'windowingsystem') == 'aqua': self.canvas_landscape.bind('<2>', lambda e: menu.post(e.x_root, e.y_root)) # On MacOS self.canvas_landscape.bind('<Control-1>', lambda e: menu.post(e.x_root, e.y_root)) # On MacOS else: self.canvas_landscape.bind('<3>', lambda e: menu.post(e.x_root, e.y_root)) # On Windows and X1
[docs]class LogFrame(tk.Frame): """Constructs a frame for the text widget.""" def __init__(self, parent): """Instantiates the LogFrame object.""" tk.Frame.__init__(self, parent) self.text = self._widget() def _widget(self): """____________.""" text = tk.Text(master=self, width=1, # in units of characters - not pixels height=1, # in units of characters - not pixels background='white', foreground='black') text.pack(side="left", fill="both", expand=True) text.configure(state='disabled') # block the user from entering anything text.config(wrap='word') scrollbar = ttk.Scrollbar(master=self, orient=tk.VERTICAL, command=text.yview) scrollbar.pack(side="right", fill="y", expand=False) text.config(yscrollcommand=scrollbar.set) return text
[docs] def insert_log_line(self, line): """____________.""" self.text.configure(state='normal') self.text.insert('end', line + '\n') # 1.0 refers to line 1 (start) character 0 self.text.see("end") self.text.configure(state='disabled')
[docs]class CommandFrame(tk.Frame): """Constructs an outer frame that contains two log frames.""" def __init__(self, parent, *args, **kwargs): """Instantiates the CommandFrame object.""" tk.Frame.__init__(self, parent, *args, **kwargs) self.parent = parent self.command_entry = CommandEntry(self.parent) self.command_response = CommandResponse(self.parent) self.command_entry.pack(fill='both', expand=False) self.command_response.pack(fill='both', expand=True)
[docs]class CommandEntry(tk.Frame): """Constructs a frame for the text widget.""" def __init__(self, parent): """Instantiates the CommandEntry object.""" tk.Frame.__init__(self, parent) self._label() self.entry = self._entry() def _label(self): label = tk.Label(self, text="Input") label.pack(fill='x', expand=False) def _entry(self): entry = tk.Entry(self, justify='left') entry.pack(fill='x', expand=False) entry.focus_set() return entry
[docs]class CommandResponse(tk.Frame): """Constructs a frame for the text widget.""" def __init__(self, parent): """Instantiates the CommandResponse object.""" tk.Frame.__init__(self, parent) self._label() self.text = self._text() def _label(self): label = tk.Label(self, text="Output") label.pack(fill='x', expand=False) def _text(self): text = tk.Text(self, width=1, # in units of characters - not pixels height=1) # in units of characters - not pixels text.config(wrap='word') text.tag_config('warning_style', foreground="red") text.insert(tk.END, 'the interface does not support shell meta characters \n' 'such as pipe and it\'s not possible to interact with \n' 'programs that need a response. hit control-c to quit \n', 'warning_style') text.pack(fill='both', expand=True) return text
# def _scrollbar(self): # scrollbar = ttk.Scrollbar(self.win, orient=VERTICAL, command=self.text.yview) # scrollbar.grid(row=3, # column=1) # self.text.config(yscrollcommand=scrollbar.set)