问题
I'm trying to make a text adventure with tkinter and I'm slowly getting something together. I'm trying to display commands as they come from room to room but even though the buttons appear, nothing happens when I press them.
game.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import world
from player import Player
from ui import *
def main():
gui = Window(root())
while True:
gui.mainloop()
else:
pass
if __name__ == '__main__':
main()
ui.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import ttk
import world, tiles, action
from player import Player
class Window(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master=master)
self.master = master
self.player = Player()
self.init_ui()
def init_ui(self):
self.master.title("****")
self.tabs = Tabs(self.master)
world.load_tiles()
self.world = world.tile_exists(self.player.location_x, self.player.location_y)
self.update_main()
def update_main(self):
self.world.scene_init()
self.world.modify_player(self.player)
self.tabs.update_tab(self.world, self.player)
def _label(self, master, text, side=None, anchor=None):
new_label = tk.Label(master, text=text)
new_label.pack(side=side, anchor=anchor)
def _button(self, master, text, command, side=None, anchor=None):
new_button = tk.Button(master, text=text, command=command)
new_button.pack(side=side, anchor=anchor)
class Tabs(Window):
def __init__(self, master):
self.master = master
self.nb = ttk.Notebook(self.master)
nb_1 = ttk.Frame(self.nb)
self.frame_1 = tk.Frame(nb_1, bg='red', bd=2, relief=tk.SUNKEN, padx=5, pady=5)
self.frame_1.pack(expand=1, fill='both', side=tk.LEFT)
self.nb.add(nb_1, text='Game')
self.nb.pack(expand=1, fill='both', side=tk.LEFT)
def update_tab(self, world, player):
avaliable_actions = world.avaliable_actions()
self._label(self.frame_1, world.display_text(), side=tk.LEFT, anchor=tk.N)
for action in avaliable_actions:
self._button(self.frame_1, text=action, command=player.do_action(action, **action.kwargs), side=tk.BOTTOM, anchor=tk.E)
def root():
root = tk.Tk()
root.geometry("600x350+200+200")
return root
world.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
_world = {}
def tile_exists(x, y):
"""Returns the tile at the given coordinates or None if there is no tile.
:param x: the x-coordinate in the worldspace
:param y: the y-coordinate in the worldspace
:return: the tile at the given coordinates or None if there is no tile
"""
return _world.get((x, y))
def load_tiles():
with open('scenes.txt', 'r') as f:
rows = f.readlines()
x_max = len(rows[0].split('\t'))
for y in range(len(rows)):
cols = rows[y].split('\t')
for x in range(x_max):
tile_name = cols[x].replace('\n', '')
_world[(x, y)] = None if tile_name == '' else getattr(__import__('tiles'),
tile_name)(x, y)
return _world
tiles.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import world, action
from player import Player
class MapTile():
def __init__(self, x, y):
self.x = x
self.y = y
def display_text(self):
pass
# raise NotImplementedError()
def modify_player(self, the_player):
raise NotImplementedError()
def adjacent_moves(self):
moves = []
if world.tile_exists(self.x + 1, self.y):
moves.append(action.MoveEast())
if world.tile_exists(self.x - 1, self.y):
moves.append(action.MoveWest())
if world.tile_exists(self.x, self.y - 1):
moves.append(action.MoveNorth())
if world.tile_exists(self.x, self.y + 1):
moves.append(action.MoveSouth())
return moves
def avaliable_actions(self):
'''Returns all of the default avaliable_actions in a room'''
moves = self.adjacent_moves()
# moves.append(action.ViewInventory())
return moves
class Scene_1(MapTile):
def scene_init(self):
self.location = 'Scene_1'
self.long_desc = 'Welcome to {}, the shittiest place on earth.'.format(self.location)
self.short_desc = 'Eh, I don\'t care.'
def display_text(self):
return self.long_desc
def modify_player(self, the_player):
self.first = True
return self.display_text()
class Scene_2(MapTile):
def scene_init(self):
self.location = 'Scene_2'
self.long_desc = 'This is {}, but noone gives a damn.'.format(self.location)
self.short_desc = 'Eh, I don\'t care, really.'
def display_text(self):
return self.long_desc
def modify_player(self, the_player):
self.first = True
return self.display_text()
player.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Player():
'''Base for player'''
def __init__(self):
self.inventory = []
self.hp = 100
self.location_x, self.location_y = 1, 1
self.victory = False
def is_alive(self):
return self.hp >= 0
def do_action(self, action, **kwargs):
action_method = getattr(self, action.method.__name__)
if action_method:
action_method(**kwargs)
def print_inventory(self):
for item in self.inventory:
print(item, 'n')
def move(self, dx, dy):
self.location_x += dx
self.location_y += dy
def move_north(self):
self.move(dx=0, dy=-1)
def move_south(self):
self.move(dx=0, dy=1)
def move_east(self):
self.move(dx=1, dy=0)
def move_west(self):
self.move(dx=-1, dy=0)
action.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from player import Player
class Action():
def __init__(self, method, name, **kwargs):
"""Creates a new action
:param method: the function object to execute
:param name: the name of the action
:param ends_turn: True if the player is expected to move after this action else False
:param hotkey: The keyboard key the player should use to initiate this action
"""
self.method = method
self.name = name
self.kwargs = kwargs
def __str__(self):
return "{}".format(self.name)
class MoveNorth(Action):
def __init__(self):
super().__init__(method=Player.move_north, name='north')
class MoveSouth(Action):
def __init__(self):
super().__init__(method=Player.move_south, name='south')
class MoveEast(Action):
def __init__(self):
super().__init__(method=Player.move_east, name='east')
class MoveWest(Action):
def __init__(self):
super().__init__(method=Player.move_west, name='west')
class ViewInventory(Action):
"""Prints the player's inventory"""
def __init__(self):
super().__init__(method=Player.print_inventory, name='View inventory', hotkey='i')
class Attack(Action):
def __init__(self, enemy):
super().__init__(method=Player.attack, name="Attack", hotkey='a', enemy=enemy)
class Flee(Action):
def __init__(self, tile):
super().__init__(method=Player.flee, name="Flee", hotkey='f', tile=tile)
回答1:
command
expect function name without ()
and arguments.
Mistake:
command=player.do_action(action, **action.kwargs)
This way you assign to command
value returned by player.do_action()
but this functions returns None
You have to use lambda
function
command=lambda:player.do_action(action, **action.kwargs)
but maybe you will need also arguments in lambda
because you create this in for
loop.
command=lambda act=action, kws=action.kwargs : player.do_action(act, **kws)
来源:https://stackoverflow.com/questions/35078644/button-commands-in-tkinter