class EventWidget(Canvas): """ Represents event in GUI """ supply_width = 60 def __init__(self, app, index): """ Creates EventWidget for given app at given index """ self.height = 20 self.app = app self.index = index self.event = app.my_order.events[index] self.at = app.my_order.at[index + 1] start = self.at.time current = app.scale.get() passed_time = current - start total_time = self.app.my_order.event_length(self.index) dull = total_time == float("inf") or math.isnan(total_time) actual_time = max(0, min(passed_time, total_time)) self.cooldown = self.app.my_order.get_warp_cooldown(self.index) if app.place_by_time: if not dull: time_width = EventWidget.supply_width + len(self.app.my_order.at_time) * 5 disp = start * 5 - 1 else: time_width = 0 disp = 0 else: if not dull: time_width = max(400, EventWidget.supply_width + max(self.cooldown, total_time) * 5) else: time_width = 400 disp = 0 Canvas.__init__(self, app.event_frame, height=self.height, width=time_width) if self.cooldown and not dull: cooldown_passed = max(0, min(self.cooldown, current - start)) self.cooldown_rect = self.create_rectangle( EventWidget.supply_width + disp, 2, EventWidget.supply_width + self.cooldown * 5 + disp, self.height ) self.cooldown_fill = self.create_rectangle( EventWidget.supply_width + disp, 2, EventWidget.supply_width + cooldown_passed * 5 + disp, self.height, fill="pink", ) if not dull: self.fill = self.create_rectangle( EventWidget.supply_width + disp, 2, actual_time * 5 + EventWidget.supply_width + disp, self.height, fill="aquamarine", disabledoutline="", ) self.create_text(2, 2, text=str(self.at.supply) + "/" + str(self.at.cap), anchor=N + W) if not dull: self.full_time = self.create_rectangle( EventWidget.supply_width + disp, 2, total_time * 5 + EventWidget.supply_width + disp, self.height ) self.create_text(EventWidget.supply_width + 5 + disp, 10, text=events[self.event[0]].name, anchor=W) self.bind("<Button-1>", self.clicked) self.bind("<B1-Motion>", self.drag) self.passed_str = str(actual_time) + "/" + str(total_time) self.tooltip = ToolTip(self, delay=50) self.tooltip.configure(text=self.passed_str + " " + self.app.my_order.get_note(self.index)) self.rMenu = Menu(self, tearoff=0) self.rMenu.add_command(label="Insert above", command=self.insert_above) self.rMenu.add_command(label="Insert below", command=self.insert_below) self.rMenu.add_command(label="Delete", command=self.delete) self.rMenu.add_command(label="Edit note", command=self.note) if app.my_order.can_trick(index): if app.my_order.uses_trick(index): label = "Disable gas trick" else: label = "Enable gas trick" self.rMenu.add_command(label=label, command=self.toggle_trick) self.bind("<Button-3>", self.popup) def echo(self, event=None): """ Prints the event's index, useful for testing binding events """ print self.index def chrono_check(self, chrono_index): """ Changes this event to be dashed if able to be Chrono Boosted from chrono_index """ if self.app.my_order.can_chrono(self.index, chrono_index): self.itemconfig(self.full_time, dash=(4, 4)) else: self.itemconfig(self.full_time, dash=None) def clicked(self, click_event): """ On click, check if Chrono Boost is active, and if so, Chrono Boost this event Otherwise, prepare dragging this event to other indexes """ if self.app.chrono >= 0 and self.app.my_order.can_chrono(self.index, self.app.chrono): self.app.insert_chrono(self.index) self.app.chrono = -1 else: self.app.drag = self.index def drag(self, drag_event): """ Calculates mouse position relative to dragged event, moves event if difference is large enough """ change = 0 if self.index == self.app.drag: if drag_event.y > 40: change = (drag_event.y - 13) / 27 elif drag_event.y < -14: change = (drag_event.y + 14) / 27 elif self.index - 1 >= self.app.drag: change = self.index - self.app.drag - int(drag_event.y < 14) elif self.index + 1 <= self.app.drag: change = -(self.app.drag - self.index - int(drag_event.y > 14)) if change: self.app.my_order.move(self.app.drag, self.app.drag + change) self.app.drag += change self.app.refresh() def update(self, current): """ Updates the time completion of this event given current time """ start = self.at.time if self.app.place_by_time: disp = start * 5 - 1 else: disp = 0 passed_time = current - start total_time = self.app.my_order.event_length(self.index) dull = total_time == float("inf") or math.isnan(total_time) actual_time = max(0, min(passed_time, total_time)) if not dull: self.coords( self.fill, EventWidget.supply_width + disp, 2, actual_time * 5 + EventWidget.supply_width + disp, self.height, ) if self.cooldown: cooldown_passed = max(0, min(self.cooldown, current - start)) self.coords( self.cooldown_fill, EventWidget.supply_width + disp, 2, EventWidget.supply_width + cooldown_passed * 5 + disp, self.height, ) self.passed_str = str(actual_time) + "/" + str(total_time) self.tooltip.configure(text=self.passed_str + " " + self.app.my_order.get_note(self.index)) def popup(self, event): """ Creates right-click menu """ self.rMenu.post(event.x_root, event.y_root) def insert_above(self): """ Insert event option above this event """ self.app.insert_event_choose(self.index) def insert_below(self): """ Insert event option below this event """ self.app.insert_event_choose(self.index + 1) def delete(self): """ Remove this event from the build order """ self.app.my_order.delete(self.index) self.app.refresh() def note(self): """ Allow user to edit note describing this event in a separate window """ root = Toplevel() comment = App(root) comment.label = Label(root, text="Comment: ") comment.label.pack(side=LEFT) comment.entry = Entry(root) comment.entry.insert(0, self.app.my_order.get_note(self.index)) comment.entry.pack(padx=3, side=LEFT) def submit(): self.app.my_order.set_note(self.index, comment.entry.get()) self.tooltip.configure(text=self.passed_str + " " + self.app.my_order.get_note(self.index)) comment.master.destroy() comment.button = Button(root, text="OK", command=submit) comment.button.pack(side=LEFT) root.mainloop() def toggle_trick(self): """ Toggles whether or not this event uses extractor trick """ self.app.my_order.toggle_trick(self.index) self.app.refresh()