Removed Etrigan GUI support

This commit is contained in:
simonmicro 2022-12-09 19:45:12 +01:00
parent 77e545d0fe
commit f1fa5b794f
No known key found for this signature in database
GPG key ID: 033A4D4CE4E063D6
3 changed files with 1 additions and 1471 deletions

View file

@ -274,9 +274,7 @@ class ShellMessage(object):
ShellMessage.indx += 1
def print_logging_setup(self, logger, async_flag, formatter = logging.Formatter('%(name)s %(message)s')):
from pykms_GuiBase import gui_redirector
stream = gui_redirector(StringIO())
handler = logging.StreamHandler(stream)
handler = logging.StreamHandler(StringIO())
handler.name = 'LogStream'
handler.setLevel(logging.INFO)
handler.setFormatter(formatter)
@ -293,9 +291,6 @@ class ShellMessage(object):
def print_logging(self, toprint):
if (self.nshell and ((0 in self.nshell) or (2 in self.nshell and not ShellMessage.viewclt))) or ShellMessage.indx == 0:
from pykms_GuiBase import gui_redirector_setup, gui_redirector_clear
gui_redirector_setup()
gui_redirector_clear()
self.print_logging_setup(ShellMessage.loggersrv_pty, ShellMessage.asyncmsgsrv)
self.print_logging_setup(ShellMessage.loggerclt_pty, ShellMessage.asyncmsgclt)

View file

@ -1,948 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import threading
from time import sleep
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from tkinter import filedialog
import tkinter.font as tkFont
from pykms_Server import srv_options, srv_version, srv_config, server_terminate, serverqueue, serverthread
from pykms_GuiMisc import ToolTip, TextDoubleScroll, TextRedirect, ListboxOfRadiobuttons
from pykms_GuiMisc import custom_background, custom_pages
from pykms_Client import clt_options, clt_version, clt_config, client_thread
gui_version = "py-kms_gui_v3.0"
__license__ = "MIT License"
__author__ = u"Matteo an <SystemRage@protonmail.com>"
__copyright__ = "© Copyright 2020"
__url__ = "https://github.com/SystemRage/py-kms"
gui_description = "A GUI for py-kms."
##---------------------------------------------------------------------------------------------------------------------------------------------------------
def get_ip_address():
if os.name == 'posix':
import subprocess
ip = subprocess.getoutput("hostname -I")
elif os.name == 'nt':
import socket
ip = socket.gethostbyname(socket.gethostname())
else:
ip = 'Unknown'
return ip
def gui_redirector(stream, redirect_to = TextRedirect.Pretty, redirect_conditio = True, stderr_side = "srv"):
global txsrv, txclt, txcol
if redirect_conditio:
if stream == 'stdout':
sys.stdout = redirect_to(txsrv, txclt, txcol)
elif stream == 'stderr':
sys.stderr = redirect_to(txsrv, txclt, txcol, stderr_side)
else:
stream = redirect_to(txsrv, txclt, txcol)
return stream
def gui_redirector_setup():
TextRedirect.Pretty.tag_num = 0
TextRedirect.Pretty.newlinecut = [-1, -2, -4, -5]
def gui_redirector_clear():
global txsrv, oysrv
try:
if oysrv:
txsrv.configure(state = 'normal')
txsrv.delete('1.0', 'end')
txsrv.configure(state = 'disabled')
except:
# self.onlysrv not defined (menu not used)
pass
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
class KmsGui(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wraplength = 200
serverthread.with_gui = True
self.validation_int = (self.register(self.validate_int), "%S")
self.validation_float = (self.register(self.validate_float), "%P")
## Define fonts and colors.
self.customfonts = {'btn' : tkFont.Font(family = 'Fixedsys', size = 11, weight = 'bold'),
'oth' : tkFont.Font(family = 'Times', size = 9, weight = 'bold'),
'opt' : tkFont.Font(family = 'Fixedsys', size = 9, weight = 'bold'),
'lst' : tkFont.Font(family = 'Fixedsys', size = 8, weight = 'bold', slant = 'italic'),
'msg' : tkFont.Font(family = 'Monospace', size = 6), # need a monospaced type (like courier, etc..).
}
self.customcolors = { 'black' : '#000000',
'white' : '#FFFFFF',
'green' : '#00EE76',
'yellow' : '#FFFF00',
'magenta' : '#CD00CD',
'orange' : '#FFA500',
'red' : '#FF4500',
'blue' : '#1E90FF',
'cyan' : '#AFEEEE',
'lavender': '#E6E6FA',
'brown' : '#A52A2A',
}
self.option_add('*TCombobox*Listbox.font', self.customfonts['lst'])
self.gui_create()
def invert(self, widgets = []):
for widget in widgets:
if widget['state'] == 'normal':
widget.configure(state = 'disabled')
elif widget['state'] == 'disabled':
widget.configure(state = 'normal')
def gui_menu(self):
self.onlysrv, self.onlyclt = (False for _ in range(2))
menubar = tk.Menu(self)
prefmenu = tk.Menu(menubar, tearoff = 0, font = ("Noto Sans Regular", 10), borderwidth = 3, relief = 'ridge')
menubar.add_cascade(label = 'Preferences', menu = prefmenu)
prefmenu.add_command(label = 'Enable server-side mode', command = lambda: self.pref_onlysrv(prefmenu))
prefmenu.add_command(label = 'Enable client-side mode', command = lambda: self.pref_onlyclt(prefmenu))
self.config(menu = menubar)
def pref_onlysrv(self, menu):
global oysrv
if self.onlyclt or serverthread.is_running_server:
return
self.onlysrv = not self.onlysrv
if self.onlysrv:
menu.entryconfigure(0, label = 'Disable server-side mode')
self.clt_on_show(force_remove = True)
else:
menu.entryconfigure(0, label = 'Enable server-side mode')
self.invert(widgets = [self.shbtnclt])
oysrv = self.onlysrv
def pref_onlyclt(self, menu):
if self.onlysrv or serverthread.is_running_server:
return
self.onlyclt = not self.onlyclt
if self.onlyclt:
menu.entryconfigure(1, label = 'Disable client-side mode')
if self.shbtnclt['text'] == 'SHOW\nCLIENT':
self.clt_on_show(force_view = True)
self.optsrvwin.grid_remove()
self.msgsrvwin.grid_remove()
gui_redirector('stderr', redirect_to = TextRedirect.Stderr, stderr_side = "clt")
else:
menu.entryconfigure(1, label = 'Enable client-side mode')
self.optsrvwin.grid()
self.msgsrvwin.grid()
gui_redirector('stderr', redirect_to = TextRedirect.Stderr)
self.invert(widgets = [self.runbtnsrv, self.shbtnclt, self.runbtnclt])
def gui_create(self):
## Create server gui
self.gui_srv()
## Create client gui + other operations.
self.gui_complete()
## Create menu.
self.gui_menu()
## Create globals for printing process (redirect stdout).
global txsrv, txclt, txcol
txsrv = self.textboxsrv.get()
txclt = self.textboxclt.get()
txcol = self.customcolors
## Redirect stderr.
gui_redirector('stderr', redirect_to = TextRedirect.Stderr)
def gui_pages_show(self, pagename, side):
# https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter
# https://www.reddit.com/r/learnpython/comments/7xxtsy/trying_to_understand_tkinter_and_how_to_switch/
pageside = self.pagewidgets[side]
tk.Misc.lift(pageside["PageWin"][pagename], aboveThis = None)
keylist = list(pageside["PageWin"].keys())
for elem in [pageside["BtnAni"], pageside["LblAni"]]:
if pagename == "PageStart":
elem["Left"].config(state = "disabled")
if len(keylist) == 2:
elem["Right"].config(state = "normal")
elif pagename == "PageEnd":
elem["Right"].config(state = "disabled")
if len(keylist) == 2:
elem["Left"].config(state = "normal")
else:
for where in ["Left", "Right"]:
elem[where].config(state = "normal")
if pagename != "PageStart":
page_l = keylist[keylist.index(pagename) - 1]
pageside["BtnAni"]["Left"]['command'] = lambda pag=page_l, pos=side: self.gui_pages_show(pag, pos)
if pagename != "PageEnd":
page_r = keylist[keylist.index(pagename) + 1]
pageside["BtnAni"]["Right"]['command'] = lambda pag=page_r, pos=side: self.gui_pages_show(pag, pos)
def gui_pages_buttons(self, parent, side):
btnwin = tk.Canvas(parent, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
btnwin.grid(row = 14, column = 2, padx = 2, pady = 2, sticky = 'nsew')
btnwin.grid_columnconfigure(1, weight = 1)
self.pagewidgets[side]["BtnWin"] = btnwin
for position in ["Left", "Right"]:
if position == "Left":
col = [0, 0, 1]
stick = 'e'
elif position == "Right":
col = [2, 1, 0]
stick = 'w'
aniwin = tk.Canvas(btnwin, background = self.customcolors['white'], borderwidth = 0, relief = 'ridge')
aniwin.grid(row = 0, column = col[0], padx = 5, pady = 5, sticky = 'nsew')
self.pagewidgets[side]["AniWin"][position] = aniwin
lblani = tk.Label(aniwin, width = 1, height = 1)
lblani.grid(row = 0, column = col[1], padx = 2, pady = 2, sticky = stick)
self.pagewidgets[side]["LblAni"][position] = lblani
btnani = tk.Button(aniwin)
btnani.grid(row = 0, column = col[2], padx = 2, pady = 2, sticky = stick)
self.pagewidgets[side]["BtnAni"][position] = btnani
## Customize buttons.
custom_pages(self, side)
def gui_pages_create(self, parent, side, create = {}):
self.pagewidgets.update({side : {"PageWin" : create,
"BtnWin" : None,
"BtnAni" : {"Left" : None,
"Right" : None},
"AniWin" : {"Left" : None,
"Right" : None},
"LblAni" : {"Left" : None,
"Right" : None},
}
})
for pagename in self.pagewidgets[side]["PageWin"].keys():
page = tk.Canvas(parent, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
self.pagewidgets[side]["PageWin"][pagename] = page
page.grid(row = 0, column = 2, padx = 2, pady = 2, sticky = "nsew")
page.grid_columnconfigure(1, weight = 1)
self.gui_pages_buttons(parent = parent, side = side)
self.gui_pages_show("PageStart", side = side)
def gui_store(self, side, typewidgets):
stored = []
for pagename in self.pagewidgets[side]["PageWin"].keys():
for widget in self.pagewidgets[side]["PageWin"][pagename].winfo_children():
if widget.winfo_class() in typewidgets:
stored.append(widget)
return stored
def gui_srv(self):
## Create main containers. ------------------------------------------------------------------------------------------------------------------
self.masterwin = tk.Canvas(self, borderwidth = 3, relief = tk.RIDGE)
self.btnsrvwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
self.optsrvwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
self.msgsrvwin = tk.Frame(self.masterwin, background = self.customcolors['black'], relief = 'ridge', width = 300, height = 200)
## Layout main containers.
self.masterwin.grid(row = 0, column = 0, sticky = 'nsew')
self.btnsrvwin.grid(row = 0, column = 1, padx = 2, pady = 2, sticky = 'nw')
self.optsrvwin.grid(row = 0, column = 2, padx = 2, pady = 2, sticky = 'nsew')
self.optsrvwin.grid_rowconfigure(0, weight = 1)
self.optsrvwin.grid_columnconfigure(1, weight = 1)
self.pagewidgets = {}
## Subpages of "optsrvwin".
self.gui_pages_create(parent = self.optsrvwin, side = "Srv", create = {"PageStart": None,
"PageEnd": None})
## Continue to grid.
self.msgsrvwin.grid(row = 1, column = 2, padx = 1, pady = 1, sticky = 'nsew')
self.msgsrvwin.grid_propagate(False)
self.msgsrvwin.grid_columnconfigure(0, weight = 1)
self.msgsrvwin.grid_rowconfigure(0, weight = 1)
## Create widgets (btnsrvwin) ---------------------------------------------------------------------------------------------------------------
self.statesrv = tk.Label(self.btnsrvwin, text = 'Server\nState:\nStopped', font = self.customfonts['oth'],
foreground = self.customcolors['red'])
self.runbtnsrv = tk.Button(self.btnsrvwin, text = 'START\nSERVER', background = self.customcolors['green'],
foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'],
command = self.srv_on_start)
self.shbtnclt = tk.Button(self.btnsrvwin, text = 'SHOW\nCLIENT', background = self.customcolors['magenta'],
foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'],
command = self.clt_on_show)
self.defaubtnsrv = tk.Button(self.btnsrvwin, text = 'DEFAULTS', background = self.customcolors['brown'],
foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'],
command = self.on_defaults)
self.clearbtnsrv = tk.Button(self.btnsrvwin, text = 'CLEAR', background = self.customcolors['orange'],
foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'],
command = lambda: self.on_clear([txsrv, txclt]))
self.exitbtnsrv = tk.Button(self.btnsrvwin, text = 'EXIT', background = self.customcolors['black'],
foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'],
command = self.on_exit)
## Layout widgets (btnsrvwin)
self.statesrv.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'ew')
self.runbtnsrv.grid(row = 1, column = 0, padx = 2, pady = 2, sticky = 'ew')
self.shbtnclt.grid(row = 2, column = 0, padx = 2, pady = 2, sticky = 'ew')
self.defaubtnsrv.grid(row = 3, column = 0, padx = 2, pady = 2, sticky = 'ew')
self.clearbtnsrv.grid(row = 4, column = 0, padx = 2, pady = 2, sticky = 'ew')
self.exitbtnsrv.grid(row = 5, column = 0, padx = 2, pady = 2, sticky = 'ew')
## Create widgets (optsrvwin:Srv:PageWin:PageStart) -----------------------------------------------------------------------------------------
# Version.
ver = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"],
text = 'You are running server version: ' + srv_version, font = self.customfonts['oth'],
foreground = self.customcolors['red'])
# Ip Address.
srvipaddlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'IP Address: ', font = self.customfonts['opt'])
self.srvipadd = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'ip')
self.srvipadd.insert('end', srv_options['ip']['def'])
ToolTip(self.srvipadd, text = srv_options['ip']['help'], wraplength = self.wraplength)
myipadd = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Your IP address is: {}'.format(get_ip_address()),
font = self.customfonts['oth'], foreground = self.customcolors['red'])
# Port.
srvportlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Port: ', font = self.customfonts['opt'])
self.srvport = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'port',
validate = "key", validatecommand = self.validation_int)
self.srvport.insert('end', str(srv_options['port']['def']))
ToolTip(self.srvport, text = srv_options['port']['help'], wraplength = self.wraplength)
# EPID.
epidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'EPID: ', font = self.customfonts['opt'])
self.epid = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'epid')
self.epid.insert('end', str(srv_options['epid']['def']))
ToolTip(self.epid, text = srv_options['epid']['help'], wraplength = self.wraplength)
# LCID.
lcidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'LCID: ', font = self.customfonts['opt'])
self.lcid = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lcid',
validate = "key", validatecommand = self.validation_int)
self.lcid.insert('end', str(srv_options['lcid']['def']))
ToolTip(self.lcid, text = srv_options['lcid']['help'], wraplength = self.wraplength)
# HWID.
hwidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'HWID: ', font = self.customfonts['opt'])
self.hwid = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = (str(srv_options['hwid']['def']), 'RANDOM'),
width = 17, height = 10, font = self.customfonts['lst'], name = 'hwid')
self.hwid.set(str(srv_options['hwid']['def']))
ToolTip(self.hwid, text = srv_options['hwid']['help'], wraplength = self.wraplength)
# Client Count
countlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Client Count: ', font = self.customfonts['opt'])
self.count = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'count')
self.count.insert('end', str(srv_options['count']['def']))
ToolTip(self.count, text = srv_options['count']['help'], wraplength = self.wraplength)
# Activation Interval.
activlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Activation Interval: ', font = self.customfonts['opt'])
self.activ = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'activation',
validate = "key", validatecommand = self.validation_int)
self.activ.insert('end', str(srv_options['activation']['def']))
ToolTip(self.activ, text = srv_options['activation']['help'], wraplength = self.wraplength)
# Renewal Interval.
renewlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Renewal Interval: ', font = self.customfonts['opt'])
self.renew = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'renewal',
validate = "key", validatecommand = self.validation_int)
self.renew.insert('end', str(srv_options['renewal']['def']))
ToolTip(self.renew, text = srv_options['renewal']['help'], wraplength = self.wraplength)
# Logfile.
srvfilelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logfile Path / Name: ', font = self.customfonts['opt'])
self.srvfile = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lfile')
self.srvfile.insert('end', srv_options['lfile']['def'])
self.srvfile.xview_moveto(1)
ToolTip(self.srvfile, text = srv_options['lfile']['help'], wraplength = self.wraplength)
srvfilebtnwin = tk.Button(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Browse', font = self.customfonts['opt'],
command = lambda: self.on_browse(self.srvfile, srv_options))
# Loglevel.
srvlevellbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.customfonts['opt'])
self.srvlevel = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = tuple(srv_options['llevel']['choi']),
width = 10, height = 10, font = self.customfonts['lst'], state = "readonly", name = 'llevel')
self.srvlevel.set(srv_options['llevel']['def'])
ToolTip(self.srvlevel, text = srv_options['llevel']['help'], wraplength = self.wraplength)
# Logsize.
srvsizelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.customfonts['opt'])
self.srvsize = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lsize',
validate = "key", validatecommand = self.validation_float)
self.srvsize.insert('end', srv_options['lsize']['def'])
ToolTip(self.srvsize, text = srv_options['lsize']['help'], wraplength = self.wraplength)
# Asynchronous messages.
self.chkvalsrvasy = tk.BooleanVar()
self.chkvalsrvasy.set(srv_options['asyncmsg']['def'])
chksrvasy = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Async\nMsg',
font = self.customfonts['opt'], var = self.chkvalsrvasy, relief = 'groove', name = 'asyncmsg')
ToolTip(chksrvasy, text = srv_options['asyncmsg']['help'], wraplength = self.wraplength)
# Listbox radiobuttons server.
self.chksrvfile = ListboxOfRadiobuttons(self.pagewidgets["Srv"]["PageWin"]["PageStart"],
['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'],
self.customfonts['lst'],
changed = [(self.srvfile, srv_options['lfile']['def']),
(srvfilebtnwin, ''),
(self.srvsize, srv_options['lsize']['def']),
(self.srvlevel, srv_options['llevel']['def'])],
width = 10, height = 1, borderwidth = 2, relief = 'ridge')
## Layout widgets (optsrvwin:Srv:PageWin:PageStart)
ver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew')
srvipaddlbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e')
self.srvipadd.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew')
myipadd.grid(row = 2, column = 1, columnspan = 2, padx = 5, pady = 5, sticky = 'ew')
srvportlbl.grid(row = 3, column = 0, padx = 5, pady = 5, sticky = 'e')
self.srvport.grid(row = 3, column = 1, padx = 5, pady = 5, sticky = 'ew')
epidlbl.grid(row = 4, column = 0, padx = 5, pady = 5, sticky = 'e')
self.epid.grid(row = 4, column = 1, padx = 5, pady = 5, sticky = 'ew')
lcidlbl.grid(row = 5, column = 0, padx = 5, pady = 5, sticky = 'e')
self.lcid.grid(row = 5, column = 1, padx = 5, pady = 5, sticky = 'ew')
hwidlbl.grid(row = 6, column = 0, padx = 5, pady = 5, sticky = 'e')
self.hwid.grid(row = 6, column = 1, padx = 5, pady = 5, sticky = 'ew')
countlbl.grid(row = 7, column = 0, padx = 5, pady = 5, sticky = 'e')
self.count.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = 'ew')
activlbl.grid(row = 8, column = 0, padx = 5, pady = 5, sticky = 'e')
self.activ.grid(row = 8, column = 1, padx = 5, pady = 5, sticky = 'ew')
renewlbl.grid(row = 9, column = 0, padx = 5, pady = 5, sticky = 'e')
self.renew.grid(row = 9, column = 1, padx = 5, pady = 5, sticky = 'ew')
srvfilelbl.grid(row = 10, column = 0, padx = 5, pady = 5, sticky = 'e')
self.srvfile.grid(row = 10, column = 1, padx = 5, pady = 5, sticky = 'ew')
srvfilebtnwin.grid(row = 10, column = 2, padx = 5, pady = 5, sticky = 'ew')
self.chksrvfile.grid(row = 11, column = 1, padx = 5, pady = 5, sticky = 'ew')
chksrvasy.grid(row = 11, column = 2, padx = 5, pady = 5, sticky = 'ew')
srvlevellbl.grid(row = 12, column = 0, padx = 5, pady = 5, sticky = 'e')
self.srvlevel.grid(row = 12, column = 1, padx = 5, pady = 5, sticky = 'ew')
srvsizelbl.grid(row = 13, column = 0, padx = 5, pady = 5, sticky = 'e')
self.srvsize.grid(row = 13, column = 1, padx = 5, pady = 5, sticky = 'ew')
## Create widgets (optsrvwin:Srv:PageWin:PageEnd)-------------------------------------------------------------------------------------------
# Timeout connection.
srvtimeout0lbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Timeout connection: ', font = self.customfonts['opt'])
self.srvtimeout0 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time0')
self.srvtimeout0.insert('end', str(srv_options['time0']['def']))
ToolTip(self.srvtimeout0, text = srv_options['time0']['help'], wraplength = self.wraplength)
# Timeout send/recv.
srvtimeout1lbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Timeout send-recv: ', font = self.customfonts['opt'])
self.srvtimeout1 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time1')
self.srvtimeout1.insert('end', str(srv_options['time1']['def']))
ToolTip(self.srvtimeout1, text = srv_options['time1']['help'], wraplength = self.wraplength)
# Sqlite database.
self.chkvalsql = tk.BooleanVar()
self.chkvalsql.set(srv_options['sql']['def'])
self.chkfilesql = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'sql')
self.chkfilesql.insert('end', srv_options['sql']['file'])
self.chkfilesql.xview_moveto(1)
self.chkfilesql.configure(state = 'disabled')
chksql = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Create Sqlite\nDatabase',
font = self.customfonts['opt'], var = self.chkvalsql, relief = 'groove',
command = lambda: self.sql_status())
ToolTip(chksql, text = srv_options['sql']['help'], wraplength = self.wraplength)
## Layout widgets (optsrvwin:Srv:PageWin:PageEnd)
# a label for vertical aligning with PageStart
tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 0,
height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw')
srvtimeout0lbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e')
self.srvtimeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'w')
srvtimeout1lbl.grid(row = 2, column = 0, padx = 5, pady = 5, sticky = 'e')
self.srvtimeout1.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'w')
chksql.grid(row = 3, column = 0, padx = 5, pady = 5, sticky = 'e')
self.chkfilesql.grid(row = 3, column = 1, padx = 5, pady = 5, sticky = 'w')
# Store server-side widgets.
self.storewidgets_srv = self.gui_store(side = "Srv", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton'])
self.storewidgets_srv.append(self.chksrvfile)
## Create widgets and layout (msgsrvwin) ---------------------------------------------------------------------------------------------------
self.textboxsrv = TextDoubleScroll(self.msgsrvwin, background = self.customcolors['black'], wrap = 'none', state = 'disabled',
relief = 'ridge', font = self.customfonts['msg'])
self.textboxsrv.put()
def sql_status(self):
if self.chkvalsql.get():
self.chkfilesql.configure(state = 'normal')
else:
self.chkfilesql.insert('end', srv_options['sql']['file'])
self.chkfilesql.xview_moveto(1)
self.chkfilesql.configure(state = 'disabled')
def always_centered(self, geo, centered, refs):
x = (self.winfo_screenwidth() // 2) - (self.winfo_width() // 2)
y = (self.winfo_screenheight() // 2) - (self.winfo_height() // 2)
w, h, dx, dy = geo.split('+')[0].split('x') + geo.split('+')[1:]
if w == refs[1]:
if centered:
self.geometry('+%d+%d' %(x, y))
centered = False
elif w == refs[0]:
if not centered:
self.geometry('+%d+%d' %(x, y))
centered = True
if dx != str(x) or dy != str(y):
self.geometry('+%d+%d' %(x, 0))
self.after(200, self.always_centered, self.geometry(), centered, refs)
def gui_complete(self):
## Create client widgets (optcltwin, msgcltwin, btncltwin)
self.update_idletasks() # update Gui to get btnsrvwin values --> btncltwin.
minw, minh = self.winfo_width(), self.winfo_height()
self.iconify()
self.gui_clt()
maxw, minh = self.winfo_width(), self.winfo_height()
## Main window custom background.
self.update_idletasks() # update Gui for custom background
self.iconify()
custom_background(self)
## Main window other modifications.
self.eval('tk::PlaceWindow %s center' %self.winfo_pathname(self.winfo_id()))
self.wm_attributes("-topmost", True)
self.protocol("WM_DELETE_WINDOW", lambda: 0)
## Disable maximize button.
self.resizable(False, False)
## Centered window.
self.always_centered(self.geometry(), False, [minw, maxw])
def get_position(self, widget):
x, y = (widget.winfo_x(), widget.winfo_y())
w, h = (widget.winfo_width(), widget.winfo_height())
return x, y, w, h
def gui_clt(self):
self.count_clear, self.keep_clear = (0, '0.0')
self.optcltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
self.msgcltwin = tk.Frame(self.masterwin, background = self.customcolors['black'], relief = 'ridge', width = 300, height = 200)
self.btncltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge')
xb, yb, wb, hb = self.get_position(self.btnsrvwin)
self.btncltwin_X = xb
self.btncltwin_Y = yb + hb + 6
self.btncltwin.place(x = self.btncltwin_X, y = self.btncltwin_Y, bordermode = 'outside', anchor = 'center')
self.optcltwin.grid(row = 0, column = 4, padx = 2, pady = 2, sticky = 'nsew')
self.optcltwin.grid_rowconfigure(0, weight = 1)
self.optcltwin.grid_columnconfigure(1, weight = 1)
## Subpages of "optcltwin".
self.gui_pages_create(parent = self.optcltwin, side = "Clt", create = {"PageStart": None,
"PageEnd": None})
## Continue to grid.
self.msgcltwin.grid(row = 1, column = 4, padx = 1, pady = 1, sticky = 'nsew')
self.msgcltwin.grid_propagate(False)
self.msgcltwin.grid_columnconfigure(0, weight = 1)
self.msgcltwin.grid_rowconfigure(0, weight = 1)
## Create widgets (btncltwin) ----------------------------------------------------------------------------------------------------------------
self.runbtnclt = tk.Button(self.btncltwin, text = 'START\nCLIENT', background = self.customcolors['blue'],
foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'],
state = 'disabled', command = self.clt_on_start, width = 8, height = 2)
## Layout widgets (btncltwin)
self.runbtnclt.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'ew')
## Create widgets (optcltwin:Clt:PageWin:PageStart) ------------------------------------------------------------------------------------------
# Version.
cltver = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'You are running client version: ' + clt_version,
font = self.customfonts['oth'], foreground = self.customcolors['red'])
# Ip Address.
cltipaddlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'IP Address: ', font = self.customfonts['opt'])
self.cltipadd = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'ip')
self.cltipadd.insert('end', clt_options['ip']['def'])
ToolTip(self.cltipadd, text = clt_options['ip']['help'], wraplength = self.wraplength)
# Port.
cltportlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Port: ', font = self.customfonts['opt'])
self.cltport = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'port',
validate = "key", validatecommand = self.validation_int)
self.cltport.insert('end', str(clt_options['port']['def']))
ToolTip(self.cltport, text = clt_options['port']['help'], wraplength = self.wraplength)
# Mode.
cltmodelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Mode: ', font = self.customfonts['opt'])
self.cltmode = ttk.Combobox(self.pagewidgets["Clt"]["PageWin"]["PageStart"], values = tuple(clt_options['mode']['choi']),
width = 17, height = 10, font = self.customfonts['lst'], state = "readonly", name = 'mode')
self.cltmode.set(clt_options['mode']['def'])
ToolTip(self.cltmode, text = clt_options['mode']['help'], wraplength = self.wraplength)
# CMID.
cltcmidlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'CMID: ', font = self.customfonts['opt'])
self.cltcmid = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'cmid')
self.cltcmid.insert('end', str(clt_options['cmid']['def']))
ToolTip(self.cltcmid, text = clt_options['cmid']['help'], wraplength = self.wraplength)
# Machine Name.
cltnamelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Machine Name: ', font = self.customfonts['opt'])
self.cltname = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'name')
self.cltname.insert('end', str(clt_options['name']['def']))
ToolTip(self.cltname, text = clt_options['name']['help'], wraplength = self.wraplength)
# Logfile.
cltfilelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logfile Path / Name: ', font = self.customfonts['opt'])
self.cltfile = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lfile')
self.cltfile.insert('end', clt_options['lfile']['def'])
self.cltfile.xview_moveto(1)
ToolTip(self.cltfile, text = clt_options['lfile']['help'], wraplength = self.wraplength)
cltfilebtnwin = tk.Button(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Browse', font = self.customfonts['opt'],
command = lambda: self.on_browse(self.cltfile, clt_options))
# Loglevel.
cltlevellbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.customfonts['opt'])
self.cltlevel = ttk.Combobox(self.pagewidgets["Clt"]["PageWin"]["PageStart"], values = tuple(clt_options['llevel']['choi']),
width = 10, height = 10, font = self.customfonts['lst'], state = "readonly", name = 'llevel')
self.cltlevel.set(clt_options['llevel']['def'])
ToolTip(self.cltlevel, text = clt_options['llevel']['help'], wraplength = self.wraplength)
# Logsize.
cltsizelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.customfonts['opt'])
self.cltsize = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lsize',
validate = "key", validatecommand = self.validation_float)
self.cltsize.insert('end', clt_options['lsize']['def'])
ToolTip(self.cltsize, text = clt_options['lsize']['help'], wraplength = self.wraplength)
# Asynchronous messages.
self.chkvalcltasy = tk.BooleanVar()
self.chkvalcltasy.set(clt_options['asyncmsg']['def'])
chkcltasy = tk.Checkbutton(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Async\nMsg',
font = self.customfonts['opt'], var = self.chkvalcltasy, relief = 'groove', name = 'asyncmsg')
ToolTip(chkcltasy, text = clt_options['asyncmsg']['help'], wraplength = self.wraplength)
# Listbox radiobuttons client.
self.chkcltfile = ListboxOfRadiobuttons(self.pagewidgets["Clt"]["PageWin"]["PageStart"],
['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'],
self.customfonts['lst'],
changed = [(self.cltfile, clt_options['lfile']['def']),
(cltfilebtnwin, ''),
(self.cltsize, clt_options['lsize']['def']),
(self.cltlevel, clt_options['llevel']['def'])],
width = 10, height = 1, borderwidth = 2, relief = 'ridge')
## Layout widgets (optcltwin:Clt:PageWin:PageStart)
cltver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew')
cltipaddlbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltipadd.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew')
cltportlbl.grid(row = 2, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltport.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'ew')
cltmodelbl.grid(row = 3, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltmode.grid(row = 3, column = 1, padx = 5, pady = 5, sticky = 'ew')
cltcmidlbl.grid(row = 4, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltcmid.grid(row = 4, column = 1, padx = 5, pady = 5, sticky = 'ew')
cltnamelbl.grid(row = 5, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltname.grid(row = 5, column = 1, padx = 5, pady = 5, sticky = 'ew')
cltfilelbl.grid(row = 6, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltfile.grid(row = 6, column = 1, padx = 5, pady = 5, sticky = 'ew')
cltfilebtnwin.grid(row = 6, column = 2, padx = 5, pady = 5, sticky = 'ew')
self.chkcltfile.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = 'ew')
chkcltasy.grid(row = 7, column = 2, padx = 5, pady = 5, sticky = 'ew')
cltlevellbl.grid(row = 8, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltlevel.grid(row = 8, column = 1, padx = 5, pady = 5, sticky = 'ew')
cltsizelbl.grid(row = 9, column = 0, padx = 5, pady = 5, sticky = 'e')
self.cltsize.grid(row = 9, column = 1, padx = 5, pady = 5, sticky = 'ew')
# ugly fix when client-side mode is activated.
templbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"],
bg = self.customcolors['lavender']).grid(row = 10, column = 0,
padx = 35, pady = 54, sticky = 'e')
## Create widgets (optcltwin:Clt:PageWin:PageEnd) -------------------------------------------------------------------------------------------
# Timeout connection.
clttimeout0lbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], text = 'Timeout connection: ', font = self.customfonts['opt'])
self.clttimeout0 = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time0')
self.clttimeout0.insert('end', str(clt_options['time0']['def']))
ToolTip(self.clttimeout0, text = clt_options['time0']['help'], wraplength = self.wraplength)
# Timeout send/recv.
clttimeout1lbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], text = 'Timeout send-recv: ', font = self.customfonts['opt'])
self.clttimeout1 = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time1')
self.clttimeout1.insert('end', str(clt_options['time1']['def']))
ToolTip(self.clttimeout1, text = clt_options['time1']['help'], wraplength = self.wraplength)
## Layout widgets (optcltwin:Clt:PageWin:PageEnd)
# a label for vertical aligning with PageStart
tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 0,
height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw')
clttimeout0lbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e')
self.clttimeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'w')
clttimeout1lbl.grid(row = 2, column = 0, padx = 5, pady = 5, sticky = 'e')
self.clttimeout1.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'w')
## Store client-side widgets.
self.storewidgets_clt = self.gui_store(side = "Clt", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton'])
self.storewidgets_clt.append(self.chkcltfile)
## Create widgets and layout (msgcltwin) -----------------------------------------------------------------------------------------------------
self.textboxclt = TextDoubleScroll(self.msgcltwin, background = self.customcolors['black'], wrap = 'none', state = 'disabled',
relief = 'ridge', font = self.customfonts['msg'])
self.textboxclt.put()
def prep_option(self, value):
try:
# is an INT
return int(value)
except (TypeError, ValueError):
try:
# is a FLOAT
return float(value)
except (TypeError, ValueError):
# is a STRING.
return value
def prep_logfile(self, filepath, status):
# FILE (pretty on, log view off, logfile yes)
# FILEOFF (pretty on, log view off, no logfile)
# STDOUT (pretty off, log view on, no logfile)
# STDOUTOFF (pretty off, log view off, logfile yes)
# FILESTDOUT (pretty off, log view on, logfile yes)
if status == 'FILE':
return filepath
elif status in ['FILESTDOUT', 'STDOUTOFF']:
return [status, filepath]
elif status in ['STDOUT', 'FILEOFF']:
return status
def validate_int(self, value):
return value == "" or value.isdigit()
def validate_float(self, value):
if value == "":
return True
try:
float(value)
return True
except ValueError:
return False
def clt_on_show(self, force_remove = False, force_view = False):
if self.optcltwin.winfo_ismapped() or force_remove:
self.shbtnclt.configure(text = 'SHOW\nCLIENT', relief = 'raised')
self.optcltwin.grid_remove()
self.msgcltwin.grid_remove()
self.btncltwin.place_forget()
elif not self.optcltwin.winfo_ismapped() or force_view:
self.shbtnclt.configure(text = 'HIDE\nCLIENT', relief = 'sunken')
self.optcltwin.grid()
self.msgcltwin.grid()
self.btncltwin.place(x = self.btncltwin_X, y = self.btncltwin_Y, bordermode = 'inside', anchor = 'nw')
def srv_on_start(self):
if self.runbtnsrv['text'] == 'START\nSERVER':
self.on_clear([txsrv, txclt])
self.srv_actions_start()
# wait for switch.
while not serverthread.is_running_server:
pass
self.srv_toggle_all(on_start = True)
# run thread for interrupting server when an error happens.
self.srv_eject_thread = threading.Thread(target = self.srv_eject, name = "Thread-SrvEjt")
self.srv_eject_thread.setDaemon(True)
self.srv_eject_thread.start()
elif self.runbtnsrv['text'] == 'STOP\nSERVER':
serverthread.terminate_eject()
def srv_eject(self):
while not serverthread.eject:
sleep(0.1)
self.srv_actions_stop()
def srv_actions_start(self):
srv_config[srv_options['ip']['des']] = self.srvipadd.get()
srv_config[srv_options['port']['des']] = self.prep_option(self.srvport.get())
srv_config[srv_options['epid']['des']] = self.epid.get()
srv_config[srv_options['lcid']['des']] = self.prep_option(self.lcid.get())
srv_config[srv_options['hwid']['des']] = self.hwid.get()
srv_config[srv_options['count']['des']] = self.prep_option(self.count.get())
srv_config[srv_options['activation']['des']] = self.prep_option(self.activ.get())
srv_config[srv_options['renewal']['des']] = self.prep_option(self.renew.get())
srv_config[srv_options['lfile']['des']] = self.prep_logfile(self.srvfile.get(), self.chksrvfile.state())
srv_config[srv_options['asyncmsg']['des']] = self.chkvalsrvasy.get()
srv_config[srv_options['llevel']['des']] = self.srvlevel.get()
srv_config[srv_options['lsize']['des']] = self.prep_option(self.srvsize.get())
srv_config[srv_options['time0']['des']] = self.prep_option(self.srvtimeout0.get())
srv_config[srv_options['time1']['des']] = self.prep_option(self.srvtimeout1.get())
srv_config[srv_options['sql']['des']] = (self.chkfilesql.get() if self.chkvalsql.get() else self.chkvalsql.get())
## Redirect stdout.
gui_redirector('stdout', redirect_to = TextRedirect.Log,
redirect_conditio = (srv_config[srv_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT']))
serverqueue.put('start')
def srv_actions_stop(self):
if serverthread.is_running_server:
if serverthread.server is not None:
server_terminate(serverthread, exit_server = True)
# wait for switch.
while serverthread.is_running_server:
pass
else:
serverthread.is_running_server = False
self.srv_toggle_all(on_start = False)
self.count_clear, self.keep_clear = (0, '0.0')
def srv_toggle_all(self, on_start = True):
self.srv_toggle_state()
if on_start:
self.runbtnsrv.configure(text = 'STOP\nSERVER', background = self.customcolors['red'],
foreground = self.customcolors['white'], relief = 'sunken')
for widget in self.storewidgets_srv:
widget.configure(state = 'disabled')
self.runbtnclt.configure(state = 'normal')
else:
self.runbtnsrv.configure(text = 'START\nSERVER', background = self.customcolors['green'],
foreground = self.customcolors['white'], relief = 'raised')
for widget in self.storewidgets_srv:
widget.configure(state = 'normal')
if isinstance(widget, ListboxOfRadiobuttons):
widget.change()
self.runbtnclt.configure(state = 'disabled')
def srv_toggle_state(self):
if serverthread.is_running_server:
txt, color = ('Server\nState:\nServing', self.customcolors['green'])
else:
txt, color = ('Server\nState:\nStopped', self.customcolors['red'])
self.statesrv.configure(text = txt, foreground = color)
def clt_on_start(self):
if self.onlyclt:
self.on_clear([txclt])
else:
rng, add_newline = self.on_clear_setup()
self.on_clear([txsrv, txclt], clear_range = [rng, None], newline_list = [add_newline, False])
self.runbtnclt.configure(relief = 'sunken')
self.clt_actions_start()
# run thread for disabling interrupt server and client, when client running.
self.clt_eject_thread = threading.Thread(target = self.clt_eject, name = "Thread-CltEjt")
self.clt_eject_thread.setDaemon(True)
self.clt_eject_thread.start()
for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt, self.defaubtnsrv]:
widget.configure(state = 'disabled')
self.runbtnclt.configure(relief = 'raised')
def clt_actions_start(self):
clt_config[clt_options['ip']['des']] = self.cltipadd.get()
clt_config[clt_options['port']['des']] = self.prep_option(self.cltport.get())
clt_config[clt_options['mode']['des']] = self.cltmode.get()
clt_config[clt_options['cmid']['des']] = self.cltcmid.get()
clt_config[clt_options['name']['des']] = self.cltname.get()
clt_config[clt_options['lfile']['des']] = self.prep_logfile(self.cltfile.get(), self.chkcltfile.state())
clt_config[clt_options['asyncmsg']['des']] = self.chkvalcltasy.get()
clt_config[clt_options['llevel']['des']] = self.cltlevel.get()
clt_config[clt_options['lsize']['des']] = self.prep_option(self.cltsize.get())
clt_config[clt_options['time0']['des']] = self.prep_option(self.clttimeout0.get())
clt_config[clt_options['time1']['des']] = self.prep_option(self.clttimeout1.get())
## Redirect stdout.
gui_redirector('stdout', redirect_to = TextRedirect.Log,
redirect_conditio = (clt_config[clt_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT']))
# run client (in a thread).
self.clientthread = client_thread(name = "Thread-Clt")
self.clientthread.setDaemon(True)
self.clientthread.with_gui = True
self.clientthread.start()
def clt_eject(self):
while self.clientthread.is_alive():
sleep(0.1)
widgets = self.storewidgets_clt + [self.runbtnclt] + [self.defaubtnsrv]
if not self.onlyclt:
widgets += [self.runbtnsrv]
for widget in widgets:
if isinstance(widget, ttk.Combobox):
widget.configure(state = 'readonly')
else:
widget.configure(state = 'normal')
if isinstance(widget, ListboxOfRadiobuttons):
widget.change()
def on_browse(self, entrywidget, options):
path = filedialog.askdirectory()
if os.path.isdir(path):
entrywidget.delete('0', 'end')
entrywidget.insert('end', path + os.sep + os.path.basename(options['lfile']['def']))
def on_exit(self):
if serverthread.is_running_server:
if serverthread.server is not None:
server_terminate(serverthread, exit_server = True)
else:
serverthread.is_running_server = False
server_terminate(serverthread, exit_thread = True)
self.destroy()
def on_clear_setup(self):
if any(opt in ['STDOUT', 'FILESTDOUT'] for opt in srv_config[srv_options['lfile']['des']]):
add_newline = True
if self.count_clear == 0:
self.keep_clear = txsrv.index('end-1c')
else:
add_newline = False
if self.count_clear == 0:
self.keep_clear = txsrv.index('end')
rng = [self.keep_clear, 'end']
self.count_clear += 1
return rng, add_newline
def on_clear(self, widget_list, clear_range = None, newline_list = []):
if newline_list == []:
newline_list = len(widget_list) * [False]
for num, couple in enumerate(zip(widget_list, newline_list)):
widget, add_n = couple
try:
ini, fin = clear_range[num]
except TypeError:
ini, fin = '1.0', 'end'
widget.configure(state = 'normal')
widget.delete(ini, fin)
if add_n:
widget.insert('end', '\n')
widget.configure(state = 'disabled')
def on_defaults(self):
def put_defaults(widgets, chkasy, listofradio, options):
for widget in widgets:
wclass, wname = widget.winfo_class(), widget.winfo_name()
if wname == '!checkbutton':
continue
opt = options[wname]['def']
if wclass == 'Entry':
widget.delete(0, 'end')
if wname == 'sql':
self.chkvalsql.set(opt)
self.sql_status()
else:
widget.insert('end', (opt if isinstance(opt, str) else str(opt)))
elif wclass == 'Checkbutton':
if wname == 'asyncmsg':
chkasy.set(opt)
elif wclass == 'TCombobox':
widget.set(str(opt))
# ListboxOfRadiobuttons default.
listofradio.radiovar.set('FILE')
listofradio.textbox.yview_moveto(0)
listofradio.change()
if self.runbtnsrv['text'] == 'START\nSERVER':
apply_default = zip(["Srv", "Clt"],
[self.chkvalsrvasy, self.chkvalcltasy],
[self.chksrvfile, self.chkcltfile],
[srv_options, clt_options])
elif self.runbtnsrv['text'] == 'STOP\nSERVER':
apply_default = zip(*[("Clt",),
(self.chkvalcltasy,),
(self.chkcltfile,),
(clt_options,)])
for side, chkasy, listofradio, options in apply_default:
widgets = self.gui_store(side = side, typewidgets = ['Entry', 'TCombobox', 'Checkbutton'])
put_defaults(widgets, chkasy, listofradio, options)

View file

@ -1,517 +0,0 @@
#!/usr/bin/env python3
import os
import re
import sys
from collections import Counter
from time import sleep
import threading
import tkinter as tk
from tkinter import ttk
import tkinter.font as tkFont
from pykms_Format import MsgMap, unshell_message, unformat_message
#------------------------------------------------------------------------------------------------------------------------------------------------------------
# https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter
class ToolTip(object):
""" Create a tooltip for a given widget """
def __init__(self, widget, bg = '#FFFFEA', pad = (5, 3, 5, 3), text = 'widget info', waittime = 400, wraplength = 250):
self.waittime = waittime # ms
self.wraplength = wraplength # pixels
self.widget = widget
self.text = text
self.widget.bind("<Enter>", self.onEnter)
self.widget.bind("<Leave>", self.onLeave)
self.widget.bind("<ButtonPress>", self.onLeave)
self.bg = bg
self.pad = pad
self.id = None
self.tw = None
def onEnter(self, event = None):
self.schedule()
def onLeave(self, event = None):
self.unschedule()
self.hide()
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.waittime, self.show)
def unschedule(self):
id_ = self.id
self.id = None
if id_:
self.widget.after_cancel(id_)
def show(self):
def tip_pos_calculator(widget, label, tip_delta = (10, 5), pad = (5, 3, 5, 3)):
w = widget
s_width, s_height = w.winfo_screenwidth(), w.winfo_screenheight()
width, height = (pad[0] + label.winfo_reqwidth() + pad[2],
pad[1] + label.winfo_reqheight() + pad[3])
mouse_x, mouse_y = w.winfo_pointerxy()
x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1]
x2, y2 = x1 + width, y1 + height
x_delta = x2 - s_width
if x_delta < 0:
x_delta = 0
y_delta = y2 - s_height
if y_delta < 0:
y_delta = 0
offscreen = (x_delta, y_delta) != (0, 0)
if offscreen:
if x_delta:
x1 = mouse_x - tip_delta[0] - width
if y_delta:
y1 = mouse_y - tip_delta[1] - height
offscreen_again = y1 < 0 # out on the top
if offscreen_again:
# No further checks will be done.
# TIP:
# A further mod might automagically augment the
# wraplength when the tooltip is too high to be
# kept inside the screen.
y1 = 0
return x1, y1
bg = self.bg
pad = self.pad
widget = self.widget
# creates a toplevel window
self.tw = tk.Toplevel(widget)
# leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
win = tk.Frame(self.tw, background = bg, borderwidth = 0)
label = ttk.Label(win, text = self.text, justify = tk.LEFT, background = bg, relief = tk.SOLID, borderwidth = 0,
wraplength = self.wraplength)
label.grid(padx = (pad[0], pad[2]), pady = (pad[1], pad[3]), sticky=tk.NSEW)
win.grid()
x, y = tip_pos_calculator(widget, label)
self.tw.wm_geometry("+%d+%d" % (x, y))
def hide(self):
tw = self.tw
if tw:
tw.destroy()
self.tw = None
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
class TextRedirect(object):
class Pretty(object):
grpmsg = unformat_message([MsgMap[1], MsgMap[7], MsgMap[12], MsgMap[20]])
arrows = [ item[0] for item in grpmsg ]
clt_msg_nonewline = [ item[1] for item in grpmsg ]
arrows = list(set(arrows))
lenarrow = len(arrows[0])
srv_msg_nonewline = [ item[0] for item in unformat_message([MsgMap[2], MsgMap[5], MsgMap[13], MsgMap[18]]) ]
msg_align = [ msg[0].replace('\t', '').replace('\n', '') for msg in unformat_message([MsgMap[-2], MsgMap[-4]]) ]
def __init__(self, srv_text_space, clt_text_space, customcolors):
self.srv_text_space = srv_text_space
self.clt_text_space = clt_text_space
self.customcolors = customcolors
def textbox_write(self, tag, message, color, extras):
widget = self.textbox_choose(message)
self.w_maxpix, self.h_maxpix = widget.winfo_width(), widget.winfo_height()
self.xfont = tkFont.Font(font = widget['font'])
widget.configure(state = 'normal')
widget.insert('end', self.textbox_format(message), tag)
self.textbox_color(tag, widget, color, self.customcolors['black'], extras)
widget.after(100, widget.see('end'))
widget.configure(state = 'disabled')
def textbox_choose(self, message):
if any(item.startswith('logsrv') for item in [message, self.str_to_print]):
self.srv_text_space.focus_set()
self.where = "srv"
return self.srv_text_space
elif any(item.startswith('logclt') for item in [message, self.str_to_print]):
self.clt_text_space.focus_set()
self.where = "clt"
return self.clt_text_space
def textbox_color(self, tag, widget, forecolor = 'white', backcolor = 'black', extras = []):
for extra in extras:
if extra == 'bold':
self.xfont.configure(weight = "bold")
elif extra == 'italic':
self.xfont.configure(slant = "italic")
elif extra == 'underlined':
self.xfont.text_font.configure(underline = True)
elif extra == 'strike':
self.xfont.configure(overstrike = True)
elif extra == 'reverse':
forecolor, backcolor = backcolor, forecolor
widget.tag_configure(tag, foreground = forecolor, background = backcolor, font = self.xfont)
widget.tag_add(tag, "insert linestart", "insert lineend")
def textbox_newline(self, message):
if not message.endswith('\n'):
return message + '\n'
else:
return message
def textbox_format(self, message):
# vertical align.
self.w_maxpix = self.w_maxpix - 5 # pixel reduction for distance from border.
w_fontpix, h_fontpix = (self.xfont.measure('0'), self.xfont.metrics('linespace'))
msg_unformat = message.replace('\t', '').replace('\n', '')
lenfixed_chars = int((self.w_maxpix / w_fontpix) - len(msg_unformat))
if message in self.srv_msg_nonewline + self.clt_msg_nonewline:
lung = lenfixed_chars - self.lenarrow
if message in self.clt_msg_nonewline:
message = self.textbox_newline(message)
else:
lung = lenfixed_chars
if (self.where == "srv") or (self.where == "clt" and message not in self.arrows):
message = self.textbox_newline(message)
# horizontal align.
if msg_unformat in self.msg_align:
msg_strip = message.lstrip('\n')
message = '\n' * (len(message) - len(msg_strip) + TextRedirect.Pretty.newlinecut[0]) + msg_strip
TextRedirect.Pretty.newlinecut.pop(0)
count = Counter(message)
countab = (count['\t'] if count['\t'] != 0 else 1)
message = message.replace('\t' * countab, ' ' * lung)
return message
def textbox_do(self):
msgs, TextRedirect.Pretty.tag_num = unshell_message(self.str_to_print, TextRedirect.Pretty.tag_num)
for tag in msgs:
self.textbox_write(tag, msgs[tag]['text'], self.customcolors[msgs[tag]['color']], msgs[tag]['extra'])
def flush(self):
pass
def write(self, string):
if string != '\n':
self.str_to_print = string
self.textbox_do()
class Stderr(Pretty):
def __init__(self, srv_text_space, clt_text_space, customcolors, side):
self.srv_text_space = srv_text_space
self.clt_text_space = clt_text_space
self.customcolors = customcolors
self.side = side
self.tag_err = 'STDERR'
self.xfont = tkFont.Font(font = self.srv_text_space['font'])
def textbox_choose(self, message):
if self.side == "srv":
return self.srv_text_space
elif self.side == "clt":
return self.clt_text_space
def write(self, string):
widget = self.textbox_choose(string)
self.textbox_color(self.tag_err, widget, self.customcolors['red'], self.customcolors['black'])
self.srv_text_space.configure(state = 'normal')
self.srv_text_space.insert('end', string, self.tag_err)
self.srv_text_space.see('end')
self.srv_text_space.configure(state = 'disabled')
class Log(Pretty):
def textbox_format(self, message):
if message.startswith('logsrv'):
message = message.replace('logsrv ', '')
if message.startswith('logclt'):
message = message.replace('logclt ', '')
return message + '\n'
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
class TextDoubleScroll(tk.Frame):
def __init__(self, master, **kwargs):
""" Initialize.
- horizontal scrollbar
- vertical scrollbar
- text widget
"""
tk.Frame.__init__(self, master)
self.master = master
self.textbox = tk.Text(self.master, **kwargs)
self.sizegrip = ttk.Sizegrip(self.master)
self.hs = ttk.Scrollbar(self.master, orient = "horizontal", command = self.on_scrollbar_x)
self.vs = ttk.Scrollbar(self.master, orient = "vertical", command = self.on_scrollbar_y)
self.textbox.configure(yscrollcommand = self.on_textscroll, xscrollcommand = self.hs.set)
def on_scrollbar_x(self, *args):
""" Horizontally scrolls text widget. """
self.textbox.xview(*args)
def on_scrollbar_y(self, *args):
""" Vertically scrolls text widget. """
self.textbox.yview(*args)
def on_textscroll(self, *args):
""" Moves the scrollbar and scrolls text widget when the mousewheel is moved on a text widget. """
self.vs.set(*args)
self.on_scrollbar_y('moveto', args[0])
def put(self, **kwargs):
""" Grid the scrollbars and textbox correctly. """
self.textbox.grid(row = 0, column = 0, padx = 3, pady = 3, sticky = "nsew")
self.vs.grid(row = 0, column = 1, sticky = "ns")
self.hs.grid(row = 1, column = 0, sticky = "we")
self.sizegrip.grid(row = 1, column = 1, sticky = "news")
def get(self):
""" Return the "frame" useful to place inner controls. """
return self.textbox
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
def custom_background(window):
# first level canvas.
allwidgets = window.grid_slaves(0,0)[0].grid_slaves() + window.grid_slaves(0,0)[0].place_slaves()
widgets_alphalow = [ widget for widget in allwidgets if widget.winfo_class() == 'Canvas']
widgets_alphahigh = []
# sub-level canvas.
for side in ["Srv", "Clt"]:
widgets_alphahigh.append(window.pagewidgets[side]["BtnWin"])
for position in ["Left", "Right"]:
widgets_alphahigh.append(window.pagewidgets[side]["AniWin"][position])
for pagename in window.pagewidgets[side]["PageWin"].keys():
widgets_alphalow.append(window.pagewidgets[side]["PageWin"][pagename])
try:
from PIL import Image, ImageTk
# Open Image.
img = Image.open(os.path.dirname(os.path.abspath( __file__ )) + "/graphics/pykms_Keys.gif")
img = img.convert('RGBA')
# Resize image.
img.resize((window.winfo_width(), window.winfo_height()), Image.ANTIALIAS)
# Put semi-transparent background chunks.
window.backcrops_alphalow, window.backcrops_alphahigh = ([] for _ in range(2))
def cutter(master, image, widgets, crops, alpha):
for widget in widgets:
x, y, w, h = master.get_position(widget)
cropped = image.crop((x, y, x + w, y + h))
cropped.putalpha(alpha)
crops.append(ImageTk.PhotoImage(cropped))
# Not in same loop to prevent reference garbage.
for crop, widget in zip(crops, widgets):
widget.create_image(1, 1, image = crop, anchor = 'nw')
cutter(window, img, widgets_alphalow, window.backcrops_alphalow, 36)
cutter(window, img, widgets_alphahigh, window.backcrops_alphahigh, 96)
# Put semi-transparent background overall.
img.putalpha(128)
window.backimg = ImageTk.PhotoImage(img)
window.masterwin.create_image(1, 1, image = window.backimg, anchor = 'nw')
except ImportError:
for widget in widgets_alphalow + widgets_alphahigh:
widget.configure(background = window.customcolors['lavender'])
# Hide client.
window.clt_on_show(force_remove = True)
# Show Gui.
window.deiconify()
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
class Animation(object):
def __init__(self, gifpath, master, widget, loop = False):
from PIL import Image, ImageTk, ImageSequence
self.master = master
self.widget = widget
self.loop = loop
self.cancelid = None
self.flagstop = False
self.index = 0
self.frames = []
img = Image.open(gifpath)
size = img.size
for frame in ImageSequence.Iterator(img):
static_img = ImageTk.PhotoImage(frame.convert('RGBA'))
try:
static_img.delay = int(frame.info['duration'])
except KeyError:
static_img.delay = 100
self.frames.append(static_img)
self.widget.configure(width = size[0], height = size[1])
self.initialize()
def initialize(self):
self.widget.configure(image = self.frames[0])
self.widget.image = self.frames[0]
def deanimate(self):
while not self.flagstop:
pass
self.flagstop = False
self.index = 0
self.widget.configure(relief = "raised")
def animate(self):
frame = self.frames[self.index]
self.widget.configure(image = frame, relief = "sunken")
self.index += 1
self.cancelid = self.master.after(frame.delay, self.animate)
if self.index == len(self.frames):
if self.loop:
self.index = 0
else:
self.stop()
def start(self, event = None):
if str(self.widget['state']) != 'disabled':
if self.cancelid is None:
if not self.loop:
self.btnani_thread = threading.Thread(target = self.deanimate, name = "Thread-BtnAni")
self.btnani_thread.setDaemon(True)
self.btnani_thread.start()
self.cancelid = self.master.after(self.frames[0].delay, self.animate)
def stop(self, event = None):
if self.cancelid:
self.master.after_cancel(self.cancelid)
self.cancelid = None
self.flagstop = True
self.initialize()
def custom_pages(window, side):
buttons = window.pagewidgets[side]["BtnAni"]
labels = window.pagewidgets[side]["LblAni"]
for position in buttons.keys():
buttons[position].config(anchor = "center",
font = window.customfonts['btn'],
background = window.customcolors['white'],
activebackground = window.customcolors['white'],
borderwidth = 2)
try:
anibtn = Animation(os.path.dirname(os.path.abspath( __file__ )) + "/graphics/pykms_Keyhole_%s.gif" %position,
window, buttons[position], loop = False)
anilbl = Animation(os.path.dirname(os.path.abspath( __file__ )) + "/graphics/pykms_Arrow_%s.gif" %position,
window, labels[position], loop = True)
def animationwait(master, button, btn_animation, lbl_animation):
while btn_animation.cancelid:
pass
sleep(1)
x, y = master.winfo_pointerxy()
if master.winfo_containing(x, y) == button:
lbl_animation.start()
def animationcombo(master, button, btn_animation, lbl_animation):
wait_thread = threading.Thread(target = animationwait,
args = (master, button, btn_animation, lbl_animation),
name = "Thread-WaitAni")
wait_thread.setDaemon(True)
wait_thread.start()
lbl_animation.stop()
btn_animation.start()
buttons[position].bind("<ButtonPress>", lambda event, anim1 = anibtn, anim2 = anilbl,
bt = buttons[position], win = window:
animationcombo(win, bt, anim1, anim2))
buttons[position].bind("<Enter>", anilbl.start)
buttons[position].bind("<Leave>", anilbl.stop)
except ImportError:
buttons[position].config(activebackground = window.customcolors['blue'],
foreground = window.customcolors['blue'])
labels[position].config(background = window.customcolors['lavender'])
if position == "Left":
buttons[position].config(text = '<<')
elif position == "Right":
buttons[position].config(text = '>>')
##-----------------------------------------------------------------------------------------------------------------------------------------------------------
class ListboxOfRadiobuttons(tk.Frame):
def __init__(self, master, radios, font, changed, **kwargs):
tk.Frame.__init__(self, master)
self.master = master
self.radios = radios
self.font = font
self.changed = changed
self.scrollv = tk.Scrollbar(self, orient = "vertical")
self.textbox = tk.Text(self, yscrollcommand = self.scrollv.set, **kwargs)
self.scrollv.config(command = self.textbox.yview)
# layout.
self.scrollv.pack(side = "right", fill = "y")
self.textbox.pack(side = "left", fill = "both", expand = True)
# create radiobuttons.
self.radiovar = tk.StringVar()
self.radiovar.set('FILE')
self.create()
def create(self):
self.rdbtns = []
for n, nameradio in enumerate(self.radios):
rdbtn = tk.Radiobutton(self, text = nameradio, value = nameradio, variable = self.radiovar,
font = self.font, indicatoron = 0, width = 15,
borderwidth = 3, selectcolor = 'yellow', command = self.change)
self.textbox.window_create("end", window = rdbtn)
# to force one checkbox per line
if n != len(self.radios) - 1:
self.textbox.insert("end", "\n")
self.rdbtns.append(rdbtn)
self.textbox.configure(state = "disabled")
def change(self):
st = self.state()
for widget, default in self.changed:
wclass = widget.winfo_class()
if st in ['STDOUT', 'FILEOFF']:
if wclass == 'Entry':
widget.delete(0, 'end')
widget.configure(state = "disabled")
elif wclass == 'TCombobox':
if st == 'STDOUT':
widget.set(default)
widget.configure(state = "readonly")
elif st == 'FILEOFF':
widget.set('')
widget.configure(state = "disabled")
elif st in ['FILE', 'FILESTDOUT', 'STDOUTOFF']:
if wclass == 'Entry':
widget.configure(state = "normal")
widget.delete(0, 'end')
widget.insert('end', default)
widget.xview_moveto(1)
elif wclass == 'TCombobox':
widget.configure(state = "readonly")
widget.set(default)
elif wclass == 'Button':
widget.configure(state = "normal")
def configure(self, state):
for rb in self.rdbtns:
rb.configure(state = state)
def state(self):
return self.radiovar.get()