Merge branch 'coretk' into coretk-config
This commit is contained in:
commit
a0039d3991
14 changed files with 463 additions and 96 deletions
6
.github/workflows/coretk-checks.yml
vendored
6
.github/workflows/coretk-checks.yml
vendored
|
@ -15,7 +15,11 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pipenv
|
pip install pipenv
|
||||||
cd coretk
|
cd daemon
|
||||||
|
cp setup.py.in setup.py
|
||||||
|
cp core/constants.py.in core/constants.py
|
||||||
|
sed -i 's/True/False/g' core/constants.py
|
||||||
|
cd ../coretk
|
||||||
pipenv install --dev
|
pipenv install --dev
|
||||||
- name: isort
|
- name: isort
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -42,15 +42,15 @@ def check_directory():
|
||||||
for background in LOCAL_BACKGROUND_PATH.glob("*"):
|
for background in LOCAL_BACKGROUND_PATH.glob("*"):
|
||||||
new_background = BACKGROUNDS_PATH.joinpath(background.name)
|
new_background = BACKGROUNDS_PATH.joinpath(background.name)
|
||||||
shutil.copy(background, new_background)
|
shutil.copy(background, new_background)
|
||||||
with CONFIG_PATH.open("w") as f:
|
config = {"servers": [{"name": "example", "address": "127.0.0.1", "port": 50051}]}
|
||||||
yaml.dump(
|
save_config(config)
|
||||||
{"servers": [{"name": "example", "address": "127.0.0.1", "port": 50051}]},
|
|
||||||
f,
|
|
||||||
Dumper=IndentDumper,
|
|
||||||
default_flow_style=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def read_config():
|
def read_config():
|
||||||
with CONFIG_PATH.open("r") as f:
|
with CONFIG_PATH.open("r") as f:
|
||||||
return yaml.load(f, Loader=yaml.SafeLoader)
|
return yaml.load(f, Loader=yaml.SafeLoader)
|
||||||
|
|
||||||
|
|
||||||
|
def save_config(config):
|
||||||
|
with CONFIG_PATH.open("w") as f:
|
||||||
|
yaml.dump(config, f, Dumper=IndentDumper, default_flow_style=False)
|
||||||
|
|
|
@ -75,6 +75,7 @@ class CoreClient:
|
||||||
self.app = app
|
self.app = app
|
||||||
self.master = app.master
|
self.master = app.master
|
||||||
self.interface_helper = None
|
self.interface_helper = None
|
||||||
|
self.services = {}
|
||||||
|
|
||||||
# distributed server data
|
# distributed server data
|
||||||
self.servers = {}
|
self.servers = {}
|
||||||
|
@ -228,9 +229,16 @@ class CoreClient:
|
||||||
:return: existing sessions
|
:return: existing sessions
|
||||||
"""
|
"""
|
||||||
self.client.connect()
|
self.client.connect()
|
||||||
response = self.client.get_sessions()
|
|
||||||
|
# get service information
|
||||||
|
response = self.client.get_services()
|
||||||
|
for service in response.services:
|
||||||
|
group_services = self.services.setdefault(service.group, [])
|
||||||
|
group_services.append(service)
|
||||||
|
|
||||||
# if there are no sessions, create a new session, else join a session
|
# if there are no sessions, create a new session, else join a session
|
||||||
|
response = self.client.get_sessions()
|
||||||
|
logging.info("current sessions: %s", response)
|
||||||
sessions = response.sessions
|
sessions = response.sessions
|
||||||
if len(sessions) == 0:
|
if len(sessions) == 0:
|
||||||
self.create_new_session()
|
self.create_new_session()
|
||||||
|
|
|
@ -543,7 +543,9 @@ class CoreMenubar(object):
|
||||||
observer_widget_menu.add_command(
|
observer_widget_menu.add_command(
|
||||||
label="PIM neighbors", command=action.sub_menu_items
|
label="PIM neighbors", command=action.sub_menu_items
|
||||||
)
|
)
|
||||||
observer_widget_menu.add_command(label="Edit...", command=action.sub_menu_items)
|
observer_widget_menu.add_command(
|
||||||
|
label="Edit...", command=self.menu_action.edit_observer_widgets
|
||||||
|
)
|
||||||
|
|
||||||
widget_menu.add_cascade(label="Observer Widgets", menu=observer_widget_menu)
|
widget_menu.add_cascade(label="Observer Widgets", menu=observer_widget_menu)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
# from core.api.grpc import core_pb2
|
|
||||||
from coretk.coretoolbarhelp import CoreToolbarHelp
|
from coretk.coretoolbarhelp import CoreToolbarHelp
|
||||||
|
from coretk.dialogs.customnodes import CustomNodesDialog
|
||||||
from coretk.graph import GraphMode
|
from coretk.graph import GraphMode
|
||||||
from coretk.images import ImageEnum, Images
|
from coretk.images import ImageEnum, Images
|
||||||
from coretk.tooltip import CreateToolTip
|
from coretk.tooltip import CreateToolTip
|
||||||
|
@ -233,11 +233,12 @@ class CoreToolbar(object):
|
||||||
self.canvas.draw_node_image = Images.get(ImageEnum.OVS)
|
self.canvas.draw_node_image = Images.get(ImageEnum.OVS)
|
||||||
self.canvas.draw_node_name = "OVS"
|
self.canvas.draw_node_name = "OVS"
|
||||||
|
|
||||||
# TODO what graph node is this
|
|
||||||
def pick_editnode(self, main_button):
|
def pick_editnode(self, main_button):
|
||||||
self.network_layer_option_menu.destroy()
|
self.network_layer_option_menu.destroy()
|
||||||
main_button.configure(image=Images.get(ImageEnum.EDITNODE))
|
main_button.configure(image=Images.get(ImageEnum.EDITNODE))
|
||||||
logging.debug("Pick editnode option")
|
logging.debug("Pick editnode option")
|
||||||
|
dialog = CustomNodesDialog(self.app, self.app)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
def draw_network_layer_options(self, network_layer_button):
|
def draw_network_layer_options(self, network_layer_button):
|
||||||
"""
|
"""
|
||||||
|
|
158
coretk/coretk/dialogs/customnodes.py
Normal file
158
coretk/coretk/dialogs/customnodes.py
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import tkinter as tk
|
||||||
|
|
||||||
|
from coretk.dialogs.dialog import Dialog
|
||||||
|
from coretk.dialogs.nodeicon import IconDialog
|
||||||
|
from coretk.widgets import CheckboxList, ListboxScroll
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesSelectDialog(Dialog):
|
||||||
|
def __init__(self, master, app):
|
||||||
|
super().__init__(master, app, "Node Services", modal=True)
|
||||||
|
self.groups = None
|
||||||
|
self.services = None
|
||||||
|
self.current = None
|
||||||
|
self.current_services = set()
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.columnconfigure(0, weight=1)
|
||||||
|
self.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(stick="nsew")
|
||||||
|
frame.rowconfigure(0, weight=1)
|
||||||
|
for i in range(3):
|
||||||
|
frame.columnconfigure(i, weight=1)
|
||||||
|
self.groups = ListboxScroll(frame, text="Groups")
|
||||||
|
self.groups.grid(row=0, column=0, sticky="nsew")
|
||||||
|
for group in sorted(self.app.core.services):
|
||||||
|
self.groups.listbox.insert(tk.END, group)
|
||||||
|
self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change)
|
||||||
|
|
||||||
|
self.services = CheckboxList(
|
||||||
|
frame, text="Services", clicked=self.service_clicked
|
||||||
|
)
|
||||||
|
self.services.grid(row=0, column=1, sticky="nsew")
|
||||||
|
|
||||||
|
self.current = ListboxScroll(frame, text="Selected")
|
||||||
|
self.current.grid(row=0, column=2, sticky="nsew")
|
||||||
|
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(stick="ew")
|
||||||
|
for i in range(2):
|
||||||
|
frame.columnconfigure(i, weight=1)
|
||||||
|
button = tk.Button(frame, text="Save")
|
||||||
|
button.grid(row=0, column=0, sticky="ew")
|
||||||
|
button = tk.Button(frame, text="Cancel", command=self.destroy)
|
||||||
|
button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
|
def handle_group_change(self, event):
|
||||||
|
selection = self.groups.listbox.curselection()
|
||||||
|
if selection:
|
||||||
|
index = selection[0]
|
||||||
|
group = self.groups.listbox.get(index)
|
||||||
|
self.services.clear()
|
||||||
|
for service in sorted(self.app.core.services[group], key=lambda x: x.name):
|
||||||
|
self.services.add(service.name)
|
||||||
|
|
||||||
|
def service_clicked(self, name, var):
|
||||||
|
if var.get() and name not in self.current_services:
|
||||||
|
self.current_services.add(name)
|
||||||
|
elif not var.get() and name in self.current_services:
|
||||||
|
self.current_services.remove(name)
|
||||||
|
self.current.listbox.delete(0, tk.END)
|
||||||
|
for name in sorted(self.current_services):
|
||||||
|
self.current.listbox.insert(tk.END, name)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomNodesDialog(Dialog):
|
||||||
|
def __init__(self, master, app):
|
||||||
|
super().__init__(master, app, "Custom Nodes", modal=True)
|
||||||
|
self.save_button = None
|
||||||
|
self.delete_button = None
|
||||||
|
self.name = tk.StringVar()
|
||||||
|
self.image_button = None
|
||||||
|
self.image = None
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.columnconfigure(0, weight=1)
|
||||||
|
self.rowconfigure(0, weight=1)
|
||||||
|
self.draw_node_config()
|
||||||
|
self.draw_node_buttons()
|
||||||
|
self.draw_buttons()
|
||||||
|
|
||||||
|
def draw_node_config(self):
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(sticky="nsew")
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL)
|
||||||
|
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
|
listbox = tk.Listbox(frame, selectmode=tk.SINGLE, yscrollcommand=scrollbar.set)
|
||||||
|
listbox.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|
||||||
|
scrollbar.config(command=listbox.yview)
|
||||||
|
|
||||||
|
frame = tk.Frame(frame)
|
||||||
|
frame.grid(row=0, column=2, sticky="nsew")
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
entry = tk.Entry(frame, textvariable=self.name)
|
||||||
|
entry.grid(sticky="ew")
|
||||||
|
self.image_button = tk.Button(frame, text="Icon", command=self.click_icon)
|
||||||
|
self.image_button.grid(sticky="ew")
|
||||||
|
button = tk.Button(frame, text="Services", command=self.click_services)
|
||||||
|
button.grid(sticky="ew")
|
||||||
|
|
||||||
|
def draw_node_buttons(self):
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(pady=2, sticky="ew")
|
||||||
|
for i in range(3):
|
||||||
|
frame.columnconfigure(i, weight=1)
|
||||||
|
|
||||||
|
button = tk.Button(frame, text="Create", command=self.click_create)
|
||||||
|
button.grid(row=0, column=0, sticky="ew")
|
||||||
|
|
||||||
|
self.save_button = tk.Button(
|
||||||
|
frame, text="Save", state=tk.DISABLED, command=self.click_save
|
||||||
|
)
|
||||||
|
self.save_button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
|
self.delete_button = tk.Button(
|
||||||
|
frame, text="Delete", state=tk.DISABLED, command=self.click_delete
|
||||||
|
)
|
||||||
|
self.delete_button.grid(row=0, column=2, sticky="ew")
|
||||||
|
|
||||||
|
def draw_buttons(self):
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(sticky="ew")
|
||||||
|
for i in range(2):
|
||||||
|
frame.columnconfigure(i, weight=1)
|
||||||
|
|
||||||
|
button = tk.Button(frame, text="Save", command=self.click_save)
|
||||||
|
button.grid(row=0, column=0, sticky="ew")
|
||||||
|
|
||||||
|
button = tk.Button(frame, text="Cancel", command=self.destroy)
|
||||||
|
button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
|
def click_icon(self):
|
||||||
|
dialog = IconDialog(self, self.app, self.name.get(), self.image)
|
||||||
|
dialog.show()
|
||||||
|
if dialog.image:
|
||||||
|
self.image = dialog.image
|
||||||
|
self.image_button.config(image=self.image)
|
||||||
|
|
||||||
|
def click_services(self):
|
||||||
|
dialog = ServicesSelectDialog(self, self.app)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def click_create(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def click_save(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def click_delete(self):
|
||||||
|
pass
|
|
@ -2,7 +2,7 @@ import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
|
|
||||||
from coretk.dialogs.dialog import Dialog
|
from coretk.dialogs.dialog import Dialog
|
||||||
from coretk.dialogs.nodeicon import NodeIconDialog
|
from coretk.dialogs.nodeicon import IconDialog
|
||||||
from coretk.dialogs.nodeservice import NodeServicesDialog
|
from coretk.dialogs.nodeservice import NodeServicesDialog
|
||||||
|
|
||||||
NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"]
|
NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"]
|
||||||
|
@ -91,7 +91,9 @@ class NodeConfigDialog(Dialog):
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def click_icon(self):
|
def click_icon(self):
|
||||||
dialog = NodeIconDialog(self, self.app, self.canvas_node)
|
dialog = IconDialog(
|
||||||
|
self, self.app, self.canvas_node.name, self.canvas_node.image
|
||||||
|
)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
if dialog.image:
|
if dialog.image:
|
||||||
self.image = dialog.image
|
self.image = dialog.image
|
||||||
|
|
|
@ -6,18 +6,12 @@ from coretk.dialogs.dialog import Dialog
|
||||||
from coretk.images import Images
|
from coretk.images import Images
|
||||||
|
|
||||||
|
|
||||||
class NodeIconDialog(Dialog):
|
class IconDialog(Dialog):
|
||||||
def __init__(self, master, app, canvas_node):
|
def __init__(self, master, app, name, image):
|
||||||
"""
|
super().__init__(master, app, f"{name} Icon", modal=True)
|
||||||
create an instance of ImageModification
|
|
||||||
:param master: dialog master
|
|
||||||
:param coretk.app.Application: main app
|
|
||||||
:param coretk.graph.CanvasNode canvas_node: node object
|
|
||||||
"""
|
|
||||||
super().__init__(master, app, f"{canvas_node.name} Icon", modal=True)
|
|
||||||
self.file_path = tk.StringVar()
|
self.file_path = tk.StringVar()
|
||||||
self.image_label = None
|
self.image_label = None
|
||||||
self.image = canvas_node.image
|
self.image = image
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
|
|
@ -6,58 +6,6 @@ from tkinter import messagebox
|
||||||
|
|
||||||
from coretk.dialogs.dialog import Dialog
|
from coretk.dialogs.dialog import Dialog
|
||||||
|
|
||||||
CORE_DEFAULT_GROUPS = ["EMANE", "FRR", "ProtoSvc", "Quagga", "Security", "Utility"]
|
|
||||||
DEFAULT_GROUP_RADIO_VALUE = {
|
|
||||||
"EMANE": 1,
|
|
||||||
"FRR": 2,
|
|
||||||
"ProtoSvc": 3,
|
|
||||||
"Quagga": 4,
|
|
||||||
"Security": 5,
|
|
||||||
"Utility": 6,
|
|
||||||
}
|
|
||||||
DEFAULT_GROUP_SERVICES = {
|
|
||||||
"EMANE": ["transportd"],
|
|
||||||
"FRR": [
|
|
||||||
"FRRBable",
|
|
||||||
"FRRBGP",
|
|
||||||
"FRROSPFv2",
|
|
||||||
"FRROSPFv3",
|
|
||||||
"FRRpimd",
|
|
||||||
"FRRRIP",
|
|
||||||
"FRRRIPNG",
|
|
||||||
"FRRzebra",
|
|
||||||
],
|
|
||||||
"ProtoSvc": ["MGEN_Sink", "MgenActor", "SMF"],
|
|
||||||
"Quagga": [
|
|
||||||
"Babel",
|
|
||||||
"BGP",
|
|
||||||
"OSPFv2",
|
|
||||||
"OSPFv3",
|
|
||||||
"OSPFv3MDR",
|
|
||||||
"RIP",
|
|
||||||
"RIPNG",
|
|
||||||
"Xpimd",
|
|
||||||
"zebra",
|
|
||||||
],
|
|
||||||
"Security": ["Firewall", "IPsec", "NAT", "VPNClient", "VPNServer"],
|
|
||||||
"Utility": [
|
|
||||||
"atd",
|
|
||||||
"DefaultMulticastRoute",
|
|
||||||
"DefaultRoute",
|
|
||||||
"DHCP",
|
|
||||||
"DHCPClient",
|
|
||||||
"FTP",
|
|
||||||
"HTTP",
|
|
||||||
"IPForward ",
|
|
||||||
"pcap",
|
|
||||||
"radvd",
|
|
||||||
"SSH",
|
|
||||||
"StaticRoute",
|
|
||||||
"ucarp",
|
|
||||||
"UserDefined",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NodeServicesDialog(Dialog):
|
class NodeServicesDialog(Dialog):
|
||||||
def __init__(self, master, app, canvas_node):
|
def __init__(self, master, app, canvas_node):
|
||||||
|
@ -66,6 +14,7 @@ class NodeServicesDialog(Dialog):
|
||||||
self.core_groups = []
|
self.core_groups = []
|
||||||
self.service_to_config = None
|
self.service_to_config = None
|
||||||
self.config_frame = None
|
self.config_frame = None
|
||||||
|
self.services_list = None
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
@ -110,7 +59,7 @@ class NodeServicesDialog(Dialog):
|
||||||
listbox.grid(row=1, column=0, sticky="nsew")
|
listbox.grid(row=1, column=0, sticky="nsew")
|
||||||
listbox.bind("<<ListboxSelect>>", self.handle_group_change)
|
listbox.bind("<<ListboxSelect>>", self.handle_group_change)
|
||||||
|
|
||||||
for group in CORE_DEFAULT_GROUPS:
|
for group in sorted(self.app.core.services):
|
||||||
listbox.insert(tk.END, group)
|
listbox.insert(tk.END, group)
|
||||||
|
|
||||||
scrollbar.config(command=listbox.yview)
|
scrollbar.config(command=listbox.yview)
|
||||||
|
@ -127,7 +76,7 @@ class NodeServicesDialog(Dialog):
|
||||||
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL)
|
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL)
|
||||||
scrollbar.grid(row=1, column=1, sticky="ns")
|
scrollbar.grid(row=1, column=1, sticky="ns")
|
||||||
|
|
||||||
listbox = tk.Listbox(
|
self.services_list = tk.Listbox(
|
||||||
frame,
|
frame,
|
||||||
selectmode=tk.SINGLE,
|
selectmode=tk.SINGLE,
|
||||||
yscrollcommand=scrollbar.set,
|
yscrollcommand=scrollbar.set,
|
||||||
|
@ -135,10 +84,10 @@ class NodeServicesDialog(Dialog):
|
||||||
highlightthickness=0.5,
|
highlightthickness=0.5,
|
||||||
bd=0,
|
bd=0,
|
||||||
)
|
)
|
||||||
listbox.grid(row=1, column=0, sticky="nsew")
|
self.services_list.grid(row=1, column=0, sticky="nsew")
|
||||||
listbox.bind("<<ListboxSelect>>", self.handle_service_change)
|
self.services_list.bind("<<ListboxSelect>>", self.handle_service_change)
|
||||||
|
|
||||||
scrollbar.config(command=listbox.yview)
|
scrollbar.config(command=self.services_list.yview)
|
||||||
|
|
||||||
def draw_current_services(self):
|
def draw_current_services(self):
|
||||||
frame = tk.Frame(self.config_frame)
|
frame = tk.Frame(self.config_frame)
|
||||||
|
@ -188,11 +137,9 @@ class NodeServicesDialog(Dialog):
|
||||||
self.display_group_services(s)
|
self.display_group_services(s)
|
||||||
|
|
||||||
def display_group_services(self, group_name):
|
def display_group_services(self, group_name):
|
||||||
group_services_frame = self.config_frame.grid_slaves(row=0, column=1)[0]
|
self.services_list.delete(0, tk.END)
|
||||||
listbox = group_services_frame.grid_slaves(row=1, column=0)[0]
|
for service in sorted(self.app.core.services[group_name], key=lambda x: x.name):
|
||||||
listbox.delete(0, tk.END)
|
self.services_list.insert(tk.END, service.name)
|
||||||
for s in DEFAULT_GROUP_SERVICES[group_name]:
|
|
||||||
listbox.insert(tk.END, s)
|
|
||||||
|
|
||||||
def handle_service_change(self, event):
|
def handle_service_change(self, event):
|
||||||
print("select group service")
|
print("select group service")
|
||||||
|
|
148
coretk/coretk/dialogs/observerwidgets.py
Normal file
148
coretk/coretk/dialogs/observerwidgets.py
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import tkinter as tk
|
||||||
|
|
||||||
|
from coretk.dialogs.dialog import Dialog
|
||||||
|
|
||||||
|
|
||||||
|
class Widget:
|
||||||
|
def __init__(self, name, command):
|
||||||
|
self.name = name
|
||||||
|
self.command = command
|
||||||
|
|
||||||
|
|
||||||
|
class ObserverWidgetsDialog(Dialog):
|
||||||
|
def __init__(self, master, app):
|
||||||
|
super().__init__(master, app, "Observer Widgets", modal=True)
|
||||||
|
self.config_widgets = {}
|
||||||
|
self.widgets = None
|
||||||
|
self.save_button = None
|
||||||
|
self.delete_button = None
|
||||||
|
self.selected = None
|
||||||
|
self.selected_index = None
|
||||||
|
self.name = tk.StringVar()
|
||||||
|
self.command = tk.StringVar()
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.columnconfigure(0, weight=1)
|
||||||
|
self.rowconfigure(0, weight=1)
|
||||||
|
self.draw_widgets()
|
||||||
|
self.draw_widget_fields()
|
||||||
|
self.draw_widget_buttons()
|
||||||
|
self.draw_apply_buttons()
|
||||||
|
|
||||||
|
def draw_widgets(self):
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(sticky="nsew")
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL)
|
||||||
|
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
|
self.widgets = tk.Listbox(
|
||||||
|
frame, selectmode=tk.SINGLE, yscrollcommand=scrollbar.set
|
||||||
|
)
|
||||||
|
self.widgets.grid(row=0, column=0, sticky="nsew")
|
||||||
|
self.widgets.bind("<<ListboxSelect>>", self.handle_widget_change)
|
||||||
|
|
||||||
|
scrollbar.config(command=self.widgets.yview)
|
||||||
|
|
||||||
|
def draw_widget_fields(self):
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(sticky="ew")
|
||||||
|
frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
label = tk.Label(frame, text="Name")
|
||||||
|
label.grid(row=0, column=0, sticky="w")
|
||||||
|
entry = tk.Entry(frame, textvariable=self.name)
|
||||||
|
entry.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
|
label = tk.Label(frame, text="Command")
|
||||||
|
label.grid(row=1, column=0, sticky="w")
|
||||||
|
entry = tk.Entry(frame, textvariable=self.command)
|
||||||
|
entry.grid(row=1, column=1, sticky="ew")
|
||||||
|
|
||||||
|
def draw_widget_buttons(self):
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(pady=2, sticky="ew")
|
||||||
|
for i in range(3):
|
||||||
|
frame.columnconfigure(i, weight=1)
|
||||||
|
|
||||||
|
button = tk.Button(frame, text="Create", command=self.click_create)
|
||||||
|
button.grid(row=0, column=0, sticky="ew")
|
||||||
|
|
||||||
|
self.save_button = tk.Button(
|
||||||
|
frame, text="Save", state=tk.DISABLED, command=self.click_save
|
||||||
|
)
|
||||||
|
self.save_button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
|
self.delete_button = tk.Button(
|
||||||
|
frame, text="Delete", state=tk.DISABLED, command=self.click_delete
|
||||||
|
)
|
||||||
|
self.delete_button.grid(row=0, column=2, sticky="ew")
|
||||||
|
|
||||||
|
def draw_apply_buttons(self):
|
||||||
|
frame = tk.Frame(self)
|
||||||
|
frame.grid(sticky="ew")
|
||||||
|
for i in range(2):
|
||||||
|
frame.columnconfigure(i, weight=1)
|
||||||
|
|
||||||
|
button = tk.Button(
|
||||||
|
frame, text="Save Configuration", command=self.click_save_configuration
|
||||||
|
)
|
||||||
|
button.grid(row=0, column=0, sticky="ew")
|
||||||
|
|
||||||
|
button = tk.Button(frame, text="Cancel", command=self.destroy)
|
||||||
|
button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
|
def click_save_configuration(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def click_create(self):
|
||||||
|
name = self.name.get()
|
||||||
|
if name not in self.config_widgets:
|
||||||
|
command = self.command.get()
|
||||||
|
widget = Widget(name, command)
|
||||||
|
self.config_widgets[name] = widget
|
||||||
|
self.widgets.insert(tk.END, name)
|
||||||
|
|
||||||
|
def click_save(self):
|
||||||
|
name = self.name.get()
|
||||||
|
if self.selected:
|
||||||
|
previous_name = self.selected
|
||||||
|
self.selected = name
|
||||||
|
widget = self.config_widgets.pop(previous_name)
|
||||||
|
widget.name = name
|
||||||
|
widget.command = self.command.get()
|
||||||
|
self.config_widgets[name] = widget
|
||||||
|
self.widgets.delete(self.selected_index)
|
||||||
|
self.widgets.insert(self.selected_index, name)
|
||||||
|
self.widgets.selection_set(self.selected_index)
|
||||||
|
|
||||||
|
def click_delete(self):
|
||||||
|
if self.selected:
|
||||||
|
self.widgets.delete(self.selected_index)
|
||||||
|
del self.config_widgets[self.selected]
|
||||||
|
self.selected = None
|
||||||
|
self.selected_index = None
|
||||||
|
self.name.set("")
|
||||||
|
self.command.set("")
|
||||||
|
self.widgets.selection_clear(0, tk.END)
|
||||||
|
self.save_button.config(state=tk.DISABLED)
|
||||||
|
self.delete_button.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
def handle_widget_change(self, event):
|
||||||
|
selection = self.widgets.curselection()
|
||||||
|
if selection:
|
||||||
|
self.selected_index = selection[0]
|
||||||
|
self.selected = self.widgets.get(self.selected_index)
|
||||||
|
widget = self.config_widgets[self.selected]
|
||||||
|
self.name.set(widget.name)
|
||||||
|
self.command.set(widget.command)
|
||||||
|
self.save_button.config(state=tk.NORMAL)
|
||||||
|
self.delete_button.config(state=tk.NORMAL)
|
||||||
|
else:
|
||||||
|
self.selected_index = None
|
||||||
|
self.selected = None
|
||||||
|
self.save_button.config(state=tk.DISABLED)
|
||||||
|
self.delete_button.config(state=tk.DISABLED)
|
|
@ -1,5 +1,6 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
|
from coretk import appdirs
|
||||||
from coretk.coreclient import CoreServer
|
from coretk.coreclient import CoreServer
|
||||||
from coretk.dialogs.dialog import Dialog
|
from coretk.dialogs.dialog import Dialog
|
||||||
|
|
||||||
|
@ -39,12 +40,7 @@ class ServersDialog(Dialog):
|
||||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
self.servers = tk.Listbox(
|
self.servers = tk.Listbox(
|
||||||
frame,
|
frame, selectmode=tk.SINGLE, yscrollcommand=scrollbar.set
|
||||||
selectmode=tk.SINGLE,
|
|
||||||
yscrollcommand=scrollbar.set,
|
|
||||||
relief=tk.FLAT,
|
|
||||||
highlightthickness=0.5,
|
|
||||||
bd=0,
|
|
||||||
)
|
)
|
||||||
self.servers.grid(row=0, column=0, sticky="nsew")
|
self.servers.grid(row=0, column=0, sticky="nsew")
|
||||||
self.servers.bind("<<ListboxSelect>>", self.handle_server_change)
|
self.servers.bind("<<ListboxSelect>>", self.handle_server_change)
|
||||||
|
@ -113,7 +109,14 @@ class ServersDialog(Dialog):
|
||||||
button.grid(row=0, column=1, sticky="ew")
|
button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
def click_save_configuration(self):
|
def click_save_configuration(self):
|
||||||
pass
|
servers = []
|
||||||
|
for name in sorted(self.app.core.servers):
|
||||||
|
server = self.app.core.servers[name]
|
||||||
|
servers.append(
|
||||||
|
{"name": server.name, "address": server.address, "port": server.port}
|
||||||
|
)
|
||||||
|
self.app.config["servers"] = servers
|
||||||
|
appdirs.save_config(self.app.config)
|
||||||
|
|
||||||
def click_create(self):
|
def click_create(self):
|
||||||
name = self.name.get()
|
name = self.name.get()
|
||||||
|
@ -126,7 +129,7 @@ class ServersDialog(Dialog):
|
||||||
|
|
||||||
def click_save(self):
|
def click_save(self):
|
||||||
name = self.name.get()
|
name = self.name.get()
|
||||||
if self.selected and name not in self.app.core.servers:
|
if self.selected:
|
||||||
previous_name = self.selected
|
previous_name = self.selected
|
||||||
self.selected = name
|
self.selected = name
|
||||||
server = self.app.core.servers.pop(previous_name)
|
server = self.app.core.servers.pop(previous_name)
|
||||||
|
|
|
@ -5,7 +5,7 @@ wlan configuration
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
from coretk.dialogs.dialog import Dialog
|
from coretk.dialogs.dialog import Dialog
|
||||||
from coretk.dialogs.nodeicon import NodeIconDialog
|
from coretk.dialogs.nodeicon import IconDialog
|
||||||
|
|
||||||
|
|
||||||
class WlanConfigDialog(Dialog):
|
class WlanConfigDialog(Dialog):
|
||||||
|
@ -167,7 +167,9 @@ class WlanConfigDialog(Dialog):
|
||||||
button.grid(row=0, column=1, padx=2, sticky="ew")
|
button.grid(row=0, column=1, padx=2, sticky="ew")
|
||||||
|
|
||||||
def click_icon(self):
|
def click_icon(self):
|
||||||
dialog = NodeIconDialog(self, self.app, self.canvas_node)
|
dialog = IconDialog(
|
||||||
|
self, self.app, self.canvas_node.name, self.canvas_node.image
|
||||||
|
)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
if dialog.image:
|
if dialog.image:
|
||||||
self.image = dialog.image
|
self.image = dialog.image
|
||||||
|
|
|
@ -11,6 +11,7 @@ from coretk.appdirs import XML_PATH
|
||||||
from coretk.dialogs.canvasbackground import CanvasBackgroundDialog
|
from coretk.dialogs.canvasbackground import CanvasBackgroundDialog
|
||||||
from coretk.dialogs.canvassizeandscale import SizeAndScaleDialog
|
from coretk.dialogs.canvassizeandscale import SizeAndScaleDialog
|
||||||
from coretk.dialogs.hooks import HooksDialog
|
from coretk.dialogs.hooks import HooksDialog
|
||||||
|
from coretk.dialogs.observerwidgets import ObserverWidgetsDialog
|
||||||
from coretk.dialogs.servers import ServersDialog
|
from coretk.dialogs.servers import ServersDialog
|
||||||
from coretk.dialogs.sessionoptions import SessionOptionsDialog
|
from coretk.dialogs.sessionoptions import SessionOptionsDialog
|
||||||
from coretk.dialogs.sessions import SessionsDialog
|
from coretk.dialogs.sessions import SessionsDialog
|
||||||
|
@ -403,3 +404,7 @@ class MenuAction:
|
||||||
logging.debug("Click session emulation servers")
|
logging.debug("Click session emulation servers")
|
||||||
dialog = ServersDialog(self.app, self.app)
|
dialog = ServersDialog(self.app, self.app)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
|
def edit_observer_widgets(self):
|
||||||
|
dialog = ObserverWidgetsDialog(self.app, self.app)
|
||||||
|
dialog.show()
|
||||||
|
|
93
coretk/coretk/widgets.py
Normal file
93
coretk/coretk/widgets.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import tkinter as tk
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
|
||||||
|
class FrameScroll(tk.LabelFrame):
|
||||||
|
def __init__(self, master=None, cnf={}, **kw):
|
||||||
|
super().__init__(master, cnf, **kw)
|
||||||
|
self.rowconfigure(0, weight=1)
|
||||||
|
self.columnconfigure(0, weight=1)
|
||||||
|
self.columnconfigure(1, weight=1)
|
||||||
|
self.canvas = tk.Canvas(self, highlightthickness=0)
|
||||||
|
self.canvas.grid(row=0, columnspan=2, sticky="nsew", padx=2, pady=2)
|
||||||
|
self.canvas.columnconfigure(0, weight=1)
|
||||||
|
self.canvas.rowconfigure(0, weight=1)
|
||||||
|
self.scrollbar = tk.Scrollbar(
|
||||||
|
self, orient="vertical", command=self.canvas.yview
|
||||||
|
)
|
||||||
|
self.scrollbar.grid(row=0, column=2, sticky="ns")
|
||||||
|
self.frame = tk.Frame(self.canvas, padx=2, pady=2)
|
||||||
|
self.frame.columnconfigure(0, weight=1)
|
||||||
|
self.frame_id = self.canvas.create_window(0, 0, anchor="nw", window=self.frame)
|
||||||
|
self.canvas.update_idletasks()
|
||||||
|
self.canvas.configure(
|
||||||
|
scrollregion=self.canvas.bbox("all"), yscrollcommand=self.scrollbar.set
|
||||||
|
)
|
||||||
|
self.frame.bind(
|
||||||
|
"<Configure>",
|
||||||
|
lambda event: self.canvas.configure(scrollregion=self.canvas.bbox("all")),
|
||||||
|
)
|
||||||
|
self.canvas.bind(
|
||||||
|
"<Configure>",
|
||||||
|
lambda event: self.canvas.itemconfig(self.frame_id, width=event.width),
|
||||||
|
)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
for widget in self.frame.winfo_children():
|
||||||
|
widget.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class ListboxScroll(tk.LabelFrame):
|
||||||
|
def __init__(self, master=None, cnf={}, **kw):
|
||||||
|
super().__init__(master, cnf, **kw)
|
||||||
|
self.columnconfigure(0, weight=1)
|
||||||
|
self.rowconfigure(0, weight=1)
|
||||||
|
self.scrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
|
||||||
|
self.scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
self.listbox = tk.Listbox(
|
||||||
|
self, selectmode=tk.SINGLE, yscrollcommand=self.scrollbar.set
|
||||||
|
)
|
||||||
|
self.listbox.grid(row=0, column=0, sticky="nsew")
|
||||||
|
self.scrollbar.config(command=self.listbox.yview)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckboxList(tk.LabelFrame):
|
||||||
|
def __init__(self, master=None, cnf={}, clicked=None, **kw):
|
||||||
|
super().__init__(master, cnf, **kw)
|
||||||
|
self.clicked = clicked
|
||||||
|
self.rowconfigure(0, weight=1)
|
||||||
|
self.columnconfigure(0, weight=1)
|
||||||
|
self.columnconfigure(1, weight=1)
|
||||||
|
self.canvas = tk.Canvas(self, highlightthickness=0)
|
||||||
|
self.canvas.grid(row=0, columnspan=2, sticky="nsew", padx=2, pady=2)
|
||||||
|
self.canvas.columnconfigure(0, weight=1)
|
||||||
|
self.canvas.rowconfigure(0, weight=1)
|
||||||
|
self.scrollbar = tk.Scrollbar(
|
||||||
|
self, orient="vertical", command=self.canvas.yview
|
||||||
|
)
|
||||||
|
self.scrollbar.grid(row=0, column=2, sticky="ns")
|
||||||
|
self.frame = tk.Frame(self.canvas, padx=2, pady=2)
|
||||||
|
self.frame.columnconfigure(0, weight=1)
|
||||||
|
self.frame_id = self.canvas.create_window(0, 0, anchor="nw", window=self.frame)
|
||||||
|
self.canvas.update_idletasks()
|
||||||
|
self.canvas.configure(
|
||||||
|
scrollregion=self.canvas.bbox("all"), yscrollcommand=self.scrollbar.set
|
||||||
|
)
|
||||||
|
self.frame.bind(
|
||||||
|
"<Configure>",
|
||||||
|
lambda event: self.canvas.configure(scrollregion=self.canvas.bbox("all")),
|
||||||
|
)
|
||||||
|
self.canvas.bind(
|
||||||
|
"<Configure>",
|
||||||
|
lambda event: self.canvas.itemconfig(self.frame_id, width=event.width),
|
||||||
|
)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
for widget in self.frame.winfo_children():
|
||||||
|
widget.destroy()
|
||||||
|
|
||||||
|
def add(self, name):
|
||||||
|
var = tk.BooleanVar()
|
||||||
|
func = partial(self.clicked, name, var)
|
||||||
|
checkbox = tk.Checkbutton(self.frame, text=name, variable=var, command=func)
|
||||||
|
checkbox.grid(sticky="w")
|
Loading…
Reference in a new issue