Merge branch 'develop' into pygui-rmallservices-fix

This commit is contained in:
Huy Pham 2020-04-30 13:48:38 -07:00
commit b116d525d9
7 changed files with 248 additions and 34 deletions

View file

@ -0,0 +1,158 @@
import logging
import tkinter as tk
from tkinter import ttk
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY
class FindDialog(Dialog):
def __init__(self, master, app) -> None:
super().__init__(master, app, "Find", modal=True)
self.find_text = tk.StringVar(value="")
self.tree = None
self.draw()
self.protocol("WM_DELETE_WINDOW", self.close_dialog)
self.bind("<Return>", self.find_node)
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
self.top.rowconfigure(1, weight=5)
self.top.rowconfigure(2, weight=1)
# Find node frame
frame = ttk.Frame(self.top, padding=FRAME_PAD)
frame.grid(sticky="nsew")
frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text="Find:")
label.grid()
entry = ttk.Entry(frame, textvariable=self.find_text)
entry.grid(row=0, column=1, sticky="nsew")
# node list frame
frame = ttk.Frame(self.top)
frame.columnconfigure(0, weight=1)
frame.rowconfigure(0, weight=1)
frame.grid(sticky="nsew", padx=PADX, pady=PADY)
self.tree = ttk.Treeview(
frame,
columns=("nodeid", "name", "location", "detail"),
show="headings",
selectmode=tk.BROWSE,
)
self.tree.grid(sticky="nsew", pady=PADY)
style = ttk.Style()
heading_size = int(self.app.guiconfig["scale"] * 10)
style.configure("Treeview.Heading", font=(None, heading_size, "bold"))
self.tree.column("nodeid", stretch=tk.YES, anchor="center")
self.tree.heading("nodeid", text="Node ID")
self.tree.column("name", stretch=tk.YES, anchor="center")
self.tree.heading("name", text="Name")
self.tree.column("location", stretch=tk.YES, anchor="center")
self.tree.heading("location", text="Location")
self.tree.column("detail", stretch=tk.YES, anchor="center")
self.tree.heading("detail", text="Detail")
self.tree.bind("<<TreeviewSelect>>", self.click_select)
yscrollbar = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
yscrollbar.grid(row=0, column=1, sticky="ns")
self.tree.configure(yscrollcommand=yscrollbar.set)
xscrollbar = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview)
xscrollbar.grid(row=1, sticky="ew")
self.tree.configure(xscrollcommand=xscrollbar.set)
# button frame
frame = ttk.Frame(self.top)
frame.grid(sticky="nsew")
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)
button = ttk.Button(frame, text="Find", command=self.find_node)
button.grid(row=0, column=0, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Cancel", command=self.close_dialog)
button.grid(row=0, column=1, sticky="ew", padx=PADX)
def clear_treeview_items(self) -> None:
"""
clear all items in the treeview
"""
for i in list(self.tree.get_children("")):
self.tree.delete(i)
def find_node(self, _event: tk.Event = None) -> None:
"""
Query nodes that have the same node name as our search key,
display results to tree view
"""
node_name = self.find_text.get().strip()
self.clear_treeview_items()
for node_id, node in sorted(
self.app.core.canvas_nodes.items(), key=lambda x: x[0]
):
name = node.core_node.name
if not node_name or node_name == name:
pos_x = round(node.core_node.position.x, 1)
pos_y = round(node.core_node.position.y, 1)
# TODO I am not sure what to insert for Detail column, leaving in blank for now
self.tree.insert(
"",
tk.END,
text=str(node_id),
values=(node_id, name, f"<{pos_x}, {pos_y}>", ""),
)
results = self.tree.get_children("")
if results:
self.tree.selection_set(results[0])
def close_dialog(self) -> None:
self.app.canvas.delete("find")
self.destroy()
def click_select(self, _event: tk.Event = None) -> None:
"""
find the node that matches search criteria, circle around that node
and scroll the x and y scrollbar to be able to see the node if
it is out of sight
"""
item = self.tree.selection()
if item:
self.app.canvas.delete("find")
node_id = int(self.tree.item(item, "text"))
canvas_node = self.app.core.canvas_nodes[node_id]
x0, y0, x1, y1 = self.app.canvas.bbox(canvas_node.id)
dist = 5 * self.app.guiconfig["scale"]
self.app.canvas.create_oval(
x0 - dist,
y0 - dist,
x1 + dist,
y1 + dist,
tags="find",
outline="red",
width=3.0 * self.app.guiconfig["scale"],
)
_x, _y, _, _ = self.app.canvas.bbox(canvas_node.id)
oid = self.app.canvas.find_withtag("rectangle")
x0, y0, x1, y1 = self.app.canvas.bbox(oid[0])
logging.debug("Dist to most left: %s", abs(x0 - _x))
logging.debug("White canvas width: %s", abs(x0 - x1))
# calculate the node's location
# (as fractions of white canvas's width and height)
# and instantly scroll the x and y scrollbar to that location
xscroll_fraction = abs(x0 - _x) / abs(x0 - x1)
yscroll_fraction = abs(y0 - _y) / abs(y0 - y1)
# scroll a little more to the left or a little bit up so that the node
# doesn't always fall in the most top-left corner
for i in range(2):
if xscroll_fraction > 0.05:
xscroll_fraction = xscroll_fraction - 0.05
if yscroll_fraction > 0.05:
yscroll_fraction = yscroll_fraction - 0.05
self.app.canvas.xview_moveto(xscroll_fraction)
self.app.canvas.yview_moveto(yscroll_fraction)

View file

@ -72,12 +72,15 @@ class SessionsDialog(Dialog):
show="headings",
selectmode=tk.BROWSE,
)
style = ttk.Style()
heading_size = int(self.app.guiconfig["scale"] * 10)
style.configure("Treeview.Heading", font=(None, heading_size, "bold"))
self.tree.grid(sticky="nsew")
self.tree.column("id", stretch=tk.YES)
self.tree.column("id", stretch=tk.YES, anchor="center")
self.tree.heading("id", text="ID")
self.tree.column("state", stretch=tk.YES)
self.tree.column("state", stretch=tk.YES, anchor="center")
self.tree.heading("state", text="State")
self.tree.column("nodes", stretch=tk.YES)
self.tree.column("nodes", stretch=tk.YES, anchor="center")
self.tree.heading("nodes", text="Node Count")
for index, session in enumerate(self.sessions):
@ -213,3 +216,5 @@ class SessionsDialog(Dialog):
def on_closing(self) -> None:
if self.is_start_app and messagebox.askokcancel("Exit", "Quit?", parent=self):
self.click_exit()
if not self.is_start_app:
self.destroy()

View file

@ -11,6 +11,7 @@ from core.gui.dialogs.about import AboutDialog
from core.gui.dialogs.canvassizeandscale import SizeAndScaleDialog
from core.gui.dialogs.canvaswallpaper import CanvasWallpaperDialog
from core.gui.dialogs.executepython import ExecutePythonDialog
from core.gui.dialogs.find import FindDialog
from core.gui.dialogs.hooks import HooksDialog
from core.gui.dialogs.ipdialog import IpConfigDialog
from core.gui.dialogs.macdialog import MacConfigDialog
@ -114,6 +115,7 @@ class Menubar(tk.Menu):
Create edit menu
"""
menu = tk.Menu(self)
menu.add_command(label="Find", accelerator="Ctrl+F", command=self.click_find)
menu.add_command(label="Preferences", command=self.click_preferences)
menu.add_command(label="Undo", accelerator="Ctrl+Z", state=tk.DISABLED)
menu.add_command(label="Redo", accelerator="Ctrl+Y", state=tk.DISABLED)
@ -125,6 +127,7 @@ class Menubar(tk.Menu):
label="Delete", accelerator="Ctrl+D", command=self.click_delete
)
self.add_cascade(label="Edit", menu=menu)
self.app.master.bind_all("<Control-f>", self.click_find)
self.app.master.bind_all("<Control-x>", self.click_cut)
self.app.master.bind_all("<Control-c>", self.click_copy)
self.app.master.bind_all("<Control-v>", self.click_paste)
@ -397,6 +400,10 @@ class Menubar(tk.Menu):
self.core.create_new_session()
self.core.xml_file = None
def click_find(self, _event: tk.Event = None) -> None:
dialog = FindDialog(self.app, self.app)
dialog.show()
def click_preferences(self) -> None:
dialog = PreferencesDialog(self.app, self.app)
dialog.show()