diff --git a/py-kms/pykms_Format.py b/py-kms/pykms_Format.py index dd968c0..d0d28c5 100644 --- a/py-kms/pykms_Format.py +++ b/py-kms/pykms_Format.py @@ -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) diff --git a/py-kms/pykms_GuiBase.py b/py-kms/pykms_GuiBase.py deleted file mode 100644 index 30f5dec..0000000 --- a/py-kms/pykms_GuiBase.py +++ /dev/null @@ -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 " -__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) diff --git a/py-kms/pykms_GuiMisc.py b/py-kms/pykms_GuiMisc.py deleted file mode 100644 index 97b1cc1..0000000 --- a/py-kms/pykms_GuiMisc.py +++ /dev/null @@ -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("", self.onEnter) - self.widget.bind("", self.onLeave) - self.widget.bind("", 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("", lambda event, anim1 = anibtn, anim2 = anilbl, - bt = buttons[position], win = window: - animationcombo(win, bt, anim1, anim2)) - buttons[position].bind("", anilbl.start) - buttons[position].bind("", 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()