diff --git a/py-kms/graphics/pykms_Arrow_Left.gif b/py-kms/graphics/pykms_Arrow_Left.gif new file mode 100644 index 0000000..731405d Binary files /dev/null and b/py-kms/graphics/pykms_Arrow_Left.gif differ diff --git a/py-kms/graphics/pykms_Arrow_Right.gif b/py-kms/graphics/pykms_Arrow_Right.gif new file mode 100644 index 0000000..422583f Binary files /dev/null and b/py-kms/graphics/pykms_Arrow_Right.gif differ diff --git a/py-kms/graphics/pykms_Keyhole_Left.gif b/py-kms/graphics/pykms_Keyhole_Left.gif new file mode 100644 index 0000000..5d2933c Binary files /dev/null and b/py-kms/graphics/pykms_Keyhole_Left.gif differ diff --git a/py-kms/graphics/pykms_Keyhole_Right.gif b/py-kms/graphics/pykms_Keyhole_Right.gif new file mode 100644 index 0000000..d7499bb Binary files /dev/null and b/py-kms/graphics/pykms_Keyhole_Right.gif differ diff --git a/py-kms/pykms_Keys.gif b/py-kms/graphics/pykms_Keys.gif similarity index 100% rename from py-kms/pykms_Keys.gif rename to py-kms/graphics/pykms_Keys.gif diff --git a/py-kms/pykms_Client.py b/py-kms/pykms_Client.py index f208680..0ee89b8 100644 --- a/py-kms/pykms_Client.py +++ b/py-kms/pykms_Client.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- import re import binascii @@ -21,9 +22,9 @@ from pykms_RequestV5 import kmsRequestV5 from pykms_RequestV6 import kmsRequestV6 from pykms_RpcBase import rpcBase from pykms_DB2Dict import kmsDB2Dict -from pykms_Misc import logger_create, check_logfile -from pykms_Misc import KmsParser, KmsException, KmsHelper -from pykms_Format import justify, byterize, enco, deco, ShellMessage, pretty_printer +from pykms_Misc import check_setup +from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp +from pykms_Format import justify, byterize, enco, deco, pretty_printer clt_version = "py-kms_2020-02-02" __license__ = "The Unlicense" @@ -91,20 +92,14 @@ def client_options(): try: if "-h" in sys.argv[1:]: - KmsHelper().printer(parsers = [client_parser]) + KmsParserHelp().printer(parsers = [client_parser]) clt_config.update(vars(client_parser.parse_args())) - except KmsException as e: + except KmsParserException as e: pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True) def client_check(): - # Check logfile. - clt_config['logfile'] = check_logfile(clt_config['logfile'], clt_options['lfile']['def'], where = "clt") - - # Setup hidden or not messages. - ShellMessage.view = ( False if any(i in ['STDOUT', 'FILESTDOUT'] for i in clt_config['logfile']) else True ) - - # Create log. - logger_create(loggerclt, clt_config, mode = 'a') + # Setup and some checks. + check_setup(clt_config, clt_options, loggerclt, where = "clt") # Check cmid. if clt_config['cmid'] is not None: @@ -115,6 +110,12 @@ def client_check(): put_text = "{reverse}{red}{bold}Bad CMID. Exiting...{end}") # Check machineName. if clt_config['machineName'] is not None: + try: + clt_config['machineName'].encode('utf-16le') + except UnicodeEncodeError: + pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", + put_text = "{reverse}{red}{bold}Bad machineName. Exiting...{end}") + if len(clt_config['machineName']) < 2 or len(clt_config['machineName']) > 63: pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", put_text = "{reverse}{red}{bold}machineName must be between 2 and 63 characters in length. Exiting...{end}") diff --git a/py-kms/pykms_GuiBase.py b/py-kms/pykms_GuiBase.py index 72df2cc..7d3c3df 100644 --- a/py-kms/pykms_GuiBase.py +++ b/py-kms/pykms_GuiBase.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- import os import sys @@ -21,7 +22,8 @@ except ImportError: 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, custom_background +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_v2.0" @@ -55,7 +57,7 @@ def gui_redirect(str_to_print, where): except: print(str_to_print) -##--------------------------------------------------------------------------------------------------------------------------------------------------------- +##----------------------------------------------------------------------------------------------------------------------------------------------------------- class KmsGui(tk.Tk): def browse(self, entrywidget, options): @@ -69,12 +71,14 @@ class KmsGui(tk.Tk): tk.Tk.__init__(self, *args, **kwargs) self.wraplength = 200 serverthread.with_gui = True - self.validation_int = self.register(self.validate_int) + self.validation_int = (self.register(self.validate_int), "%S") + self.validation_float = (self.register(self.validate_float), "%P") ## Define fonts and colors. self.btnwinfont = tkFont.Font(family = 'Times', size = 12, weight = 'bold') self.othfont = tkFont.Font(family = 'Times', size = 9, weight = 'bold') self.optfont = tkFont.Font(family = 'Helvetica', size = 11, weight = 'bold') + self.optfontredux = tkFont.Font(family = 'Helvetica', size = 9, weight = 'bold') self.msgfont = tkFont.Font(family = 'Monospace', size = 6) # need a monospaced type (like courier, etc..). self.customcolors = { 'black' : '#000000', @@ -88,6 +92,9 @@ class KmsGui(tk.Tk): 'cyan' : '#AFEEEE', 'lavender': '#E6E6FA', } + + self.option_add('*TCombobox*Listbox.font', self.optfontredux) + self.gui_create() def gui_create(self): @@ -102,26 +109,117 @@ class KmsGui(tk.Tk): txcol = self.customcolors ## Redirect stderr. sys.stderr = TextRedirect.StderrRedirect(txsrv, txclt, txcol) - + + 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. ------------------------------------------------------------------------------------------------------------- + ## 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.optaddsrvwin = 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 = 'nw') - # self.optaddsrvwin.grid(row = 0, column = 3, 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) ----------------------------------------------------------------------------------------------------------- + ## Create widgets (btnsrvwin) --------------------------------------------------------------------------------------------------------------- self.statesrv = tk.Label(self.btnsrvwin, text = 'Server\nState:\nStopped', font = self.othfont, foreground = self.customcolors['red']) self.runbtnsrv = tk.Button(self.btnsrvwin, text = 'START\nSERVER', background = self.customcolors['green'], foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont, command = self.srv_on_start) @@ -140,84 +238,90 @@ class KmsGui(tk.Tk): self.clearbtnsrv.grid(row = 3, column = 0, padx = 2, pady = 2, sticky = 'ew') self.exitbtnsrv.grid(row = 4, column = 0, padx = 2, pady = 2, sticky = 'ew') - ## Create widgets (optsrvwin) ------------------------------------------------------------------------------------------------------ + ## Create widgets (optsrvwin:Srv:PageWin:PageStart) ----------------------------------------------------------------------------------------- # Version. - ver = tk.Label(self.optsrvwin, text = 'You are running server version: ' + srv_version, foreground = self.customcolors['red'], + ver = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], + text = 'You are running server version: ' + srv_version, foreground = self.customcolors['red'], font = self.othfont) - self.allopts_srv = [] # Ip Address. - srvipaddlbl = tk.Label(self.optsrvwin, text = 'IP Address: ', font = self.optfont) - self.srvipadd = tk.Entry(self.optsrvwin, width = 10, font = self.optfont) + srvipaddlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'IP Address: ', font = self.optfont) + self.srvipadd = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.srvipadd.insert('end', srv_options['ip']['def']) ToolTip(self.srvipadd, text = srv_options['ip']['help'], wraplength = self.wraplength) - myipadd = tk.Label(self.optsrvwin, text = 'Your IP address is: {}'.format(get_ip_address()), foreground = self.customcolors['red'], - font = self.othfont) - self.allopts_srv.append(self.srvipadd) + myipadd = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Your IP address is: {}'.format(get_ip_address()), + foreground = self.customcolors['red'], font = self.othfont) # Port. - srvportlbl = tk.Label(self.optsrvwin, text = 'Port: ', font = self.optfont) - self.srvport = tk.Entry(self.optsrvwin, width = 10, font = self.optfont, validate = "key", validatecommand = (self.validation_int, "%S")) + srvportlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Port: ', font = self.optfont) + self.srvport = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, 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) - self.allopts_srv.append(self.srvport) # EPID. - epidlbl = tk.Label(self.optsrvwin, text = 'EPID: ', font = self.optfont) - self.epid = tk.Entry(self.optsrvwin, width = 10, font = self.optfont) + epidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'EPID: ', font = self.optfont) + self.epid = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.epid.insert('end', str(srv_options['epid']['def'])) ToolTip(self.epid, text = srv_options['epid']['help'], wraplength = self.wraplength) - self.allopts_srv.append(self.epid) # LCID. - lcidlbl = tk.Label(self.optsrvwin, text = 'LCID: ', font = self.optfont) - self.lcid = tk.Entry(self.optsrvwin, width = 10, font = self.optfont, validate = "key", validatecommand = (self.validation_int, "%S")) + lcidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'LCID: ', font = self.optfont) + self.lcid = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, 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) - self.allopts_srv.append(self.lcid) # HWID. - hwidlbl = tk.Label(self.optsrvwin, text = 'HWID: ', font = self.optfont) - self.hwid = tk.Entry(self.optsrvwin, width = 10, font = self.optfont) - self.hwid.insert('end', srv_options['hwid']['def']) + hwidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'HWID: ', font = self.optfont) + self.hwid = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = (str(srv_options['hwid']['def']), 'RANDOM'), + width = 17, height = 10, font = self.optfontredux) + self.hwid.set(str(srv_options['hwid']['def'])) ToolTip(self.hwid, text = srv_options['hwid']['help'], wraplength = self.wraplength) - self.allopts_srv.append(self.hwid) # Client Count - countlbl = tk.Label(self.optsrvwin, text = 'Client Count: ', font = self.optfont) - self.count = tk.Entry(self.optsrvwin, width = 10, font = self.optfont) + countlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Client Count: ', font = self.optfont) + self.count = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.count.insert('end', str(srv_options['count']['def'])) ToolTip(self.count, text = srv_options['count']['help'], wraplength = self.wraplength) - self.allopts_srv.append(self.count) # Activation Interval. - activlbl = tk.Label(self.optsrvwin, text = 'Activation Interval: ', font = self.optfont) - self.activ = tk.Entry(self.optsrvwin, width = 10, font = self.optfont, validate = "key", validatecommand = (self.validation_int, "%S")) + activlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Activation Interval: ', font = self.optfont) + self.activ = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, 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) - self.allopts_srv.append(self.activ) # Renewal Interval. - renewlbl = tk.Label(self.optsrvwin, text = 'Activation Interval: ', font = self.optfont) - self.renew = tk.Entry(self.optsrvwin, width = 10, font = self.optfont, validate = "key", validatecommand = (self.validation_int, "%S")) + renewlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Activation Interval: ', font = self.optfont) + self.renew = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, 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) - self.allopts_srv.append(self.renew) # Logfile. - srvfilelbl = tk.Label(self.optsrvwin, text = 'Logfile Path / Name: ', font = self.optfont) - self.srvfile = tk.Entry(self.optsrvwin, width = 10, font = self.optfont) + srvfilelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logfile Path / Name: ', font = self.optfont) + self.srvfile = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.srvfile.insert('end', srv_options['lfile']['def']) self.srvfile.xview_moveto(1) ToolTip(self.srvfile, text = srv_options['lfile']['help'], wraplength = self.wraplength) - self.allopts_srv.append(self.srvfile) - filebtnwin = tk.Button(self.optsrvwin, text = 'Browse', command = lambda: self.browse(self.srvfile, srv_options)) - self.allopts_srv.append(filebtnwin) + srvfilebtnwin = tk.Button(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Browse', + command = lambda: self.browse(self.srvfile, srv_options)) + # Loglevel. - srvlevellbl = tk.Label(self.optsrvwin, text = 'Loglevel: ', font = self.optfont) - self.srvlevel = ttk.Combobox(self.optsrvwin, values = tuple(srv_options['llevel']['choi']), width = 10) + srvlevellbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.optfont) + self.srvlevel = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = tuple(srv_options['llevel']['choi']), + width = 10, height = 10, font = self.optfontredux, state = "readonly") self.srvlevel.set(srv_options['llevel']['def']) ToolTip(self.srvlevel, text = srv_options['llevel']['help'], wraplength = self.wraplength) - self.allopts_srv.append(self.srvlevel) - # Sqlite database. - self.chkval = tk.BooleanVar() - self.chkval.set(srv_options['sql']['def']) - chksql = tk.Checkbutton(self.optsrvwin, text = 'Create Sqlite\nDatabase', font = self.optfont, var = self.chkval) - ToolTip(chksql, text = srv_options['sql']['help'], wraplength = self.wraplength) - self.allopts_srv.append(chksql) - ## Layout widgets (optsrvwin) + self.chksrvfile = ListboxOfRadiobuttons(self.pagewidgets["Srv"]["PageWin"]["PageStart"], + ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], + self.optfontredux, + changed = [(self.srvfile, srv_options['lfile']['def']), + (srvfilebtnwin, ''), + (self.srvlevel, srv_options['llevel']['def'])], + width = 10, height = 1, borderwidth = 2, relief = 'ridge') + + # Logsize. + srvsizelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.optfont) + self.srvsize = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, 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) + + ## 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') @@ -238,22 +342,41 @@ class KmsGui(tk.Tk): 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') - filebtnwin.grid(row = 10, column = 2, padx = 5, pady = 5, sticky = 'ew') - srvlevellbl.grid(row = 11, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvlevel.grid(row = 11, column = 1, padx = 5, pady = 5, sticky = 'ew') - chksql.grid(row = 12, 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') + 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 and layout (msgsrvwin) ----------------------------------------------------------------------------------------------- + ## Create widgets (optsrvwin:Srv:PageWin:PageEnd)------------------------------------------------------------------------------------------- + # Timeout connection. + timeout0lbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Timeout connection: ', font = self.optfont) + self.timeout0 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 10, font = self.optfont) + self.timeout0.insert('end', str(srv_options['time0']['def'])) + ToolTip(self.timeout0, text = srv_options['time0']['help'], wraplength = self.wraplength) + # Sqlite database. + self.chkvalsql = tk.BooleanVar() + self.chkvalsql.set(srv_options['sql']['def']) + chksql = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Create Sqlite\nDatabase', + font = self.optfont, var = self.chkvalsql) + ToolTip(chksql, text = srv_options['sql']['help'], wraplength = self.wraplength) + + ## Layout widgets (optsrvwin:Srv:PageWin:PageEnd) + tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 0, height = 0).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') + timeout0lbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e') + self.timeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew') + chksql.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'ew') + + # Store Srv 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.msgfont) self.textboxsrv.put() - - ## Create widgets (optaddsrvwin) ----------------------------------------------------------------------------------------------------- - # self.timeout = tk.Entry(self.optaddsrvwin, width = 10) - # self.timeout.insert('end', '555') - ## Layout widgets (optaddsrvwin) - # self.timeout.grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'e') - + def gui_complete(self): ## Create client widgets (optcltwin, msgcltwin, btncltwin) self.update_idletasks() # update Gui to get btnsrvwin values --> btncltwin. @@ -284,76 +407,93 @@ class KmsGui(tk.Tk): self.btncltwin_X = xb + 2 self.btncltwin_Y = yb + hb + 10 self.btncltwin.place(x = self.btncltwin_X, y = self.btncltwin_Y, bordermode = 'inside', anchor = 'nw') - self.optcltwin.grid(row = 0, column = 4, padx = 2, pady = 2, sticky = 'nw') + 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) ------------------------------------------------------------------------------------------------------------ + # Create widgets (btncltwin) ---------------------------------------------------------------------------------------------------------------- self.runbtnclt = tk.Button(self.btncltwin, text = 'START\nCLIENT', background = self.customcolors['blue'], foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont, state = 'disabled', command = self.clt_on_start) - #self.othbutt = tk.Button(self.btncltwin, text = 'Botton\n2', background = self.customcolors['green'], - # foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont) +## self.othbutt = tk.Button(self.btncltwin, text = 'Botton\n2', background = self.customcolors['green'], +## foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont) # Layout widgets (btncltwin) self.runbtnclt.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'ew') - #self.othbutt.grid(row = 1, column = 0, padx = 2, pady = 2, sticky = 'ew') +## self.othbutt.grid(row = 1, column = 0, padx = 2, pady = 2, sticky = 'ew') - # Create widgets (optcltwin) ------------------------------------------------------------------------------------------------------------ + # Create widgets (optcltwin:Clt:PageWin:PageStart) ------------------------------------------------------------------------------------------ # Version. - cltver = tk.Label(self.optcltwin, text = 'You are running client version: ' + clt_version, foreground = self.customcolors['red'], - font = self.othfont) - self.allopts_clt = [] + cltver = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'You are running client version: ' + clt_version, + foreground = self.customcolors['red'], font = self.othfont) # Ip Address. - cltipaddlbl = tk.Label(self.optcltwin, text = 'IP Address: ', font = self.optfont) - self.cltipadd = tk.Entry(self.optcltwin, width = 10, font = self.optfont) + cltipaddlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'IP Address: ', font = self.optfont) + self.cltipadd = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.cltipadd.insert('end', clt_options['ip']['def']) ToolTip(self.cltipadd, text = clt_options['ip']['help'], wraplength = self.wraplength) - self.allopts_clt.append(self.cltipadd) # Port. - cltportlbl = tk.Label(self.optcltwin, text = 'Port: ', font = self.optfont) - self.cltport = tk.Entry(self.optcltwin, width = 10, font = self.optfont, validate = "key", validatecommand = (self.validation_int, "%S")) + cltportlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Port: ', font = self.optfont) + self.cltport = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont, 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) - self.allopts_clt.append(self.cltport) # Mode. - cltmodelbl = tk.Label(self.optcltwin, text = 'Mode: ', font = self.optfont) - self.cltmode = ttk.Combobox(self.optcltwin, values = tuple(clt_options['mode']['choi']), width = 10) + cltmodelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Mode: ', font = self.optfont) + self.cltmode = ttk.Combobox(self.pagewidgets["Clt"]["PageWin"]["PageStart"], values = tuple(clt_options['mode']['choi']), + width = 17, height = 10, font = self.optfontredux, state = "readonly") self.cltmode.set(clt_options['mode']['def']) ToolTip(self.cltmode, text = clt_options['mode']['help'], wraplength = self.wraplength) - self.allopts_clt.append(self.cltmode) # CMID. - cltcmidlbl = tk.Label(self.optcltwin, text = 'CMID: ', font = self.optfont) - self.cltcmid = tk.Entry(self.optcltwin, width = 10, font = self.optfont) + cltcmidlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'CMID: ', font = self.optfont) + self.cltcmid = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.cltcmid.insert('end', str(clt_options['cmid']['def'])) ToolTip(self.cltcmid, text = clt_options['cmid']['help'], wraplength = self.wraplength) - self.allopts_clt.append(self.cltcmid) # Machine Name. - cltnamelbl = tk.Label(self.optcltwin, text = 'Machine Name: ', font = self.optfont) - self.cltname = tk.Entry(self.optcltwin, width = 10, font = self.optfont) + cltnamelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Machine Name: ', font = self.optfont) + self.cltname = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.cltname.insert('end', str(clt_options['name']['def'])) ToolTip(self.cltname, text = clt_options['name']['help'], wraplength = self.wraplength) - self.allopts_clt.append(self.cltname) # Logfile. - cltfilelbl = tk.Label(self.optcltwin, text = 'Logfile Path / Name: ', font = self.optfont) - self.cltfile = tk.Entry(self.optcltwin, width = 10, font = self.optfont) + cltfilelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logfile Path / Name: ', font = self.optfont) + self.cltfile = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont) self.cltfile.insert('end', clt_options['lfile']['def']) self.cltfile.xview_moveto(1) ToolTip(self.cltfile, text = clt_options['lfile']['help'], wraplength = self.wraplength) - self.allopts_clt.append(self.cltfile) - cltfilebtnwin = tk.Button(self.optcltwin, text = 'Browse', command = lambda: self.browse(self.cltfile, clt_options)) - self.allopts_clt.append(cltfilebtnwin) + cltfilebtnwin = tk.Button(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Browse', + command = lambda: self.browse(self.cltfile, clt_options)) # Loglevel. - cltlevellbl = tk.Label(self.optcltwin, text = 'Loglevel: ', font = self.optfont) - self.cltlevel = ttk.Combobox(self.optcltwin, values = tuple(clt_options['llevel']['choi']), width = 10) + cltlevellbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.optfont) + self.cltlevel = ttk.Combobox(self.pagewidgets["Clt"]["PageWin"]["PageStart"], values = tuple(clt_options['llevel']['choi']), + width = 10, height = 10, font = self.optfontredux, state = "readonly") self.cltlevel.set(clt_options['llevel']['def']) ToolTip(self.cltlevel, text = clt_options['llevel']['help'], wraplength = self.wraplength) - self.allopts_clt.append(self.cltlevel) + + self.chkcltfile = ListboxOfRadiobuttons(self.pagewidgets["Clt"]["PageWin"]["PageStart"], + ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], + self.optfontredux, + changed = [(self.cltfile, clt_options['lfile']['def']), + (cltfilebtnwin, ''), + (self.cltlevel, clt_options['llevel']['def'])], + width = 10, height = 1, borderwidth = 2, relief = 'ridge') + # Logsize. + cltsizelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.optfont) + self.cltsize = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont, 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) - # Layout widgets (optcltwin) + # 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') @@ -365,40 +505,67 @@ class KmsGui(tk.Tk): 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 = 'ew') - self.cltfile.grid(row = 6, column = 1, padx = 5, pady = 5, sticky = 'e') + 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') - cltlevellbl.grid(row = 7, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltlevel.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = 'ew') + self.chkcltfile.grid(row = 7, column = 1, 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') + + ## Create widgets (optcltwin:Clt:PageWin:PageEnd) ------------------------------------------------------------------------------------------- + + ## Layout widgets (optcltwin:Clt:PageWin:PageEnd) + tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 0, height = 0).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') + + # Store Clt widgets. + self.storewidgets_clt = self.gui_store(side = "Clt", typewidgets = ['Button', 'Entry', 'TCombobox']) + self.storewidgets_clt.append(self.chkcltfile) - # Create widgets and layout (msgcltwin) ---------------------------------------------------------------------------------------------------------- + # Create widgets and layout (msgcltwin) ----------------------------------------------------------------------------------------------------- self.textboxclt = TextDoubleScroll(self.msgcltwin, background = self.customcolors['black'], wrap = 'none', state = 'disabled', relief = 'ridge', font = self.msgfont) self.textboxclt.put() def prep_option(self, value): - value = None if value == 'None' else value try: + # is an INT return int(value) except (TypeError, ValueError): - # is NONE or is a STRING. - return value + try: + # is a FLOAT + return float(value) + except (TypeError, ValueError): + # is a STRING. + return value - def prep_logfile(self, optionlog): - if optionlog.startswith('FILESTDOUT '): - split = optionlog.split('FILESTDOUT ') - split[0] = 'FILESTDOUT' - return split - elif optionlog.startswith('STDOUT '): - split = optionlog.split('STDOUT ') - split[0] = 'STDOUT' - return split - else: - return optionlog + def prep_logfile(self, filepath): + # 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) + st = self.chksrvfile.state() + if st == 'FILE': + return filepath + elif st in ['FILESTDOUT', 'STDOUTOFF']: + return [st, filepath] + elif st in ['STDOUT', 'FILEOFF']: + return st def validate_int(self, value): - return value.isdigit() - + 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 = False): if self.optcltwin.winfo_ismapped() or force: self.shbtnclt['text'] = 'SHOW\nCLIENT' @@ -436,7 +603,7 @@ class KmsGui(tk.Tk): 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.prep_option(self.epid.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()) @@ -444,11 +611,9 @@ class KmsGui(tk.Tk): srv_config[srv_options['renewal']['des']] = self.prep_option(self.renew.get()) srv_config[srv_options['lfile']['des']] = self.prep_logfile(self.srvfile.get()) srv_config[srv_options['llevel']['des']] = self.srvlevel.get() - srv_config[srv_options['sql']['des']] = self.chkval.get() - - ## TODO. - srv_config[srv_options['lsize']['des']] = 0 - srv_config[srv_options['time']['des']] = None + srv_config[srv_options['sql']['des']] = self.chkvalsql.get() + srv_config[srv_options['lsize']['des']] = self.prep_option(self.srvsize.get()) + srv_config[srv_options['time0']['des']] = self.prep_option(self.timeout0.get()) serverqueue.put('start') @@ -468,13 +633,13 @@ class KmsGui(tk.Tk): if on_start: self.runbtnsrv.configure(text = 'STOP\nSERVER', background = self.customcolors['red'], foreground = self.customcolors['white']) - for widget in self.allopts_srv: + 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']) - for widget in self.allopts_srv: + for widget in self.storewidgets_srv: widget.configure(state = 'normal') self.runbtnclt.configure(state = 'disabled') @@ -494,20 +659,18 @@ class KmsGui(tk.Tk): self.clt_eject_thread.start() self.on_clear([txsrv, txclt]) - for widget in self.allopts_clt + [self.runbtnsrv, self.runbtnclt]: + for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt]: widget.configure(state = 'disabled') 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.prep_option(self.cltcmid.get()) - clt_config[clt_options['name']['des']] = self.prep_option(self.cltname.get()) + clt_config[clt_options['cmid']['des']] = self.cltcmid.get() + clt_config[clt_options['name']['des']] = self.cltname.get() clt_config[clt_options['llevel']['des']] = self.cltlevel.get() clt_config[clt_options['lfile']['des']] = self.prep_logfile(self.cltfile.get()) - - ## TODO. - clt_config[clt_options['lsize']['des']] = 0 + clt_config[clt_options['lsize']['des']] = self.prep_option(self.cltsize.get()) # run client (in a thread). self.clientthread = client_thread(name = "Thread-Clt") @@ -518,7 +681,7 @@ class KmsGui(tk.Tk): def clt_eject(self): while self.clientthread.is_alive(): sleep(0.1) - for widget in self.allopts_clt + [self.runbtnsrv, self.runbtnclt]: + for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt]: widget.configure(state = 'normal') def on_exit(self): diff --git a/py-kms/pykms_GuiMisc.py b/py-kms/pykms_GuiMisc.py index 74bcb64..b1c7914 100644 --- a/py-kms/pykms_GuiMisc.py +++ b/py-kms/pykms_GuiMisc.py @@ -4,6 +4,8 @@ import os import re import sys from collections import Counter +from time import sleep +import threading try: # Python 2.x imports @@ -18,7 +20,7 @@ except ImportError: from pykms_Format import MsgMap, unshell_message, unformat_message -#--------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------------ # https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter class ToolTip(object): @@ -117,7 +119,7 @@ class ToolTip(object): tw.destroy() self.tw = None -##-------------------------------------------------------------------------------------------------------------------------------------------------------- +##----------------------------------------------------------------------------------------------------------------------------------------------------------- # https://stackoverflow.com/questions/2914603/segmentation-fault-while-redirecting-sys-stdout-to-tkinter-text-widget # https://stackoverflow.com/questions/7217715/threadsafe-printing-across-multiple-processes-python-2-x # https://stackoverflow.com/questions/3029816/how-do-i-get-a-thread-safe-print-in-python-2-6 @@ -237,7 +239,7 @@ class TextRedirect(object): self.srv_text_space.see('end') self.srv_text_space.configure(state = 'disabled') -##------------------------------------------------------------------------------------------------------------------------------------------------------- +##----------------------------------------------------------------------------------------------------------------------------------------------------------- class TextDoubleScroll(tk.Frame): def __init__(self, master, **kwargs): """ Initialize. @@ -245,8 +247,8 @@ class TextDoubleScroll(tk.Frame): - vertical scrollbar - text widget """ - self.master = master tk.Frame.__init__(self, master) + self.master = master self.textbox = tk.Text(self.master, **kwargs) self.sizegrip = ttk.Sizegrip(self.master) @@ -278,43 +280,232 @@ class TextDoubleScroll(tk.Frame): """ 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 = [ widget for widget in allwidgets if widget.winfo_class() == 'Canvas'] + 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__ )) + "/pykms_Keys.gif") + 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 = [] - - for widget in widgets: - x, y, w, h = window.get_position(widget) - cropped = img.crop((x, y, x + w, y + h)) - cropped.putalpha(24) - window.backcrops.append(ImageTk.PhotoImage(cropped)) - - # Not in same loop to prevent reference garbage. - for crop, widget in zip(window.backcrops, widgets): - widget.create_image(1, 1, image = crop, anchor = 'nw') + 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(96) + 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: + for widget in widgets_alphalow + widgets_alphahigh: widget.configure(background = window.customcolors['lavender']) # Hide client. window.clt_on_show(force = 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.btnwinfont, + 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') + elif wclass == 'TCombobox': + 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() diff --git a/py-kms/pykms_Misc.py b/py-kms/pykms_Misc.py index 3db56a5..022d473 100644 --- a/py-kms/pykms_Misc.py +++ b/py-kms/pykms_Misc.py @@ -6,9 +6,9 @@ import logging import os import argparse from logging.handlers import RotatingFileHandler -from pykms_Format import ColorExtraMap, pretty_printer +from pykms_Format import ColorExtraMap, ShellMessage, pretty_printer -#----------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------------ # https://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility # https://stackoverflow.com/questions/17558552/how-do-i-add-custom-field-to-python-log-format-string @@ -93,14 +93,19 @@ def logger_create(log_obj, config, mode = 'a'): # Configure visualization. log_handlers = [] - if any(i in ['STDOUT', 'FILESTDOUT'] for i in config['logfile']): - # (Only STDOUT) or (logfile and STDOUT) - log_handlers.append(logging.StreamHandler(sys.stdout)) - if 'FILESTDOUT' in config['logfile']: + if any(opt in ['STDOUT', 'FILESTDOUT', 'STDOUTOFF'] for opt in config['logfile']): + if 'STDOUTOFF' not in config['logfile']: + # STDOUT. + log_handlers.append(logging.StreamHandler(sys.stdout)) + if any(opt in ['STDOUTOFF', 'FILESTDOUT'] for opt in config['logfile']): + # FILESTDOUT or STDOUTOFF. log_handlers.append(RotatingFileHandler(filename = config['logfile'][1], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512), backupCount = 1, encoding = None, delay = 0)) + elif 'FILEOFF' in config['logfile']: + config['loglevel'] = 'ERROR' # for py-kms GUI: set a recognized level never used. + log_handlers.append(logging.FileHandler(os.devnull)) else: - # Only logfile. + # FILE. log_handlers.append(RotatingFileHandler(filename = config['logfile'][0], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512), backupCount = 1, encoding = None, delay = 0)) @@ -132,7 +137,7 @@ def logger_create(log_obj, config, mode = 'a'): log_obj.setLevel(config['loglevel']) [ log_obj.addHandler(log_handler) for log_handler in log_handlers ] -#---------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------------ def check_logfile(optionlog, defaultlog, where): if not isinstance(optionlog, list): @@ -154,7 +159,8 @@ def check_logfile(optionlog, defaultlog, where): if lenopt > 2: pretty_printer(put_text = msg_long, where = where, to_exit = True) - if 'FILESTDOUT' in optionlog: + + if (any(opt in ['FILESTDOUT', 'STDOUTOFF'] for opt in optionlog)): if lenopt == 1: # add default logfile. optionlog.append(defaultlog) @@ -164,12 +170,13 @@ def check_logfile(optionlog, defaultlog, where): else: if lenopt == 2: pretty_printer(put_text = msg_long, where = where, to_exit = True) - elif lenopt == 1 and 'STDOUT' not in optionlog: + elif lenopt == 1 and (any(opt not in ['STDOUT', 'FILEOFF'] for opt in optionlog)): # check directory path. checkdir(optionlog[0]) + return optionlog -#---------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------------ # Valid language identifiers to be used in the EPID (see "kms.c" in vlmcsd) ValidLcid = [1025, 1026, 1027, 1028, 1029, @@ -213,16 +220,16 @@ def check_lcid(lcid, log_obj): return fixlcid return lcid -#---------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------------ -class KmsException(Exception): +class KmsParserException(Exception): pass class KmsParser(argparse.ArgumentParser): def error(self, message): - raise KmsException(message) + raise KmsParserException(message) -class KmsHelper(object): +class KmsParserHelp(object): def replace(self, parser, replace_epilog_with): text = parser.format_help().splitlines() help_list = [] @@ -255,7 +262,31 @@ class KmsHelper(object): print(parser_base.epilog + '\n') parser_base.exit() -#---------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------------ +def proper_none(dictionary): + for key in dictionary.keys(): + dictionary[key] = None if dictionary[key] == 'None' else dictionary[key] + +def check_setup(config, options, logger, where): + # Check logfile. + config['logfile'] = check_logfile(config['logfile'], options['lfile']['def'], where = where) + + # Setup hidden or not messages. + hidden = ['STDOUT', 'FILESTDOUT', 'STDOUTOFF'] + ShellMessage.view = (False if any(opt in hidden for opt in config['logfile']) else True) + + # Create log. + logger_create(logger, config, mode = 'a') + + # 'None'--> None. + proper_none(config) + + # Check port. + if not 1 <= config['port'] <= 65535: + pretty_printer(log_obj = logger.error, to_exit = True, + put_text = "{reverse}{red}{bold}Port number '%s' is invalid. Enter between 1 - 65535. Exiting...{end}" %config['port']) + +#------------------------------------------------------------------------------------------------------------------------------------------------------------ # http://joshpoley.blogspot.com/2011/09/hresults-user-0x004.html (slerror.h) ErrorCodes = { diff --git a/py-kms/pykms_Server.py b/py-kms/pykms_Server.py index 883d0de..9b5eb06 100755 --- a/py-kms/pykms_Server.py +++ b/py-kms/pykms_Server.py @@ -27,9 +27,9 @@ except ImportError: import pykms_RpcBind, pykms_RpcRequest from pykms_RpcBase import rpcBase from pykms_Dcerpc import MSRPCHeader -from pykms_Misc import logger_create, check_logfile, check_lcid -from pykms_Misc import KmsParser, KmsException, KmsHelper -from pykms_Format import enco, deco, ShellMessage, pretty_printer +from pykms_Misc import check_setup, check_lcid +from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp +from pykms_Format import enco, deco, pretty_printer from Etrigan import Etrigan, Etrigan_parser, Etrigan_check, Etrigan_job srv_version = "py-kms_2020-02-02" @@ -177,11 +177,13 @@ for server OSes and Office >=5', 'def' : None, 'des' : "CurrentClientCount"}, 'def' : False, 'des' : "sqlite"}, 'hwid' : {'help' : 'Use this option to specify a HWID. The HWID must be an 16-character string of hex characters. \ The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID.', 'def' : "364F463A8863D35F", 'des' : "hwid"}, - 'time' : {'help' : 'Max time (in seconds) for server to generate an answer. If \"None\" (default) serve forever.', 'def' : None, 'des' : "timeout"}, + 'time0' : {'help' : 'Maximum inactivity time (in seconds) after which the connection with the client is closed. If \"None\" (default) serve forever.', + 'def' : None, 'des' : "timeout_idle"}, 'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel", 'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]}, - 'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logserver.log\". Type \"STDOUT\" to view \ -log info on stdout. Type \"FILESTDOUT\" to combine previous actions.', + 'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logserver.log\". \ +Type \"STDOUT\" to view log info on stdout. Type \"FILESTDOUT\" to combine previous actions. \ +Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to create logfile.', 'def' : os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pykms_logserver.log'), 'des' : "logfile"}, 'lsize' : {'help' : 'Use this flag to set a maximum size (in MB) to the output log file. Desactivated by default.', 'def' : 0, 'des': "logsize"}, } @@ -199,7 +201,7 @@ def server_options(): server_parser.add_argument("-l", "--lcid", action = "store", dest = srv_options['lcid']['des'], default = srv_options['lcid']['def'], help = srv_options['lcid']['help'], type = int) server_parser.add_argument("-c", "--client-count", action = "store", dest = srv_options['count']['des'] , default = srv_options['count']['def'], - help = srv_options['count']['help'], type = int) + help = srv_options['count']['help'], type = str) server_parser.add_argument("-a", "--activation-interval", action = "store", dest = srv_options['activation']['des'], default = srv_options['activation']['def'], help = srv_options['activation']['help'], type = int) server_parser.add_argument("-r", "--renewal-interval", action = "store", dest = srv_options['renewal']['des'], default = srv_options['renewal']['def'], @@ -208,8 +210,8 @@ def server_options(): help = srv_options['sql']['help']) server_parser.add_argument("-w", "--hwid", action = "store", dest = srv_options['hwid']['des'], default = srv_options['hwid']['def'], help = srv_options['hwid']['help'], type = str) - server_parser.add_argument("-t", "--timeout", action = "store", dest = srv_options['time']['des'], default = srv_options['time']['def'], - help = srv_options['time']['help'], type = int) + server_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = srv_options['time0']['des'], default = srv_options['time0']['def'], + help = srv_options['time0']['help'], type = str) server_parser.add_argument("-V", "--loglevel", action = "store", dest = srv_options['llevel']['des'], choices = srv_options['llevel']['choi'], default = srv_options['llevel']['def'], help = srv_options['llevel']['help'], type = str) server_parser.add_argument("-F", "--logfile", nargs = "+", action = "store", dest = srv_options['lfile']['des'], default = srv_options['lfile']['def'], @@ -234,7 +236,7 @@ def server_options(): try: if "-h" in sys.argv[1:]: - KmsHelper().printer(parsers = [server_parser, daemon_parser, etrigan_parser]) + KmsParserHelp().printer(parsers = [server_parser, daemon_parser, etrigan_parser]) # Set defaults for config. # case: python3 pykms_Server.py @@ -267,11 +269,11 @@ def server_options(): # case: python3 pykms_Server.py 1.2.3.4 1234 --main_optionals knw_args, knw_extras = server_parser.parse_known_args() if knw_extras != []: - raise KmsException("unrecognized arguments: %s" %' '.join(knw_extras)) + raise KmsParserException("unrecognized arguments: %s" %' '.join(knw_extras)) else: srv_config.update(vars(knw_args)) - except KmsException as e: + except KmsParserException as e: pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True) @@ -311,14 +313,8 @@ def server_daemon(): Etrigan_job(srv_config['operation'], serverdaemon) def server_check(): - # Check logfile. - srv_config['logfile'] = check_logfile(srv_config['logfile'], srv_options['lfile']['def'], where = "srv") - - # Setup hidden or not messages. - ShellMessage.view = ( False if any(i in ['STDOUT', 'FILESTDOUT'] for i in srv_config['logfile']) else True ) - - # Create log. - logger_create(loggersrv, srv_config, mode = 'a') + # Setup and some checks. + check_setup(srv_config, srv_options, loggersrv, where = "srv") # Random HWID. if srv_config['hwid'] == "RANDOM": @@ -361,14 +357,21 @@ def server_check(): else: srv_config['dbSupport'] = True - # Check port. - if not 1 <= srv_config['port'] <= 65535: - pretty_printer(log_obj = loggersrv.error, to_exit = True, - put_text = "{red}{bold}Port number '%s' is invalid. Enter between 1 - 65535. Exiting...{end}" %srv_config['port']) + # Check client count, timeout. + list_dest = ['CurrentClientCount', 'timeout_idle'] + list_opt = ['--client-count', '--timeout-idle'] + for dest, opt in zip(list_dest, list_opt): + if srv_config[dest] is not None: + if not srv_config[dest].isdigit(): + pretty_printer(log_obj = loggersrv.error, to_exit = True, + put_text = "{reverse}{red}{bold}Option %s is invalid with '%s'. Exiting...{end}" + %(opt, srv_config[dest])) + else: + srv_config[dest] = int(srv_config[dest]) def server_create(): server = KeyServer((srv_config['ip'], srv_config['port']), kmsServerHandler) - server.timeout = srv_config['timeout'] + server.timeout = srv_config['timeout_idle'] loggersrv.info("TCP server listening at %s on port %d." % (srv_config['ip'], srv_config['port'])) loggersrv.info("HWID: %s" % deco(binascii.b2a_hex(srv_config['hwid']), 'utf-8').upper()) return server @@ -427,7 +430,7 @@ def server_with_gui(): height = 660 root = pykms_GuiBase.KmsGui() - root.title(pykms_GuiBase.gui_description + ' ' + pykms_GuiBase.gui_version) + root.title(pykms_GuiBase.gui_description + ' (' + pykms_GuiBase.gui_version + ')') # Main window initial position. ## https://stackoverflow.com/questions/14910858/how-to-specify-where-a-tkinter-window-opens ws = root.winfo_screenwidth()