Deprecated the code= keyword of contrib/menusystem - this is replaced with a callback keyword where a function object is supplied rather than a code string. Code still works, but will give a deprecation warning in log. Cleaned up the code and fixed dynamic yes/no allocation. Should handle the remaining issues of #596.

This commit is contained in:
Griatch 2014-10-23 01:27:31 +02:00
parent 671c541a1c
commit 60931c26a7
2 changed files with 98 additions and 50 deletions

View file

@ -147,14 +147,6 @@ class CmdPasswordSelect(Command):
# abort menu, do cleanup. # abort menu, do cleanup.
self.menutree.goto("END") self.menutree.goto("END")
# we are logged in. Look around.
character = player.character
if character:
character.execute_cmd("look")
else:
# we have no character yet; use player's look, if it exists
player.execute_cmd("look")
# Menu entry 2a - Creating a Username # Menu entry 2a - Creating a Username
@ -331,7 +323,7 @@ node2a = MenuNode("node2a", text="Please enter your desired account name (empty
nodefaultcmds=True) nodefaultcmds=True)
node2b = MenuNode("node2b", text="Please enter your password (empty to go back).", node2b = MenuNode("node2b", text="Please enter your password (empty to go back).",
links=["node2a", "START"], links=["node2a", "START"],
helptext="Your password cannot contain any characters.", helptext="Try to pick a long and hard-to-guess password.",
keywords=[CMD_NOINPUT, CMD_NOMATCH], keywords=[CMD_NOINPUT, CMD_NOMATCH],
selectcmds=[CmdPasswordCreateBack, CmdPasswordCreate], selectcmds=[CmdPasswordCreateBack, CmdPasswordCreate],
nodefaultcmds=True) nodefaultcmds=True)

View file

@ -27,10 +27,11 @@ this custom module.
The test command is also a good example of how to use this module in code. The test command is also a good example of how to use this module in code.
""" """
from types import MethodType
from ev import syscmdkeys from ev import syscmdkeys
from ev import Command, CmdSet, utils from ev import Command, CmdSet, utils
from ev import default_cmds from ev import default_cmds, logger
# imported only to make it available during execution of code blocks # imported only to make it available during execution of code blocks
import ev import ev
@ -53,11 +54,20 @@ class CmdMenuNode(Command):
help_category = "Menu" help_category = "Menu"
menutree = None menutree = None
callback = None
# deprecated
code = None code = None
def func(self): def func(self):
"Execute a selection" "Execute a selection"
if self.code:
if self.callback:
try:
self.callback()
except Exception, e:
self.caller.msg("%s\n{rThere was an error with this selection.{n" % e)
elif self.code:
ev.logger.log_depmsg("menusystem.code is deprecated. Use menusystem.func.")
try: try:
exec(self.code) exec(self.code)
except Exception, e: except Exception, e:
@ -132,6 +142,10 @@ class MenuCmdSet(CmdSet):
key = "menucmdset" key = "menucmdset"
priority = 1 priority = 1
mergetype = "Replace" mergetype = "Replace"
# secure the menu against local cmdsets (but leave channels)
no_objs = True
no_exits = True
no_channels = False
def at_cmdset_creation(self): def at_cmdset_creation(self):
"populate cmdset" "populate cmdset"
@ -204,16 +218,25 @@ class MenuTree(object):
return return
# not exiting, look for a valid code. # not exiting, look for a valid code.
node = self.tree.get(key, None) node = self.tree.get(key, None)
# make caller available on node
node.caller = self.caller
if node: if node:
# initialize - this creates new cmdset # call on-node callback
node.init(self) if node.callback:
try:
node.callback()
except Exception:
logger.log_trace()
self.caller.msg("{rNode callback could not be executed for node %s. Continuing anyway.{n" % key)
if node.code: if node.code:
# Execute eventual code active on this # Execute eventual code active on this node. self.caller is available at this point.
# node. self.caller is available at this point. ev.logger.log_depmsg("menusystem.code is deprecated. Use menusystem.callback.")
try: try:
exec(node.code) exec(node.code)
except Exception: except Exception:
self.caller.msg("{rCode could not be executed for node %s. Continuing anyway.{n" % key) self.caller.msg("{rCode could not be executed for node %s. Continuing anyway.{n" % key)
# initialize - this creates new cmdset
node.init(self)
# clean old menu cmdset and replace with the new one # clean old menu cmdset and replace with the new one
self.caller.cmdset.delete("menucmdset") self.caller.cmdset.delete("menucmdset")
self.caller.cmdset.add(node.cmdset) self.caller.cmdset.add(node.cmdset)
@ -235,7 +258,7 @@ class MenuNode(object):
""" """
def __init__(self, key, text="", links=None, linktexts=None, def __init__(self, key, text="", links=None, linktexts=None,
keywords=None, cols=1, helptext=None, keywords=None, cols=1, helptext=None,
selectcmds=None, code="", nodefaultcmds=False, separator=""): selectcmds=None, callback=None, code="", nodefaultcmds=False, separator=""):
""" """
key - the unique identifier of this node. key - the unique identifier of this node.
text - is the text that will be displayed at top when viewing this text - is the text that will be displayed at top when viewing this
@ -261,10 +284,13 @@ class MenuNode(object):
CMD_NOENTRY, in which case no text will be generated. These CMD_NOENTRY, in which case no text will be generated. These
commands have access to self.menutree and so can be used to commands have access to self.menutree and so can be used to
select nodes. select nodes.
code - functional code. This will be executed just before this code - functional code. Deprecated. This will be executed just before this
node is loaded (i.e. as soon after it's been selected from node is loaded (i.e. as soon after it's been selected from
another node). self.caller is available to call from this another node). self.caller is available to call from this
code block, as well as ev. code block, as well as ev.
callback - function callback. This will be called as callback(currentnode) just
before this node is loaded (i.e. as soon as possible as it's
been selected from another node). currentnode.caller is available.
nodefaultcmds - if true, don't offer the default help and look commands nodefaultcmds - if true, don't offer the default help and look commands
in the node in the node
separator - this string will be put on the line between menu nodes. separator - this string will be put on the line between menu nodes.
@ -277,10 +303,14 @@ class MenuNode(object):
self.cols = cols self.cols = cols
self.selectcmds = selectcmds self.selectcmds = selectcmds
self.code = code self.code = code
self.callback = MethodType(callback, self, MenuNode) if callback else None
self.nodefaultcmds = nodefaultcmds self.nodefaultcmds = nodefaultcmds
self.separator = separator self.separator = separator
Nlinks = len(self.links) Nlinks = len(self.links)
if code:
ev.logger.log_depmsg("menusystem.code is deprecated. Use menusystem.callback.")
# validate the input # validate the input
if not self.links: if not self.links:
self.links = [] self.links = []
@ -351,10 +381,13 @@ class MenuNode(object):
if self.selectcmds[i]: if self.selectcmds[i]:
cmd = self.selectcmds[i]() cmd = self.selectcmds[i]()
else: else:
# this is the operable command, it moves us to the next node.
cmd = CmdMenuNode() cmd = CmdMenuNode()
cmd.key = str(i + 1) cmd.key = str(i + 1)
# this is the operable command, it moves us to the next node. cmd.link = link
cmd.code = "self.menutree.goto('%s')" % link def _callback(self):
self.menutree.goto(self.link)
cmd.callback = MethodType(_callback, cmd, CmdMenuNode)
# also custom commands get access to the menutree. # also custom commands get access to the menutree.
cmd.menutree = menutree cmd.menutree = menutree
if self.keywords[i] and cmd.key not in (CMD_NOMATCH, CMD_NOINPUT): if self.keywords[i] and cmd.key not in (CMD_NOMATCH, CMD_NOINPUT):
@ -372,34 +405,55 @@ class MenuNode(object):
# make use the node system since there is only one level of choice. # make use the node system since there is only one level of choice.
# #
def prompt_yesno(caller, question="", yescode="", nocode="", default="N"): def prompt_yesno(caller, question="", yesfunc=None, nofunc=None, yescode="", nocode="", default="N"):
""" """
This sets up a simple yes/no questionnaire. Question will This sets up a simple yes/no questionnaire. Question will be
be asked, followed by a Y/[N] prompt where the [x] signifies asked, followed by a Y/[N] prompt where the [x] signifies the
the default selection. default selection. Note that this isn't making use of the menu
node system.
yesfunc - function callback to be called as yesfunc(self) when choosing yes (self.caller is available)
nofunc - function callback to be called as yesfunc(self) when choosing no (self.caller is available)
yescode - deprecated, executable code
nocode - "
""" """
# creating and defining commands # creating and defining commands
cmdyes = CmdMenuNode() cmdyes = CmdMenuNode(key="yes", aliases=["y"])
cmdyes.key = "yes" if yesfunc:
cmdyes.aliases = ["y"] cmdyes.yesfunc = yesfunc
# this will be executed in the context of the yes command (so def _yesfunc(self):
# self.caller will be available) self.yesfunc(self)
cmdyes.code = yescode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data" self.caller.cmdset.delete('menucmdset')
del self.caller.db._menu_data
cmdyes.callback = MethodType(_yesfunc, cmdyes, CmdMenuNode)
cmdno = CmdMenuNode() cmdno = CmdMenuNode(key="no", aliases=["n"])
cmdno.key = "no" if nofunc:
cmdno.aliases = ["n"] cmdno.nofunc = nofunc
# this will be executed in the context of the no command def _nofunc(self):
cmdno.code = nocode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data" self.nofunc(self) if self.nofunc else None
self.caller.cmdset.delete('menucmdset')
del self.caller.db._menu_data
cmdno.callback = MethodType(_nofunc, cmdno, CmdMenuNode)
errorcmd = CmdMenuNode() errorcmd = CmdMenuNode(key=CMD_NOMATCH)
errorcmd.key = CMD_NOMATCH def _errorcmd(self):
errorcmd.code = "self.caller.msg('Please choose either Yes or No.')" self.caller.msg("Please choose either Yes or No.")
errorcmd.callback = MethodType(_errorcmd, errorcmd, CmdMenuNode)
defaultcmd = CmdMenuNode() defaultcmd = CmdMenuNode(key=CMD_NOINPUT)
defaultcmd.key = CMD_NOINPUT def _defaultcmd(self):
defaultcmd.code = "self.caller.execute_cmd('%s')" % default self.caller.execute_cmd('%s' % default)
defaultcmd.callback = MethodType(_defaultcmd, defaultcmd, CmdMenuNode)
# code exec is deprecated:
if yescode:
ev.logger.log_depmsg("yesnosystem.code is deprecated. Use yesnosystem.callback.")
cmdyes.code = yescode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data"
if nocode:
ev.logger.log_depmsg("yesnosystem.code is deprecated. Use yesnosystem.callback.")
cmdno.code = nocode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data"
# creating cmdset (this will already have look/help commands) # creating cmdset (this will already have look/help commands)
yesnocmdset = MenuCmdSet() yesnocmdset = MenuCmdSet()
@ -407,6 +461,8 @@ def prompt_yesno(caller, question="", yescode="", nocode="", default="N"):
yesnocmdset.add(cmdno) yesnocmdset.add(cmdno)
yesnocmdset.add(errorcmd) yesnocmdset.add(errorcmd)
yesnocmdset.add(defaultcmd) yesnocmdset.add(defaultcmd)
yesnocmdset.add(CmdMenuLook())
yesnocmdset.add(CmdMenuHelp())
# assinging menu data flags to caller. # assinging menu data flags to caller.
caller.db._menu_data = {"help": "Please select Yes or No.", caller.db._menu_data = {"help": "Please select Yes or No.",
@ -446,7 +502,13 @@ class CmdMenuTest(Command):
def func(self): def func(self):
"Testing the menu system" "Testing the menu system"
if not self.args or self.args != "yesno": if self.args.strip() == "yesno":
"Testing the yesno question"
prompt_yesno(self.caller, question="Please answer yes or no - Are you the master of this mud or not?",
yesfunc=lambda self: self.caller.msg('{gGood for you!{n'),
nofunc=lambda self: self.caller.msg('{GNow you are just being modest ...{n'),
default="N")
else:
# testing the full menu-tree system # testing the full menu-tree system
node0 = MenuNode("START", text="Start node. Select one of the links below. Here the links are ordered in one column.", node0 = MenuNode("START", text="Start node. Select one of the links below. Here the links are ordered in one column.",
@ -458,18 +520,12 @@ class CmdMenuTest(Command):
node3 = MenuNode("node3", text="Attribute 'menutest' set on you. You can examine it (only works if you are allowed to use the examine command) or remove it. You can also quit and examine it manually.", node3 = MenuNode("node3", text="Attribute 'menutest' set on you. You can examine it (only works if you are allowed to use the examine command) or remove it. You can also quit and examine it manually.",
links=["node4", "node5", "node2", "END"], linktexts=["Remove attribute", "Examine attribute", links=["node4", "node5", "node2", "END"], linktexts=["Remove attribute", "Examine attribute",
"Back to second node", "Quit menu"], cols=2, "Back to second node", "Quit menu"], cols=2,
code="self.caller.db.menutest='Testing!'") callback=lambda self: self.caller.attributes.add("menutest",'Testing!'))
node4 = MenuNode("node4", text="Attribute 'menutest' removed again.", node4 = MenuNode("node4", text="Attribute 'menutest' removed again.",
links=["node2"], linktexts=["Back to second node."], cols=2, links=["node2"], linktexts=["Back to second node."], cols=2,
code="del self.caller.db.menutest") callback=lambda self: self.caller.attributes.remove("menutest"))
node5 = MenuNode("node5", links=["node4", "node2"], linktexts=["Remove attribute", "Back to second node."], cols=2, node5 = MenuNode("node5", links=["node4", "node2"], linktexts=["Remove attribute", "Back to second node."], cols=2,
code="self.caller.msg('%s/%s = %s' % (self.caller.key, 'menutest', self.caller.db.menutest))") callback=lambda self: self.caller.msg('%s/%s = %s' % (self.caller.key, 'menutest', self.caller.db.menutest)))
menu = MenuTree(self.caller, nodes=(node0, node1, node2, node3, node4, node5)) menu = MenuTree(self.caller, nodes=(node0, node1, node2, node3, node4, node5))
menu.start() menu.start()
else:
"Testing the yesno question"
prompt_yesno(self.caller, question="Please answer yes or no - Are you the master of this mud or not?",
yescode="self.caller.msg('{gGood for you!{n')",
nocode="self.caller.msg('{GNow you are just being modest ...{n')",
default="N")