Using Python and tkinter to build a simple output viewer for game dev build tools. See blog here: https://www.executionunit.com/blog/2012/10/26/using-python-and-tkinter-to-capture-script-output/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""main script that builds the interface and calls main.py""" | |
import subprocess | |
import sys | |
from tkinter import Tk, Text, BOTH, W, N, E, S, END, INSERT, HORIZONTAL, VERTICAL, NONE, StringVar | |
from tkinter.ttk import Frame, Button, Style, Scrollbar, Checkbutton | |
from os.path import join, dirname | |
from datetime import datetime | |
mainpath = join(dirname(__file__), "main.py") | |
#there has got to be a better way to do this in windows. | |
pythonpath = "python" | |
if sys.platform == "win32": | |
pythonpath = "c:\python27\python.exe" | |
class AssetBuilder(Frame): | |
def __init__(self, parent): | |
Frame.__init__(self, parent) | |
self.parent = parent | |
self.initUI() | |
def initUI(self): | |
self.parent.title("Asset Builder") | |
self.style = Style() | |
self.style.theme_use("default") | |
self.pack(fill=BOTH, expand=1) | |
#create a grid 5x4 in to which we will place elements. | |
self.columnconfigure(1, weight=1) | |
self.columnconfigure(2, weight=0) | |
self.columnconfigure(3, weight=0) | |
self.columnconfigure(4, weight=0) | |
self.columnconfigure(5, weight=0) | |
self.rowconfigure(1, weight=1) | |
self.rowconfigure(2, weight=0) | |
self.rowconfigure(3, weight=0) | |
#create the main text are with scrollbars | |
xscrollbar = Scrollbar(self, orient=HORIZONTAL) | |
xscrollbar.grid(row=2, column=1, columnspan=4, sticky=E + W) | |
yscrollbar = Scrollbar(self, orient=VERTICAL) | |
yscrollbar.grid(row=1, column=5, sticky=N + S) | |
self.textarea = Text(self, wrap=NONE, bd=0, | |
xscrollcommand=xscrollbar.set, | |
yscrollcommand=yscrollbar.set) | |
self.textarea.grid(row=1, column=1, columnspan=4, rowspan=1, | |
padx=0, sticky=E + W + S + N) | |
xscrollbar.config(command=self.textarea.xview) | |
yscrollbar.config(command=self.textarea.yview) | |
#create the buttons/checkboxes to go along the bottom | |
self.clearButton = Button(self, text="Clear") | |
self.clearButton.grid(row=3, column=1, padx=5, pady=5, sticky=W) | |
self.clearButton.bind("<ButtonRelease-1>", self.clearText) | |
self.verboseVar = StringVar() | |
verboseCheck = Checkbutton(self, text="Verbose", variable=self.verboseVar, | |
onvalue="-v", offvalue="") | |
verboseCheck.grid(row=3, column=2, padx=5, pady=5) | |
self.forceVar = StringVar() | |
forceCheck = Checkbutton(self, text="Force Build", variable=self.forceVar, | |
onvalue="-f", offvalue="") | |
forceCheck.grid(row=3, column=3, padx=5, pady=5) | |
self.buildbutton = Button(self, text="Build") | |
self.buildbutton.grid(row=3, column=4, padx=5, pady=5) | |
self.buildbutton.bind("<ButtonRelease-1>", self.doBuild) | |
#tags are used to colorise the text added to the text widget. | |
# see self.addTtext and self.tagsForLine | |
self.textarea.tag_config("errorstring", foreground="#CC0000") | |
self.textarea.tag_config("infostring", foreground="#008800") | |
def tagsForLine(self, line): | |
"""return a tuple of tags to be applied to the line of text 'line' | |
when being added to the text widet""" | |
l = line.lower() | |
if "error" in l or "traceback" in l: | |
return ("errorstring", ) | |
return () | |
def addText(self, str, tags=None): | |
"""Add a line of text to the textWidget. If tags is None then | |
self.tagsForLine will be used to assign tags to the line""" | |
self.textarea.insert(INSERT, str, tags or self.tagsForLine(str)) | |
self.textarea.yview(END) | |
def clearText(self, event): | |
"""Clear all the text from the text widget""" | |
self.textarea.delete("1.0", END) | |
def moveCursorToEnd(self): | |
"""move the cursor to the end of the text widget's text""" | |
self.textarea.mark_set("insert", END) | |
def doBuild(self, event): | |
"""callback from the build button""" | |
self.moveCursorToEnd() | |
self.addText("Build Started %s\n" % (str(datetime.now())), ("infostring", )) | |
cmdlist = filter(lambda x: x if x else None, | |
[pythonpath, mainpath, self.verboseVar.get(), self.forceVar.get()]) | |
self.addText(" ".join(cmdlist) + "\n", ("infostring", )) | |
proc = subprocess.Popen(cmdlist, | |
stdout=subprocess.PIPE, | |
stderr=subprocess.STDOUT, | |
universal_newlines=True) | |
while True: | |
line = proc.stdout.readline() | |
if not line: | |
break | |
self.addText(line) | |
#this triggers an update of the text area, otherwise it doesn't update | |
self.textarea.update_idletasks() | |
self.addText("Build Finished %s\n" % (str(datetime.now())), ("infostring", )) | |
self.addText("*" * 80 + "\n", ("infostring", )) | |
def main(): | |
root = Tk() | |
root.geometry("650x400+300+300") | |
AssetBuilder(root) | |
root.mainloop() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment