Source code for PhysicsWindow

from tkinter import *
from tkinter import ttk

import math

import Substance
import Utility
from Options import Options
import Physics


[docs]class PhysicsWindow: """ Generated by Physics Canvas interactions, when some interaction should cause a new window to open which relates to something on the PhysicsCanvas. :param window: The main UI window :type window: :class:`Ui.MainWindow` """ def __init__(self, window): """ constructor """ self.root = Tk() # self.root.wm_attributes("-transparent", Options['windows transparent color']) # self.root['bg'] = Options['windows transparent color'] self.window = window self.window.additional_windows.append(self) self.time_elapsed_since_last_update = 0 self.root.protocol("WM_DELETE_WINDOW", self.del_win) # self.root.mainloop()
[docs] def del_win(self): """ Custom delete function also deletes window from :class:`Ui.Window` update references """ index = self.window.additional_windows.index(self) self.window.additional_windows.pop(index) self.root.destroy()
[docs] def update(self, interval): """ Set interval in Options.py to control how often these windows update. :param interval: Time in seconds :type interval: number """ self.time_elapsed_since_last_update += interval if self.time_elapsed_since_last_update >= Options['object popup update interval']: self.time_elapsed_since_last_update = 0
[docs]class PhysicsObjectWindow(PhysicsWindow): """ A window which tracks the status of a :class:`Physics.ForceObject`, showing its current x, y, velocity, etc. Also provides buttons for deleting the object and adding an "orbiter" :param window: The main UI window :type window: :class:`Ui.MainWindow` :param physics_object: The ForceObject to track :type physics_object: :class:`Physics.ForceObject` :param x: The screen x the window should appear :type x: number :param y: The screen y the window should appear :type y: number """ def __init__(self, window, physics_object, x = 200, y=200): """ constructor """ PhysicsWindow.__init__(self, window) self.root.geometry(f"+{round(x)}+{round(y)}") self.physics_object = physics_object id_label = ttk.Label(self.root, text='id: '+str(physics_object.canvas_id)) id_label.grid(row=0, column=0, sticky=W) material_text = f"{physics_object.material.name}, {physics_object.mass}kg" material_label = ttk.Label(self.root, text=material_text) material_label.grid(row=1, columnspan=4, sticky = W) x_label = ttk.Label(self.root, text='x: ') x_label.grid(row=2, column=0, sticky=E) self.x_val = ttk.Label(self.root) self.x_val.grid(row=2, column=1, sticky=W) y_label = ttk.Label(self.root, text='y: ') y_label.grid(row=2, column=2, sticky=E) self.y_val = ttk.Label(self.root) self.y_val.grid(row=2, column=3, sticky=W) self.velocity_label = ttk.Label(self.root) self.velocity_label.grid(row=3, column=0, columnspan=4, sticky=W) self.acceleration_label = ttk.Label(self.root) self.acceleration_label.grid(row=4, column=0, columnspan=4, sticky=W) self.net_force_label = ttk.Label(self.root) self.net_force_label.grid(row=5, column=0, columnspan=4, sticky=W) self.delete_button = ttk.Button(self.root, text='Delete', command=self.delete_button) self.delete_button.grid(row=3, column=4, sticky=E) self.add_orbiter_button = ttk.Button(self.root, text='Add Orbiter', command=self.orbiter_button) self.add_orbiter_button.grid(row=4, column=4, sticky=E) self.update(Options['object popup update interval']) self.root.mainloop() # add additional protocol to window close so it removes from open window list
[docs] def update(self, interval): """ Updates the displayed information. Updates less frequently than the framerate; only as often as specified in Options.py to improve performance :param interval: Time in seconds :type interval: number """ self.time_elapsed_since_last_update += interval if self.time_elapsed_since_last_update >= Options['object popup update interval']: self.root.lift() displacement = self.physics_object.displacement x_text = f"{round(displacement.x)} m" y_text = f"{round(displacement.y)} m" self.x_val['text'] = x_text self.y_val['text'] = y_text velocity = self.physics_object.velocity velocity_text = f"velocity: {round(velocity.magnitude)} m/s, {round(math.degrees(velocity.angle))} deg" self.velocity_label['text'] = velocity_text acceleration = self.physics_object.acceleration acceleration_text = f"acceleration: {round(acceleration.magnitude)} m/s^2, {round(math.degrees(acceleration.angle))} deg" self.acceleration_label['text'] = acceleration_text net_force = self.physics_object.net_force_vector net_force_text = f"net_force: {round(net_force.magnitude)} N, {round(math.degrees(net_force.angle))} deg" self.net_force_label['text'] = net_force_text self.time_elapsed_since_last_update = 0
[docs] def delete_button(self): """ Called when user presses the delete button on the window. Deletes the ForceObject and the PhysicsObjectWindow Calls clear_forces on the :class:`Physics.ForceObject` """ self.physics_object.clear_forces() self.window.physics_canvas.delete_physics_object(self.physics_object) self.del_win()
[docs] def orbiter_button(self): """ Adds a new ForceObject to the physics canvas. The new ForceObject is 1/100th the mass of the object this ForceWindow is already referencing. It starts 100 meters N of this ForceWindow's ForceObject and has a western velocity. A :class:`Physics.GravitationalForceGenerator` is also created to generate gravity between the two objects """ planet = self.physics_object gravitational_constant = 6.67*10**(-11) orbital_radius = 100 new_mass = planet.mass/100 material = planet.material force_magnitude = (gravitational_constant * planet.mass * new_mass)/orbital_radius**2 moon_radial_acceleration = new_mass/force_magnitude orbital_velocity = 10 moon = Physics.PhysicsObject(material, new_mass) moon_x = planet.displacement.x moon_y = planet.displacement.y - orbital_radius moon.displacement = Physics.Vector.make_vector_from_components(moon_x,moon_y) moon.velocity = Physics.Vector.make_directional_vector('W', orbital_velocity) self.window.physics_canvas.add_physics_object(moon) grav = Physics.GravitationalForceGenerator(planet, moon) self.window.physics_canvas.interacting_forces.append(grav) self.window.log('added orbiter')
[docs]class AddObjectWindow(PhysicsWindow): """ Opens a new window with entry fields for adding a new PhysicsObject to the canvas. :param window: The root window :type window: :class:`Ui.MainWindow` :param event: A Mousevent :type event: Tk.MouseEvent ? """ def __init__(self, window, event): PhysicsWindow.__init__(self, window) self.root.geometry(f"+{round(event.x)+300}+{round(event.y)+40}") # title_label = ttk.Label(self.root, text='Add Physics Object') # title_label.grid(row=0, column=0, columnspan=5, sticky= (W,E)) self.material_label = ttk.Label(self.root, text='material') self.material_label.grid(row=1, column=0, sticky=E) self.listbox = ttk.Spinbox(self.root, value=list(Substance.MATERIALS.keys()), state='readonly', width=11) self.listbox.set(list(Substance.MATERIALS.keys())[0]) self.listbox.grid(row=1, column=1, sticky=W) self.mass_label = ttk.Label(self.root, text='mass') self.mass_label.grid(row=2, column=0, sticky=E) self.mass_val = StringVar() self.mass_val.set(str(Options['default mass'])) print(self.mass_val.get()) validate_key = (self.root.register(Utility.validate_number_input), '%P') self.mass_entry = ttk.Entry(self.root, textvariable=self.mass_val, validate='key', validatecommand=validate_key, width=10) self.mass_entry.grid(row=2, column=1, sticky=W) self.mass_entry.insert(0, str(Options['default mass'])) self.add_button = ttk.Button(self.root, text='Add!', command=self.add_button_press) self.add_button.grid(row=3, column=0, columnspan=3, sticky=(N,S,E,W)) self.root.mainloop()
[docs] def add_button_press(self): """ Executed when user clicks Add button. Creates PhysicsObject, calls PhysicsCanvas.add_physics_object Deltes and closes the window """ material = Substance.MATERIALS[self.listbox.get()] mass = float(self.mass_entry.get()) new_object = Physics.PhysicsObject(material, mass) pc = self.window.physics_canvas pc.add_physics_object(new_object) self.del_win()