Skip to content
Browse files

New post--TKO

  • Loading branch information...
1 parent fb530b0 commit 370450abff5b7c0ed03a505bb27360480a22a320 @thunderchao committed Jul 13, 2012
Showing with 193 additions and 0 deletions.
  1. +193 −0 _posts/2012-07-13-tko.md
  2. BIN assets/img/inits1.png
View
193 _posts/2012-07-13-tko.md
@@ -0,0 +1,193 @@
+---
+layout: post
+title: "TKO"
+tagline: "technically knocking out some Tcl/Tk code"
+description: "Sorting, moving, and ordering encounters--and seeing it happen"
+category: learning
+tags: [python, roleplaying]
+---
+{% include JB/setup %}
+
+### Initialize!
+
+I had solved the riddle of the dice roll, which was neat, but I have a whole
+bag of dice, so although it was nice, those first few programs were more
+for learning's sake than anything. It was time to build something actually
+useful.
+
+In a roleplaying session, you'll typically end up in an encounter or two,
+where the players run into some bad guys and a battle ensues. Combat is turn-based
+though, so you must first decide the order of the turns. In 4e, every creature
+(including the players) have an initiative bonus which gets added to a d20
+roll. A higher value means you get to go sooner.
+
+Thinking design-wise, I figured I would need the following:
+1. Input for a creature's name
+2. Input for a creature's initiative bonus
+3. A call to a diceroll function that would add the bonus
+4. A list of all of the values
+5. Functions to sort and delete an individual score
+
+### Gooey for the GUI
+
+It didn't take me long to decide that rather than just have to deal with a
+command prompt for the Initiative Tracker, I'd rather have a nice, windowed,
+visual representation of what was going on. Because I use py3k, things like
+pywin aren't available, so I had to use the `tkinter` module.
+
+I had never used `tkinter` before, nor any version of any kind of Tcl programming.
+So I hit up the google, and I found all kinds of nice-looking documentation
+with plenty of examples (which, in my mid, if you are going to do docs right,
+you best load them up with examples!).
+
+Getting a `tk` window to appear is actually prety easy. Import the module
+then throw in one line of code: `main_window = tkinter.Tk()`
+
+I like easy like that.
+
+In fact, setting up the `tk` window was in general pretty easy, once I found
+all the parts I needed and the proper syntax. I decided to use the `grid` setup,
+rather than the `pack` setup, because grids are easy and you don't have to
+plot pixels and do crazy stuff like that. After getting my rows and columns
+all counted and correct, I had the following layout:
+
+{% highlight python %}
+main_window = tkinter.Tk()
+main_window.title("Initiative Tracker")
+frame = tkinter.Frame(main_window, relief=RIDGE, borderwidth=2)
+frame.pack(fill=BOTH, expand=1)
+
+toplabel = tkinter.Label(frame, text="Initiative Tracker")
+toplabel.grid(column=0, row=0, columnspan=4, pady=5)
+
+l =[]
+listbox = tkinter.Listbox(frame, selectmode=BROWSE, height=6)
+for item in l:
+ listbox.insert(END, item)
+listbox.grid(column=0, row=1, rowspan=3)
+
+yscroll = tkinter.Scrollbar(frame, command=listbox.yview, orient=VERTICAL)
+yscroll.grid(column=1, row=1, rowspan=3, sticky=(N,S,W))
+listbox.configure(yscrollcommand=yscroll.set)
+
+addentry = tkinter.Entry(frame, width=20)
+addentry.insert(0, '')
+addentry.grid(column=2, row=1, columnspan=2, padx=5, pady=5)
+
+addbutton = tkinter.Button(frame, text="add", command=add_item)
+addbutton.grid(column=2, row=2)
+
+rollbutton = tkinter.Button(frame, text='roll for selected', command=roll_it)
+rollbutton.grid(column=3, row=2)
+
+sortbutton = tkinter.Button(frame, text='sort', command=sort_list)
+sortbutton.grid(column=2, row=3)
+
+delbutton = tkinter.Button(frame, text="delete selected", command=delete_item)
+delbutton.grid(column=3, row=3)
+
+botlabel = tkinter.Label(frame, text="© Nathaniel Ray Pickett")
+botlabel.grid(column=0, row=4, columnspan=2, padx=5, sticky=W)
+
+quitbutton = tkinter.Button(frame, text="Exit", command=main_window.destroy)
+quitbutton.grid(column=2, row=4, columnspan=2, pady=5)
+
+main_window.mainloop()
+{% endhighlight %}
+
+That ends up looking like this when you run the program:
+
+![diceroll!](/assets/img/inits1.png)
+
+### Function Junction
+
+Design? Check. But without any code to run, all the buttons are boring and
+dumb. I've got 5 buttons, so I needed 5 functions: `delete`, `add`, `sort`, `roll`,
+and `exit` (except exit doesn't count because the `tk` code can do it built-in).
+
+Three of them were pretty easy, getting data from inputs or lists, then sending
+that info to the right places:
+
+{% highlight python %}
+def delete_item():
+ try:
+ index = listbox.curselection()[0]
+ listbox.delete(index)
+ except IndexError:
+ pass
+
+def add_item():
+ listbox.insert(END, addentry.get())
+ addentry.delete(0, END)
+
+def sort_list():
+ temp_list = list(listbox.get(0, END))
+ temp_list.sort(key=lambda thing: thing[0])
+ listbox.delete(0, END)
+ for item in temp_list:
+ listbox.insert(END, item)
+{% endhighlight %}
+
+I thought `roll` would also be pretty easy, but then I remembered I still
+needed to add an initiative bonus, so I had to do some more googling to find
+out how to make a popup window asking for the bonus. The answer: I had to
+write the thing that I hate writing the most because I don't understand how
+it works very well--a `Class`.
+
+This is the code for `roll` and its attendant class, which has to go first
+because it has to exist before it can be called:
+
+{% highlight python %}
+class AskBox:
+ def __init__(self, parent):
+ top = self.top = tkinter.Toplevel(parent)
+ top.title('Add bonus')
+
+ self.l = tkinter.Label(top, text='Add initiative bonus')
+ self.l.grid(column=0, row=0, columnspan=2)
+
+ self.e = tkinter.Entry(top, width=10)
+ self.e.insert(0, '0')
+ self.e.grid(column=0, row=1, padx=3, pady=5)
+
+ b = tkinter.Button(top, text="ok", command=self.ok)
+ b.grid(column=1, row=1, padx=3, pady=5)
+
+ def ok(self):
+ self.result = self.e.get()
+ self.top.destroy()
+
+def roll_it():
+ try:
+ index = listbox.curselection()[0]
+ sel = listbox.get(index)
+
+ main_window.update()
+ ask = AskBox(main_window)
+ main_window.wait_window(ask.top)
+
+ rand = random.randint(1,20) + int(ask.result)
+ this = str(rand).rjust(2, '0'), sel
+ listbox.delete(index)
+ except IndexError:
+ index = END
+ listbox.insert(index, this)
+{% endhighlight %}
+
+Basically, you type a name in, click 'add', it goes to the box. You click
+on its name there, click 'roll', type in the Initiative Bonus, click 'ok,'
+and then the value in the list is updated reflecting the value of the roll.
+
+Kind of clunky, but hey, it worked.
+
+Pretty sweet, right? For a first time, yes. But next time I reveal 1) how
+I made it more awesome by looking more awesome and 2) how I made it more
+awesome* by being a windows executable.
+
+* _used liberally here_
+
+### Give it a Spin
+
+You can grab/fork this script from [its github page](https://github.com/thunderchao/roleplaying/blob/master/inits/initiatives.py),
+and if you have any suggestions/comments (and you don't want to fork it),
+tweet me up [@thunderchao](https://twitter.com/intent/tweet?screen_name=thunderchao)
View
BIN assets/img/inits1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 370450a

Please sign in to comment.
Something went wrong with that request. Please try again.