This is a blog that I will be maintaining to record my adventure writing a combat log analyzer (mainly to calculate damage-per-second and related figures) for The Lord of the Rings Online. The analyzer is being developed in Python 2.6.2 for Windows and makes use of the Tkinter library.
I ditched the threading and used .after to do the readings and it doesn't crash anymore!
Lesson learned: Listen to Lerch and Stou. Avoid threading if it is practical to do so!
Anyway, now I'm gonna write up the calculations that are done to update the analysis panel, and then write up more regexps for each kind of logline. Right now I just have damage dealt and received - I have to get it to recognize healing, partial damage dealt/received, and kills.
The calculations for outgoing damage, um... sort of work... except that it keeps re-adding them for some reason and gleefully adding up into oblivion. Har har.
So I ran into a problem with my code that was causing crashes that gave no error messages. This was very frustrating because I had no idea where to start actually looking up info on what might be causing the crash.
Here's what was happening: My 'logbox,' which is where the log text is displayed as it gets processed, seemed to be causing crashes when particularly large log files (10k+) were opened. I did not actually test this much on medium-sized logfiles, but kept using the 10k file in my tests because it was really good at producing the crash. This crash sometimes happened and sometimes didn't happen.
The code that actually processes the log lines is executed in a separate thread, which then tells the GUI to display the line after it has been processed.
A couple days passed without me doing much work - I was discouraged and worried that tkinter may just have limitations that won't allow logfiles that big to work with the program.
I randomly did a search on 'tkinter limitations' and got this page, which had a few interesting points. But the jackpot was in section 10.7.1...
"If you do use threads in Tkinter programs, however, only the main thread (the one that built the GUI and started the mainloop) can make GUI calls. Things like the update method described in the preceding section cannot be called from spawned threads in a GUI programthey'll likely trigger very strange program crashes."
Time to restructure part of my app... Again :P
Remember, kids! thread.start() and thread.run() is the difference between 0% and 100% CPU usage!
I've just entered a realm of understanding about OOP that I had never conceived of before. I just did some object recursion type things that I didn't even think were possible! But now that I've done them and demonstrated them to myself, I have to wonder how I ever did any useful coding without this functionality.
I used to think objects were really restrictive because methods inside of one object, in my viewpoint, weren't able to do modifications to another object - especially not the 'calling' object. Like if I have a GUI class, and then I have a Settings class. The GUI class puts the Settings class in a variable in itself, and calls some methods in it. Well, things in the Settings class may necessitate changing things in the GUI class. I always thought that this required doing returns of codes and having the GUI 'listen' for a change to be made... I knew I was missing something, but I wasn't sure what. It felt like too much work compared to the ease and convenience that OOP is always touted to be.
This is the way I thought about OOP for years. Tonight, I was messing around with some rewritten code for the app, and I wanted to use the Tkinter IntVar, StringVar, etc. variable classes with the objects I made that held data about sessions, subsessions and lines in a log. But I also wanted this Session class to be separate from the GUI class. Therefore, the Session class didn't come with a Tk() object to get stuff it needs from, so it can't use the IntVar by itself. I wanted so badly to figure out how to make the Session class aware of the GUI class - up till this point, the GUI class was aware of the Session class, but not vice versa.
Then I realized I could just pass the GUI object to the Session class, and put that reference into a variable in the Session class. I took it a step further when I made the classes that Session would use to create Subsessions and LogLines, and kept passing the GUI object down to each class's __init__ method.
Not only was I able to make Session, Subsession and LogLine aware of GUI, but I was able to, from within LogLine, make changes to the GUI object! Just to make sure that it was making changes to the original object and not a copy of the object, I had the original GUI object print the value of an attribute that LogLine had changed. It worked!
If programming is a roleplaying game, I've just leveled up about 5 times over...
I think it's time for a significant restructuring of my program - there are some previously-believed limitations that don't actually exist, and those were the only things keeping me from doing it the way I originally wanted to. Something about passing arguments to a callback in an .after method or something.
Also, the grid code is a horrible, horrible mess and I have to figure out how to clean it up.
Right now I have a bunch of obsolete file operations in fileops.py - at one time I used all of the functions, but I abandoned the ideas that required them, and now I only use one of them. And it's a small one. I might as well just put it in the gui.py file instead, since that's where the function is used. So that gets rid of an entire file. But I'm going to add more!
- init.pyw - gets the basics initialized so they can be hooked together
- gui.py - controls the GUI and its callbacks, as well as some minor file operations that the GUI needs
EDIT: Instead of a bunch of different files for small classes, I just decided to make a 'classes.py' file. Not sure how many more files I might end up with - depends on what happens with the threads and line data-grabber...