C++ Drawing Library
Over the last couple of weeks, I’ve been building a graphics/animation library to use in the class that I’m teaching at USC. It’s now in a stable state and has the features that I think I’ll need this semester.
A lot of the motivation comes from Princeton where I taught previously: the class there, taught in Java, uses the awesome book, assignments, and libraries of Kevin Wayne and Bob Sedgewick. StdDraw.java, their library, Just Works™. You don’t need to create any objects, or initialize any canvas, or create a hook. If you want a circle, just call StdDraw.circle(x, y, r). This makes it reasonable to write code for a beginner class that students will actually read, and reasonable for students to write their own clients. So I’ve tried to emulate that.
Of course, with Java, you have the benefit of native platform support, and their library is a wrapper over the java.awt and javax.swing libraries. In my case, after a lot of false starts, the only C++ library that
- I could get working on my VM
- I could get making graphics and animation
was “Qt.” More on that is below. But I’m pretty happy with how it turned out.
Here’s a little picture showing it in action:
If you’re interested, here is the:
- Documentation: http://bits.usc.edu/cs103/draw/
- Get as zip: ftp://bits.usc.edu/cs103/draw/draw.zip
- Get as git: https://github.com/daveagp/draw
There were a few things that made life unnecessarily difficult, at least for some shlub with a Java/Python background like me.
- I have no idea if someone not using the course VM will be able to get it to run, since they’ll have to install Qt. And adding audio & .mid support is another order of magnitude difficult for the student.
- Every function call causes a pretty long chain of events, and as a result, there’s a ridiculous amount of boilerplate in draw.cpp, so every function has to be declared 4 times in slightly different ways:
- The user calls a library function.
- The library function, a static function, calls a public member function of a QObject.
- That member function “emits” a protected “signal”.
- It’s transmitted (through a cross-thread queue) to the “slot” of another object, the GUI’s “QWidget”.
- That slot actually does the work.
- You need threading in order for the student’s calls to be non-blocking, which is important for animation. But clang++ doesn’t support threads properly. So we have to use g++, which is not our course compiler, and is not as portable to OSX.
- We really would like to run some stuff (wait for the window to close) after the student’s main() function, but there’s no exposed API for this that worked. So we use the preprocessor to rename their main() method to _main(). It’s awful because you need to transform both a no-argument main() and a main(int, char**) to the same thing. This really makes me feel dirty…
// transform main() or main(int, char**) to _main(int, char**) #define main(...) vamain(__VA_ARGS__) #define vamain(...) vamainhelp(,##__VA_ARGS__, int, char**) #define vamainhelp(blank, first, second, ...) _main(first, second)
But, I would have to say that I learned a lot in the process. The library even works with gdb for debugging and stepping. It’s weird that Qt seems to try to do a lot of stuff to C++ that other languages support natively: hooks (signals/slots), reflection and multi-threading in particular.
We’ll see how it goes during the semester. Hopefully many of the undergrads working in my course are wizards enough at Qt, from their experience using it in the data structures course, that more people can configure it on whatever machine they like. My fingers are crossed!
Filed under: c++, media, programming | 3 Comments