updated canvas dialogs to make use of common canvas redraw/wallpaper logic

This commit is contained in:
bharnden 2019-11-14 12:58:27 -08:00
parent 4d2b84b107
commit aec1126a14
4 changed files with 159 additions and 241 deletions

View file

@ -21,9 +21,6 @@ class Application(tk.Frame):
self.canvas = None self.canvas = None
self.statusbar = None self.statusbar = None
# variables
self.set_wallpaper = None
# setup # setup
self.config = appconfig.read() self.config = appconfig.read()
self.style = ttk.Style() self.style = ttk.Style()

View file

@ -1,12 +1,11 @@
""" """
set wallpaper set wallpaper
""" """
import enum
import logging import logging
import tkinter as tk import tkinter as tk
from tkinter import filedialog, ttk from tkinter import filedialog, ttk
from PIL import Image, ImageTk from PIL import Image
from coretk.appconfig import BACKGROUNDS_PATH from coretk.appconfig import BACKGROUNDS_PATH
from coretk.dialogs.dialog import Dialog from coretk.dialogs.dialog import Dialog
@ -15,14 +14,6 @@ from coretk.images import Images
PADX = 5 PADX = 5
class ScaleOption(enum.Enum):
NONE = 0
UPPER_LEFT = 1
CENTERED = 2
SCALED = 3
TILED = 4
class CanvasBackgroundDialog(Dialog): class CanvasBackgroundDialog(Dialog):
def __init__(self, master, app): def __init__(self, master, app):
""" """
@ -33,8 +24,8 @@ class CanvasBackgroundDialog(Dialog):
super().__init__(master, app, "Canvas Background", modal=True) super().__init__(master, app, "Canvas Background", modal=True)
self.canvas = self.app.canvas self.canvas = self.app.canvas
self.scale_option = tk.IntVar(value=self.canvas.scale_option.get()) self.scale_option = tk.IntVar(value=self.canvas.scale_option.get())
self.show_grid = tk.IntVar(value=self.canvas.show_grid.get()) self.show_grid = tk.BooleanVar(value=self.canvas.show_grid.get())
self.adjust_to_dim = tk.IntVar(value=self.canvas.adjust_to_dim.get()) self.adjust_to_dim = tk.BooleanVar(value=self.canvas.adjust_to_dim.get())
self.filename = tk.StringVar(value=self.canvas.wallpaper_file) self.filename = tk.StringVar(value=self.canvas.wallpaper_file)
self.image_label = None self.image_label = None
self.options = [] self.options = []
@ -124,9 +115,6 @@ class CanvasBackgroundDialog(Dialog):
) )
checkbutton.grid(row=5, column=0, sticky="ew", padx=PADX) checkbutton.grid(row=5, column=0, sticky="ew", padx=PADX)
self.show_grid.set(1)
self.adjust_to_dim.set(0)
def draw_buttons(self): def draw_buttons(self):
frame = ttk.Frame(self.top) frame = ttk.Frame(self.top)
frame.grid(row=6, column=0, pady=5, sticky="ew") frame.grid(row=6, column=0, pady=5, sticky="ew")
@ -171,202 +159,40 @@ class CanvasBackgroundDialog(Dialog):
def click_adjust_canvas(self): def click_adjust_canvas(self):
# deselect all radio buttons and grey them out # deselect all radio buttons and grey them out
if self.adjust_to_dim.get() == 1: if self.adjust_to_dim.get():
self.scale_option.set(0) self.scale_option.set(0)
for option in self.options: for option in self.options:
option.config(state=tk.DISABLED) option.config(state=tk.DISABLED)
# turn back the radio button to active state so that user can choose again # turn back the radio button to active state so that user can choose again
elif self.adjust_to_dim.get() == 0: else:
self.scale_option.set(1) self.scale_option.set(1)
for option in self.options: for option in self.options:
option.config(state=tk.NORMAL) option.config(state=tk.NORMAL)
else:
logging.error("canvasbackground.py adjust_canvas_size invalid value")
def delete_canvas_components(self, tag_list): def click_apply(self):
"""
delete canvas items whose tag is in the tag list
:param list[string] tag_list: list of tags
:return: nothing
"""
for tag in tag_list:
for i in self.canvas.find_withtag(tag):
self.canvas.delete(i)
def get_canvas_width_and_height(self):
"""
retrieve canvas width and height in pixels
:return: nothing
"""
grid = self.canvas.find_withtag("rectangle")[0]
x0, y0, x1, y1 = self.canvas.coords(grid)
canvas_w = abs(x0 - x1)
canvas_h = abs(y0 - y1)
return canvas_w, canvas_h
def determine_cropped_image_dimension(self):
"""
determine the dimension of the image after being cropped
:return: nothing
"""
return
def upper_left(self, img):
tk_img = ImageTk.PhotoImage(img)
# crop image if it is bigger than canvas
canvas_w, canvas_h = self.get_canvas_width_and_height()
cropx = img_w = tk_img.width()
cropy = img_h = tk_img.height()
if img_w > canvas_w:
cropx -= img_w - canvas_w
if img_h > canvas_h:
cropy -= img_h - canvas_h
cropped = img.crop((0, 0, cropx, cropy))
cropped_tk = ImageTk.PhotoImage(cropped)
# place left corner of image to the left corner of the canvas
self.canvas.wallpaper_drawn = cropped_tk
self.delete_canvas_components(["wallpaper"])
wid = self.canvas.create_image(
(cropx / 2, cropy / 2), image=cropped_tk, tags="wallpaper"
)
self.canvas.wallpaper_id = wid
def center(self, img):
"""
place the image at the center of canvas
:param Image img: image object
:return: nothing
"""
tk_img = ImageTk.PhotoImage(img)
canvas_w, canvas_h = self.get_canvas_width_and_height()
cropx = img_w = tk_img.width()
cropy = img_h = tk_img.height()
# dimension of the cropped image
if img_w > canvas_w:
cropx -= img_w - canvas_w
if img_h > canvas_h:
cropy -= img_h - canvas_h
x0 = (img_w - cropx) / 2
y0 = (img_h - cropy) / 2
x1 = x0 + cropx
y1 = y0 + cropy
cropped = img.crop((x0, y0, x1, y1))
cropped_tk = ImageTk.PhotoImage(cropped)
# place the center of the image at the center of the canvas
self.delete_canvas_components(["wallpaper"])
# self.delete_previous_wallpaper()
wid = self.canvas.create_image(
(canvas_w / 2, canvas_h / 2), image=cropped_tk, tags="wallpaper"
)
self.canvas.wallpaper_id = wid
self.canvas.wallpaper_drawn = cropped_tk
def scaled(self, img):
"""
scale image based on canvas dimension
:param Image img: image object
:return: nothing
"""
canvas_w, canvas_h = self.get_canvas_width_and_height()
resized_image = img.resize((int(canvas_w), int(canvas_h)), Image.ANTIALIAS)
image_tk = ImageTk.PhotoImage(resized_image)
self.delete_canvas_components(["wallpaper"])
wid = self.canvas.create_image(
(canvas_w / 2, canvas_h / 2), image=image_tk, tags="wallpaper"
)
self.canvas.wallpaper_id = wid
self.canvas.wallpaper_drawn = image_tk
def tiled(self, img):
return
def draw_new_canvas(self, canvas_width, canvas_height):
"""
delete the old canvas and draw a new one
:param int canvas_width: canvas width in pixel
:param int canvas_height: canvas height in pixel
:return:
"""
self.delete_canvas_components(["rectangle", "gridline"])
self.canvas.draw_grid(canvas_width, canvas_height)
def canvas_to_image_dimension(self, img):
image_tk = ImageTk.PhotoImage(img)
img_w = image_tk.width()
img_h = image_tk.height()
self.delete_canvas_components(["wallpaper"])
self.draw_new_canvas(img_w, img_h)
wid = self.canvas.create_image((img_w / 2, img_h / 2), image=image_tk)
self.canvas.wallpaper_id = wid
self.canvas.wallpaper_drawn = image_tk
def draw_grid(self):
self.canvas.adjust_to_dim.set(self.adjust_to_dim.get())
if self.show_grid.get() == 0:
for i in self.canvas.find_withtag("gridline"):
self.canvas.itemconfig(i, state=tk.HIDDEN)
elif self.show_grid.get() == 1:
for i in self.canvas.find_withtag("gridline"):
self.canvas.itemconfig(i, state=tk.NORMAL)
self.canvas.lift(i)
else:
logging.error("canvasbackground.py show_grid invalid value")
def save_wallpaper_options(self):
self.canvas.scale_option.set(self.scale_option.get()) self.canvas.scale_option.set(self.scale_option.get())
self.canvas.show_grid.set(self.show_grid.get()) self.canvas.show_grid.set(self.show_grid.get())
self.canvas.adjust_to_dim.set(self.adjust_to_dim.get()) self.canvas.adjust_to_dim.set(self.adjust_to_dim.get())
self.canvas.update_grid()
def click_apply(self):
filename = self.filename.get() filename = self.filename.get()
if not filename: if not filename:
self.delete_canvas_components(["wallpaper"]) self.canvas.delete(self.canvas.wallpaper_id)
self.destroy()
self.canvas.wallpaper = None self.canvas.wallpaper = None
self.canvas.wallpaper_file = None self.canvas.wallpaper_file = None
self.save_wallpaper_options() self.destroy()
return return
try: try:
img = Image.open(filename) img = Image.open(filename)
self.canvas.wallpaper = img self.canvas.wallpaper = img
self.canvas.wallpaper_file = filename self.canvas.wallpaper_file = filename
self.canvas.redraw()
except FileNotFoundError: except FileNotFoundError:
logging.error("invalid background: %s", filename) logging.error("invalid background: %s", filename)
if self.canvas.wallpaper_id: if self.canvas.wallpaper_id:
self.canvas.delete(self.canvas.wallpaper_id) self.canvas.delete(self.canvas.wallpaper_id)
self.canvas.wallpaper_id = None self.canvas.wallpaper_id = None
self.destroy() self.canvas.wallpaper_file = None
return
self.canvas.adjust_to_dim.set(self.adjust_to_dim.get())
if self.adjust_to_dim.get() == 0:
self.canvas.scale_option.set(self.scale_option.get())
option = ScaleOption(self.scale_option.get())
if option == ScaleOption.UPPER_LEFT:
self.upper_left(img)
elif option == ScaleOption.CENTERED:
self.center(img)
elif option == ScaleOption.SCALED:
self.scaled(img)
elif option == ScaleOption.TILED:
logging.warning("tiled background not implemented yet")
elif self.adjust_to_dim.get() == 1:
self.canvas_to_image_dimension(img)
self.draw_grid()
self.destroy() self.destroy()

View file

@ -1,14 +1,11 @@
""" """
size and scale size and scale
""" """
import logging
import tkinter as tk import tkinter as tk
from tkinter import font, ttk from tkinter import font, ttk
from coretk.dialogs.canvasbackground import ScaleOption
from coretk.dialogs.dialog import Dialog from coretk.dialogs.dialog import Dialog
DRAW_OBJECT_TAGS = ["edge", "node", "nodename", "linkinfo", "antenna"]
FRAME_PAD = 5 FRAME_PAD = 5
PADX = 5 PADX = 5
@ -172,47 +169,11 @@ class SizeAndScaleDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy) button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew") button.grid(row=0, column=1, sticky="ew")
def redraw_grid(self):
"""
redraw grid with new dimension
:return: nothing
"""
width, height = self.pixel_width.get(), self.pixel_height.get()
self.canvas.config(scrollregion=(0, 0, width + 200, height + 200))
# delete old plot and redraw
for i in self.canvas.find_withtag("gridline"):
self.canvas.delete(i)
for i in self.canvas.find_withtag("rectangle"):
self.canvas.delete(i)
self.canvas.draw_grid(width=width, height=height)
# lift anything that is drawn on the plot before
for tag in DRAW_OBJECT_TAGS:
for i in self.canvas.find_withtag(tag):
self.canvas.lift(i)
def click_apply(self): def click_apply(self):
meter_per_pixel = float(self.scale.get()) / 100 meter_per_pixel = float(self.scale.get()) / 100
width, height = self.pixel_width.get(), self.pixel_height.get()
self.canvas.meters_per_pixel = meter_per_pixel self.canvas.meters_per_pixel = meter_per_pixel
self.redraw_grid() self.canvas.redraw_grid(width, height)
# if there is a current wallpaper showing, redraw it based on current if self.canvas.wallpaper:
# wallpaper options self.canvas.redraw()
wallpaper_tool = self.app.set_wallpaper
wallpaper = self.canvas.wallpaper
if wallpaper:
if self.canvas.adjust_to_dim.get() == 0:
scale_option = ScaleOption(self.canvas.scale_option.get())
if scale_option == ScaleOption.UPPER_LEFT:
wallpaper_tool.upper_left(wallpaper)
elif scale_option == ScaleOption.CENTERED:
wallpaper_tool.center(wallpaper)
elif scale_option == ScaleOption.SCALED:
wallpaper_tool.scaled(wallpaper)
elif scale_option == ScaleOption.TILED:
logging.warning("tiled background not implemented")
elif self.canvas.adjust_to_dim.get() == 1:
wallpaper_tool.canvas_to_image_dimension(wallpaper)
wallpaper_tool.show_grid()
self.destroy() self.destroy()

View file

@ -2,6 +2,8 @@ import enum
import logging import logging
import tkinter as tk import tkinter as tk
from PIL import ImageTk
from core.api.grpc import core_pb2 from core.api.grpc import core_pb2
from coretk.canvasaction import CanvasAction from coretk.canvasaction import CanvasAction
from coretk.canvastooltip import CanvasTooltip from coretk.canvastooltip import CanvasTooltip
@ -21,6 +23,14 @@ class GraphMode(enum.Enum):
OTHER = 4 OTHER = 4
class ScaleOption(enum.Enum):
NONE = 0
UPPER_LEFT = 1
CENTERED = 2
SCALED = 3
TILED = 4
CORE_NODES = ["router"] CORE_NODES = ["router"]
CORE_WIRED_NETWORK_NODES = [] CORE_WIRED_NETWORK_NODES = []
CORE_WIRELESS_NODE = ["wlan"] CORE_WIRELESS_NODE = ["wlan"]
@ -60,8 +70,8 @@ class CanvasGraph(tk.Canvas):
self.wallpaper_drawn = None self.wallpaper_drawn = None
self.wallpaper_file = "" self.wallpaper_file = ""
self.scale_option = tk.IntVar(value=1) self.scale_option = tk.IntVar(value=1)
self.show_grid = tk.IntVar(value=1) self.show_grid = tk.BooleanVar(value=True)
self.adjust_to_dim = tk.IntVar(value=0) self.adjust_to_dim = tk.BooleanVar(value=False)
def setup_menus(self): def setup_menus(self):
self.node_context = tk.Menu(self.master) self.node_context = tk.Menu(self.master)
@ -132,11 +142,12 @@ class CanvasGraph(tk.Canvas):
width=1, width=1,
tags="rectangle", tags="rectangle",
) )
self.tag_lower(self.grid)
for i in range(0, width, 27): for i in range(0, width, 27):
self.create_line(i, 0, i, height, dash=(2, 4), tags="gridline") self.create_line(i, 0, i, height, dash=(2, 4), tags="gridline")
for i in range(0, height, 27): for i in range(0, height, 27):
self.create_line(0, i, width, i, dash=(2, 4), tags="gridline") self.create_line(0, i, width, i, dash=(2, 4), tags="gridline")
self.tag_lower("gridline")
self.tag_lower(self.grid)
def draw_existing_component(self, session): def draw_existing_component(self, session):
""" """
@ -228,9 +239,8 @@ class CanvasGraph(tk.Canvas):
if2 if2
) )
# lift the nodes so they on top of the links # raise the nodes so they on top of the links
for i in self.find_withtag("node"): self.tag_raise("node")
self.lift(i)
def canvas_xy(self, event): def canvas_xy(self, event):
""" """
@ -426,6 +436,132 @@ class CanvasGraph(tk.Canvas):
self.core.add_graph_node(self.core.session_id, node.id, x, y, node_name) self.core.add_graph_node(self.core.session_id, node.id, x, y, node_name)
return node return node
def width_and_height(self):
"""
retrieve canvas width and height in pixels
:return: nothing
"""
grid = self.find_withtag("rectangle")[0]
x0, y0, x1, y1 = self.coords(grid)
canvas_w = abs(x0 - x1)
canvas_h = abs(y0 - y1)
return canvas_w, canvas_h
def wallpaper_upper_left(self):
tk_img = ImageTk.PhotoImage(self.wallpaper)
# crop image if it is bigger than canvas
canvas_w, canvas_h = self.width_and_height()
cropx = img_w = tk_img.width()
cropy = img_h = tk_img.height()
if img_w > canvas_w:
cropx -= img_w - canvas_w
if img_h > canvas_h:
cropy -= img_h - canvas_h
cropped = self.wallpaper.crop((0, 0, cropx, cropy))
cropped_tk = ImageTk.PhotoImage(cropped)
self.delete(self.wallpaper_id)
# place left corner of image to the left corner of the canvas
self.wallpaper_id = self.create_image(
(cropx / 2, cropy / 2), image=cropped_tk, tags="wallpaper"
)
self.wallpaper_drawn = cropped_tk
def wallpaper_center(self):
"""
place the image at the center of canvas
:param Image img: image object
:return: nothing
"""
tk_img = ImageTk.PhotoImage(self.wallpaper)
canvas_w, canvas_h = self.width_and_height()
cropx = img_w = tk_img.width()
cropy = img_h = tk_img.height()
# dimension of the cropped image
if img_w > canvas_w:
cropx -= img_w - canvas_w
if img_h > canvas_h:
cropy -= img_h - canvas_h
x0 = (img_w - cropx) / 2
y0 = (img_h - cropy) / 2
x1 = x0 + cropx
y1 = y0 + cropy
cropped = self.wallpaper.crop((x0, y0, x1, y1))
cropped_tk = ImageTk.PhotoImage(cropped)
# place the center of the image at the center of the canvas
self.delete(self.wallpaper_id)
self.wallpaper_id = self.create_image(
(canvas_w / 2, canvas_h / 2), image=cropped_tk, tags="wallpaper"
)
self.wallpaper_drawn = cropped_tk
def wallpaper_scaled(self):
"""
scale image based on canvas dimension
:param Image img: image object
:return: nothing
"""
canvas_w, canvas_h = self.width_and_height()
image = Images.create(self.wallpaper_file, int(canvas_w), int(canvas_h))
self.delete(self.wallpaper_id)
self.wallpaper_id = self.create_image(
(canvas_w / 2, canvas_h / 2), image=image, tags="wallpaper"
)
self.wallpaper_drawn = image
def resize_to_wallpaper(self):
image_tk = ImageTk.PhotoImage(self.wallpaper)
img_w = image_tk.width()
img_h = image_tk.height()
self.delete(self.wallpaper_id)
self.delete("rectangle")
self.delete("gridline")
self.draw_grid(img_w, img_h)
self.wallpaper_id = self.create_image((img_w / 2, img_h / 2), image=image_tk)
self.wallpaper_drawn = image_tk
def redraw_grid(self, width, height):
"""
redraw grid with new dimension
:return: nothing
"""
self.config(scrollregion=(0, 0, width + 200, height + 200))
# delete previous grid
self.delete("rectangle")
self.delete("gridline")
# redraw
self.draw_grid(width=width, height=height)
# hide/show grid
self.update_grid()
def redraw(self):
if self.adjust_to_dim.get():
self.resize_to_wallpaper()
else:
option = ScaleOption(self.scale_option.get())
if option == ScaleOption.UPPER_LEFT:
self.wallpaper_upper_left()
elif option == ScaleOption.CENTERED:
self.wallpaper_center()
elif option == ScaleOption.SCALED:
self.wallpaper_scaled()
elif option == ScaleOption.TILED:
logging.warning("tiled background not implemented yet")
def update_grid(self):
logging.info("updating grid show: %s", self.show_grid.get())
if self.show_grid.get():
self.itemconfig("gridline", state=tk.NORMAL)
self.tag_raise("gridline")
else:
self.itemconfig("gridline", state=tk.HIDDEN)
class CanvasEdge: class CanvasEdge:
""" """
@ -469,8 +605,6 @@ class CanvasEdge:
self.link_info = None self.link_info = None
self.throughput = None self.throughput = None
self.wired = is_wired self.wired = is_wired
# TODO resolve this
# self.canvas.tag_lower(self.id)
def complete(self, dst, x, y): def complete(self, dst, x, y):
self.dst = dst self.dst = dst
@ -478,8 +612,8 @@ class CanvasEdge:
x1, y1, _, _ = self.canvas.coords(self.id) x1, y1, _, _ = self.canvas.coords(self.id)
self.canvas.coords(self.id, x1, y1, x, y) self.canvas.coords(self.id, x1, y1, x, y)
self.canvas.helper.draw_wireless_case(self.src, self.dst, self) self.canvas.helper.draw_wireless_case(self.src, self.dst, self)
self.canvas.lift(self.src) self.canvas.tag_raise(self.src)
self.canvas.lift(self.dst) self.canvas.tag_raise(self.dst)
def delete(self): def delete(self):
self.canvas.delete(self.id) self.canvas.delete(self.id)