# gamegui.py from tkinter import * from tkinter import ttk from tkinter import filedialog from tkinter import messagebox from tile import Tile from gamebase import GameBase import io class App(ttk.Frame): def __init__(self, master, gameBase): #print('Initializing...') super(App, self).__init__(master) self.gameBase = gameBase self.gameBase.outstream = io.StringIO() self.gameBase.requestInput = self.requestInput self.gameBase.onLevelLoad = self.getGraphics self.selected = (-1, -1) self.parent = master self.mapSize = (800, 450) self.hasChanged = False self.fileName = 'Untitled' self.invTuple = tuple(self.gameBase.playerInv.keys()) self.invNames = StringVar(value=self.invTuple) #self.project = Project() #self.tool = StringVar() #self.brush = StringVar() #self.history = [] #self.redos = [] self.useOnCont = None # useOn continued #self.selectedArea = (0, 0, self.project.x-1, self.project.y-1) self.parent.clipboard_clear() self.parent.clipboard_append('') self.grid() self.createWidgets() self.makeTheDisplayWork() self.bindKeys() #print('Done.') def createWidgets(self): #print('Creating Widgets...') self.initWidth, self.initHeight = 0, 0 # create menu bar self.createMenuBar() # create map self.createMapView() # create text out self.createTextOut() # create inventory list self.createInventoryView() #self.tool.set('setone') #brushes = ttk.Combobox(self, textvariable = self.brush, values = tuple(sorted(Project.tileID.keys()))) #brushes.grid(column = 0, row = 6, sticky = (E, W)) #brushes.state(['readonly']) #brushes.set('Dirt') #ttk.Separator(self, orient = VERTICAL).grid(column = 1, row = 0, rowspan = 9, sticky = (N, S)) ttk.Sizegrip(self).grid(column = 1, row = 1, sticky = (S, E)) self.columnconfigure(0, weight = 1) self.rowconfigure(0, weight = 1) #print('Done.') def createMenuBar(self): #print('Creating the menubar...') menubar = Menu(self.parent) menuFile = Menu(menubar) #menuEdit = Menu(menubar) menuHelp = Menu(menubar) menubar.add_cascade(menu = menuFile, label = 'File') menuFile.add_command(label = 'New', command = lambda: self.loadLevel('maps/apartment.xml'), accelerator = 'Ctrl+N') menuFile.add_command(label = 'Open...', command = self.fileOpen, accelerator = 'Ctrl+O') menuFile.add_command(label = 'Save', command = self.fileSave, accelerator = 'Ctrl+S') menuFile.add_command(label = 'Save As...', command = self.fileSaveAs) menuFile.add_separator() menuFile.add_command(label = 'Exit', command = self.onQuit, accelerator = 'Ctrl+Q') #menubar.add_cascade(menu = menuEdit, label = 'Edit') #menuEdit.add_command(label = 'Undo', command = self.undo, accelerator = 'Ctrl+Z') #menuEdit.add_command(label = 'Redo', command = self.redo, accelerator = 'Ctrl+Y') #menuEdit.add_separator() #menuEdit.add_command(label = 'Cut', command = self.cut, accelerator = 'Ctrl+X') #menuEdit.add_command(label = 'Copy', command = self.copy, accelerator = 'Ctrl+C') #menuEdit.add_command(label = 'Paste', command = self.paste, accelerator = 'Ctrl+V') menubar.add_cascade(menu = menuHelp, label = 'Help') menuHelp.add_command(label = 'About', command = self.about) menuHelp.add_separator() menuHelp.add_command(label = 'Read Me', command = self.readme) self.parent.configure(menu = menubar) #print('Done.') def createMapView(self): mapFrame = ttk.LabelFrame(self, text = "map") mapFrame.grid(column = 0, row = 0) self.levelDisplay = Canvas(mapFrame, width = self.mapSize[0], height = self.mapSize[1], scrollregion = '0 0 2048 2048') self.levelDisplay.grid(column = 0, row = 0) vbar = ttk.Scrollbar(mapFrame, orient = VERTICAL, command = self.levelDisplay.yview) self.levelDisplay.configure(yscrollcommand = vbar.set) vbar.grid(column = 1, row = 0, sticky = (N, S)) hbar = ttk.Scrollbar(mapFrame, orient = HORIZONTAL, command = self.levelDisplay.xview) self.levelDisplay.configure(xscrollcommand = hbar.set) hbar.grid(column = 0, row = 1, sticky = (E, W)) self.bind('', self.onResize) def createTextOut(self): textFrame = ttk.LabelFrame(self, text = "Info") textFrame.grid(column = 0, row = 1) self.infoDisplay = Text(textFrame, width = 80, height = 8, state = 'disabled') self.infoDisplay.grid(column = 0, row = 0) vbar = ttk.Scrollbar(textFrame, orient = VERTICAL, command = self.infoDisplay.yview) self.infoDisplay.configure(yscrollcommand = vbar.set) vbar.grid(column = 1, row = 0, sticky = (N, S)) self.infoCursor = 0 def createInventoryView(self): invFrame = ttk.LabelFrame(self, text = "Inventory") invFrame.grid(column = 1, row = 0) self.invDisplay = Listbox(invFrame, height = 24, listvariable = self.invNames) self.invDisplay.grid(column = 0, row = 0) vbar = ttk.Scrollbar(invFrame, orient = VERTICAL, command = self.invDisplay.yview) self.invDisplay.configure(yscrollcommand = vbar.set) vbar.grid(column = 1, row = 0, sticky = (N, S)) def getGraphics(self): self.tiles = {} for i in range(len(self.gameBase.level.floorColors)): self.tiles['e{0}'.format(i)] = Tile(self.gameBase.level.floorColors[i]) for i in range(len(self.gameBase.level.wallColors)): self.tiles['w{0}'.format(i)] = Tile(self.gameBase.level.wallColors[i]) self.tiles['player'] = Tile('clear', 'blue', 'o') for thing in self.gameBase.level.thingNames: graphic = self.gameBase.level.thingNames[thing].graphic self.tiles[thing] = Tile(graphic[0], graphic[1], graphic[2]) def makeTheDisplayWork(self): #print('Making the display work...') # level display self.context = Menu(self.levelDisplay) self.context.add_command(label = 'Load Map', command = lambda: self.loadLevel('maps/apartment.yml')) self.context.add_command(label = 'Go', command = lambda: self.go(self.selected[0], self.selected[1])) self.context.add_command(label = 'Run', command = lambda: self.go(self.selected[0], self.selected[1], True)) self.context.add_command(label = 'Look', command = lambda: self.look(self.selected[0], self.selected[1])) self.context.add_command(label = 'Use', command = lambda: self.use(self.selected[0], self.selected[1])) self.context.add_command(label = 'Take', command = lambda: self.take(self.selected[0], self.selected[1])) #self.context.add_command(label = 'Cut', command = self.cut, accelerator = 'Ctrl+X') #self.context.add_command(label = 'Goblin...') #self.context.entryconfigure('Goblin...', command = self.configureGoblin, state = DISABLED) #self.refreshDisplay() self.levelDisplay.bind('<1>', self.handleClick) self.levelDisplay.bind('', self.handleDoubleClick) #self.levelDisplay.bind('', self.handleDragEvent) #self.levelDisplay.bind('', self.handleReleaseEvent) self.levelDisplay.bind('<3>', self.handleRightClick) # inventory list self.invContext = Menu(self.invDisplay) self.invContext.add_command(label = 'Look', command = lambda: self.look(self.invTuple[self.invDisplay.curselection()[0]])) self.invContext.add_command(label = 'Use', command = lambda: self.use(self.invTuple[self.invDisplay.curselection()[0]])) self.invContext.add_command(label = 'Use on...', command = lambda: self.useOn(self.invTuple[self.invDisplay.curselection()[0]])) self.invContext.add_command(label = 'Drop', command = lambda: self.drop(self.invTuple[self.invDisplay.curselection()[0]])) self.invDisplay.bind('<>', lambda e: self.look(self.invTuple[self.invDisplay.curselection()[0]])) self.invDisplay.bind('<3>', lambda e: self.invContext.post(e.x_root, e.y_root)) #print('Done.') pass def bindKeys(self): self.parent.bind('', lambda e: self.fileNew()) self.parent.bind('', lambda e: self.fileOpen()) self.parent.bind('', lambda e: self.fileSave()) #self.parent.bind('', lambda e: self.undo()) #self.parent.bind('', lambda e: self.redo()) #self.parent.bind('', lambda e: self.cut()) #self.parent.bind('', lambda e: self.copy()) #self.parent.bind('', lambda e: self.paste()) self.parent.bind('', lambda e: self.onQuit()) def refreshDisplay(self): #print('Refreshing the display...') # refresh the map self.levelDisplay.delete('all') for y in range(self.gameBase.level.dimensions[1]): for x in range(self.gameBase.level.dimensions[0]): pos = self.gameBase.level.mapMatrix[y][x] if pos[0] == 'w': self.tiles['w{0}'.format(pos[1])].paint(self.levelDisplay, x, y) else: self.tiles['e{0}'.format(pos[1])].paint(self.levelDisplay, x, y) for name in self.gameBase.level.thingNames: thing = self.gameBase.level.getThingByName(name) self.tiles[name].paint(self.levelDisplay, thing.x, thing.y) self.tiles['player'].paint(self.levelDisplay, self.gameBase.playerx, self.gameBase.playery) # refresh the info box self.gameBase.outstream.seek(self.infoCursor) #print(self.gameBase.outstream.tell()) #print(self.gameBase.outstream.read()) self.infoDisplay['state'] = 'normal' self.infoDisplay.insert('end', self.gameBase.outstream.read()) self.infoDisplay.see('end -1 chars') self.infoDisplay['state'] = 'disabled' self.infoCursor = self.gameBase.outstream.tell() #print(self.infoCursor) #print('Done.') def handleClick(self, event): x = int(self.levelDisplay.canvasx(event.x) / 32) y = int(self.levelDisplay.canvasy(event.y) / 32) if self.useOnCont == None: if (x, y) != self.selected: self.selected = (x, y) self.look(x, y) else: self.useOn(x, y) def handleDoubleClick(self, event): x = int(self.levelDisplay.canvasx(event.x) / 32) y = int(self.levelDisplay.canvasy(event.y) / 32) thing = self.gameBase.level.getThingAtCoords(x, y) if thing != None and thing.useable: self.use(x, y) else: self.go(x, y) def handleRightClick(self, event): x = int(self.levelDisplay.canvasx(event.x) / 32) y = int(self.levelDisplay.canvasy(event.y) / 32) self.selected = (x, y) self.context.post(event.x_root, event.y_root) #def openInvContext(self, event): # self.invContext.post(event.x_root, event.y_root) def handleDragEvent(self, event): x = min(int(self.levelDisplay.canvasx(event.x) / 32), self.project.x-1) y = min(int(self.levelDisplay.canvasy(event.y) / 32), self.project.y-1) if self.tool.get() == 'select': rect = self.levelDisplay.find_withtag('selectArea') self.selectedArea = (min(x, self.firstX), min(y, self.firstY), max(x, self.firstX), max(y, self.firstY)) self.levelDisplay.coords(rect, (self.selectedArea[0]*32, self.selectedArea[1]*32, self.selectedArea[2]*32+31, self.selectedArea[3]*32+31)) def handleReleaseEvent(self, event): x = int(self.levelDisplay.canvasx(event.x) / 32) y = int(self.levelDisplay.canvasy(event.y) / 32) if self.tool.get() == 'select': rect = self.levelDisplay.find_withtag('selectArea') self.selectedArea = (min(x, self.firstX), min(y, self.firstY), max(x, self.firstX), max(y, self.firstY)) self.levelDisplay.coords(rect, (self.selectedArea[0]*32, self.selectedArea[1]*32, self.selectedArea[2]*32+31, self.selectedArea[3]*32+31)) tiles = self.project.getTiles(self.selectedArea) for y in range(len(tiles)): for x in range(len(tiles[0])): if type(tiles[y][x]) == Project.GoblinSettings: self.context.entryconfigure('Goblin...', state = NORMAL) break break else: pass def loadLevel(self, fileName): self.gameBase.loadMap([fileName]) #self.getGraphics() self.refreshDisplay() def go(self, x, y, running = False): #print("go called") #print(x, y) if running: self.gameBase.go(['-r', str(x), str(y)]) else: self.gameBase.go([str(x), str(y)]) self.gameBase.gameEventLoop() self.refreshDisplay() #inefficient, but will work for now. def look(self, x, y = 1): #print("look called") #if x == self.gameBase.playerx and y == self.gameBase.playery: # self.gameBase.look([ if isinstance(x, int): thing = self.gameBase.level.getThingAtCoords(x, y) if thing != None: self.gameBase.look([thing.name]) else: self.gameBase.look([]) else: self.gameBase.look([x]) self.gameBase.gameEventLoop() self.refreshDisplay() #inefficient, but will work for now. def use(self, x, y = 1): if isinstance(x, int): self.gameBase.use([str(x), str(y)]) else: self.gameBase.use([x]) self.gameBase.gameEventLoop() self.refreshDisplay() #inefficient, but will work for now. def useOn(self, x, y = 1): if isinstance(x, int): self.gameBase.use([self.useOnCont, 'on', str(x), str(y)]) self.useOnCont = None self.gameBase.gameEventLoop() self.refreshDisplay() else: # x is a string self.useOnCont = x print("Click on something to use {0} on.".format(x), file = self.gameBase.outstream) self.refreshDisplay() def take(self, x, y): self.gameBase.take([str(x), str(y)]) self.gameBase.gameEventLoop() self.refreshDisplay() #inefficient, but will work for now. self.invTuple = tuple(self.gameBase.playerInv.keys()) self.invNames.set(self.invTuple) def drop(self, item): self.gameBase.drop([item]) self.gameBase.gameEventLoop() self.refreshDisplay() self.invTuple = tuple(self.gameBase.playerInv.keys()) self.invNames.set(self.invTuple) def requestInput(self, prompt = ''): answer = messagebox.askyesno(message=prompt, icon='question', title='Prompt') if answer: return 'y' else: return 'n' def fileNew(self): #print('Creating a new project...') if self.askToSave(): self.newDialog = Toplevel(self.parent) self.newDialog.title('New Project') newFrame = Frame(self.newDialog) newFrame.grid() self.hei = StringVar() self.wid = StringVar() ttk.Label(newFrame, text = 'Width:').grid(column = 0, row = 0, sticky = W) ttk.Label(newFrame, text = 'Height:').grid(column = 0, row = 1, sticky = W) Spinbox(newFrame, from_ = 16, to = 255, increment = 1, textvariable = self.wid ).grid(column = 1, row = 0, sticky = W) Spinbox(newFrame, from_ = 16, to = 255, increment = 1, textvariable = self.hei ).grid(column = 1, row = 1, sticky = W) ttk.Button(newFrame, text = 'Create', command = self.__confirmNewDimensions, default = 'active').grid(column = 0, row = 2) ttk.Button(newFrame, text = 'Cancel', command = self.newDialog.destroy ).grid(column = 1, row = 2) #print('Done.') def fileOpen(self): #print('Opening a project...') if self.askToSave(): newName = filedialog.askopenfilename(defaultextension = '.dat', initialdir = 'saves') if newName != '': self.fileName = newName self.gameBase.loadGame((self.fileName,)) self.gameBase.gameEventLoop() self.refreshDisplay() self.invTuple = tuple(self.gameBase.playerInv.keys()) self.invNames.set(self.invTuple) #print('Done.') def fileSave(self): #print('Saving a project...') newName = self.fileName if self.fileName == 'Untitled': self.fileSaveAs() elif newName != '': self.fileName = newName self.gameBase.saveGame((self.fileName,)) self.hasChanged = False #print('Done.') def fileSaveAs(self): newName = filedialog.asksaveasfilename(defaultextension = '.dat', initialdir = 'saves') if newName != '': self.fileName = newName self.gameBase.saveGame((self.fileName,)) self.hasChanged = False def onQuit(self): if self.askToSave(): exit() def askToSave(self): if self.hasChanged: insecure = messagebox.askyesnocancel( message = 'Do you want to save ' + self.fileName + ' before continuing?', icon = 'warning', title = 'New File') print(type(insecure)) if insecure == None: return False elif insecure == True: self.fileSave() return True else: return True else: return True def about(self): self.newDialog = Toplevel(self.parent) self.newDialog.title('About') newFrame = Frame(self.newDialog) newFrame.grid() ttk.Label(newFrame, text = 'I Am Gnome Level Editor v.0.9.0013').grid() ttk.Button(newFrame, text = 'Okay', command = self.newDialog.destroy).grid() def readme(self): self.newDialog = Toplevel(self.parent) self.newDialog.title('About') newFrame = Frame(self.newDialog) newFrame.grid() text = Text(newFrame, width=80, height=40, wrap = 'word') text.grid(column = 0, row = 0) sbar = ttk.Scrollbar(newFrame, orient = VERTICAL, command = text.yview) sbar.grid(column = 1, row = 0, sticky = (N, S, W)) text.configure(yscrollcommand = sbar.set) text.state(['disabled']) file = open('iag_readme.txt', 'r') text.insert('1.0', file.read()) def onResize(self, event): if self.initWidth == 0 and self.initHeight == 0: self.initWidth = event.width self.initHeight = event.height else: wDelta = event.width - int(self.initWidth) hDelta = event.height - int(self.initHeight) self.levelDisplay.configure(width = self.mapSize[0] + wDelta, height = self.mapSize[1] + hDelta) # main if __name__ == '__main__': root = Tk() root.title('Game Gui (debug)') #root.geometry("1024x768") #root.resizable(FALSE, FALSE) root.option_add('*tearOff', FALSE) root.columnconfigure(0, weight = 1) root.rowconfigure(0, weight = 1) newApp = App(root, GameBase()) newApp.grid(sticky = (N, S, E, W)) root.mainloop()