"""Contains 'tabs' which are really just frames but are used by the `ttk.Notebook object
<https://tkdocs.com/shipman/ttk-Notebook.html>`_ for tabbed display.
The LogTab contains a Listbox and a Scrollbar. Strings can be sent to LogTab to be logged - they will be added to
the list. When clicked, they display on a lower label to account for long-stringed log events.
The DebugTab contains buttons for adding objects, for testing purposes. The panels for adding those objects are part
of this module. """
import random
from tkinter import *
from tkinter import ttk, colorchooser
import Particle
import Utility, Substance, Physics
from Options import Options
import time, math
[docs]class LogTab(ttk.Frame):
"""
Fills a frame with UI component necessary for keeping a log. Intended use is as a tab for the Notebook object.
Created by :class:`Ui.MainWindow`
Uses a `Tkinter.Listbox <https://tkdocs.com/shipman/listbox.html>`_.
:param parent: The frame where this component will be placed.
:type parent: widget
:param window: The broad reference window.
:type window: See MainWindow in :doc: Ui
"""
def __init__(self, parent, window):
"""
Constructor
"""
ttk.Frame.__init__(self, parent)
self.window = window
self.scroll_bar = Scrollbar(self)
self.list_box = Listbox(self, selectmode=SINGLE, width=45, yscrollcommand=self.scroll_bar.set)
self.list_box.bind('<ButtonPress>', self.click)
self.selected_var = StringVar()
selected_label = ttk.Label(self, textvariable=self.selected_var, wraplength=230)
self.list_box.grid(column=1, row=0)
self.scroll_bar.grid(column=0, row=0, sticky=(N,S))
selected_label.grid(column=1,row=1)
[docs] def click(self, event):
"""
Called when ListBox in this widget is clicked.
Displays selected item in the label, in case the log entry is too long for the ListBox.
:param event: MouseEvent
"""
index = self.list_box.nearest(event.y)
selection = self.list_box.get(index)
self.selected_var.set(selection)
[docs] def log(self, string):
"""
Creates an entry in the Listbox from the string. Prepends a timestamp.
:param string: text to log
"""
timestring = time.strftime('%H:%M:%S', time.localtime())
self.list_box.insert(END, f"{string} {timestring}")
[docs]class DebugTab(ttk.Frame):
"""
Fills a frame with some UI components for doing debug operations.
Created by :class:`Ui.MainWindow`.
:param parent: The frame where this component will be placed.
:type parent: widget
"""
def __init__(self, parent, window):
ttk.Frame.__init__(self, parent)
self.window = window
self.force_adder = ForceObjectAdder(self.window, ttk.Frame(self))
[docs]class ForceObjectAdder:
"""
Created by :class:`DebugTab.DebugTab`.
May be removed in a future version. Somewhat redundant due to AddObjectWindow being implemented. However,
it still does the binding of key presses to object moves, so it's left for now while said feature is still useful
for debug and testing.
"""
def __init__(self, window, parent_frame):
"""
Will call .grid() on parent_frame
"""
self.window = window
self.frame = parent_frame
self.add_button = ttk.Button(self.frame, text='add Force Object', command=self.add_button_press)
self.add_button.grid(column=0, row=1)
self.test_collision = ttk.Button(self.frame, text='test collision', command=self.add_test_collision)
self.test_collision.grid(column=0,row=2)
self.is_horizontal_collision = BooleanVar()
collision_check = ttk.Checkbutton(self.frame, text='horizontal', variable=self.is_horizontal_collision)
collision_check.grid(column=1, row=2)
self.frame.grid()
self.particle_test = ttk.Button(self.frame, text='particle test', command=self.particle_test)
self.particle_test.grid(column=0, row=3)
self.force_objects = []
self.window.root.bind('<Down>', self.key_handler)
self.window.root.bind('<Up>', self.key_handler)
self.window.root.bind('<Left>', self.key_handler)
self.window.root.bind('<Right>', self.key_handler)
[docs] def key_handler(self, event):
direction = 's'
if event.keysym == 'Right':
direction = 'e'
elif event.keysym == 'Up':
direction = 'n'
elif event.keysym == 'Left':
direction = 'w'
for i in range(0, len(self.force_objects)):
force = Physics.Force.make_directional_force(direction, Options['key force magnitude'], Options['key force duration'])
self.force_objects[i].forces.append(force)
[docs] def particle_test(self):
particle = Particle.Particle()
physics_canvas = self.window.physics_canvas
particle.add_to(physics_canvas)
[docs] def add_test_collision(self):
physics_canvas = self.window.physics_canvas
ob1 = Physics.PhysicsObject(Substance.MATERIALS['chalk'], Options['default mass'])
ob2 = Physics.PhysicsObject(Substance.MATERIALS['maple'], (Options['default mass']*2)/3)
x1 = -(random.random() * physics_canvas.width/2)
x2 = (random.random() * physics_canvas.width/2)
ob1.displacement = Physics.Vector.make_vector_from_components(x1, 50)
ob2.displacement = Physics.Vector.make_vector_from_components(x2, 15)
if not self.is_horizontal_collision.get():
ob1.velocity = Physics.Vector.make_directional_vector('NE', 22)
ob1.velocity.rotate(-1.3)
ob2.velocity = Physics.Vector.make_directional_vector('NW', 20)
else:
ob1.velocity = Physics.Vector.make_directional_vector('E', 22)
physics_canvas.add_physics_object(ob1)
physics_canvas.add_physics_object(ob2)
# try adding a test triangle vector to check our math on vector intersections
ob1_to_2 = ob2.displacement.subtract_make(ob1.displacement) # A Vector
ob2_to_1 = ob1.displacement.subtract_make(ob2.displacement)
ob1_inner_angle = math.fabs(ob1.velocity.angle - ob1_to_2.angle)
ob2_inner_angle = math.fabs(ob2.velocity.angle - ob2_to_1.angle)
missing_inner_angle = math.pi - ob1_inner_angle - ob2_inner_angle
ob1_to_collision_length = (math.sin(ob2_inner_angle)*ob1_to_2.magnitude)/(math.sin(missing_inner_angle))
ob1_to_collision = Physics.Vector(ob1.velocity.angle, ob1_to_collision_length)
print('ob1 vel', ob1.velocity)
print('ob2 vel', ob2.velocity)
print('vec between', ob1_to_2)
print('vec between back', ob2_to_1)
line2 = Particle.Line(3, ob2.velocity.scale_make(2), 'black')
line2.displacement = ob2.displacement.scale_make(1)
line2.add_to(physics_canvas)
line3 = Particle.Line(3, ob1_to_2, 'black')
line3.displacement = ob1.displacement.scale_make(1)
line3.add_to(physics_canvas)
line5 = Particle.Line(3, ob1_to_collision, 'green')
line5.displacement = ob1.displacement.scale_make(1)
line5.add_to(physics_canvas)