c7391.htm
来自「GUI Programming with Python」· HTM 代码 · 共 962 行 · 第 1/2 页
HTM
962 行
<HTML><HEAD><TITLE>Drawing on Painters and Canvases</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.72"><LINKREL="HOME"TITLE="GUI Programming with Python: QT Edition"HREF="book1.htm"><LINKREL="UP"TITLE="Creating real applications with PyQt"HREF="p4627.htm"><LINKREL="PREVIOUS"TITLE="Conclusion"HREF="x7388.htm"><LINKREL="NEXT"TITLE="QCanvas"HREF="x7601.htm"></HEAD><BODYCLASS="CHAPTER"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#0000FF"VLINK="#840084"ALINK="#0000FF"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">GUI Programming with Python: QT Edition</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="x7388.htm"ACCESSKEY="P">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="x7601.htm"ACCESSKEY="N">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="CH16">Chapter 21. Drawing on Painters and Canvases</A></H1><DIVCLASS="TOC"><DL><DT><B>Table of Contents</B></DT><DT><AHREF="c7391.htm#AEN7412">Working with painters and paint devices</A></DT><DT><AHREF="x7601.htm">QCanvas</A></DT><DT><AHREF="x7875.htm">Conclusion</A></DT></DL></DIV><P>Constructing windows out of predefined widgets is all very nice, but the real exciting stuff occurs when you have to leave the beaten track and push the pixels around yourself. This happens when you want to create diagram editors, drawing applications, games (especially games!), complex page layout engines (like an html renderer), charts, and plots.</P><P>Python and PyQt form a good basis for this kind of work, because Qt already has two very powerful and optimized drawing engines: the <TTCLASS="CLASSNAME">QCanvas</TT> class and the <TTCLASS="CLASSNAME">QPainter</TT>. <TTCLASS="CLASSNAME">QCanvas</TT> is extremely useful if your drawing can be composed from separate elements, and if you want to be able to track events that occur on those individual elements, such as mouse clicks.</P><P><TTCLASS="CLASSNAME">QPainter</TT> offers finer control of what you put on screen, at the cost of losing control over the individual elements that make up the drawing. <TTCLASS="CLASSNAME">QPainter</TT> is more suited to drawing charts, bit-mapped drawings and plots. If you want real plotting power, you should investigate PyQwt, which is introduced in <AHREF="a8743.htm">Appendix B</A></P><P>Both <TTCLASS="CLASSNAME">QCanvas</TT> and <TTCLASS="CLASSNAME">QPainter</TT> are very powerful. In fact, they are even used in assembling software used to create animated films. Animation means a lot of intensive work for your computer, though, and Python can not always cope—even on the most modern machines. In such cases, it is quite easy to replace the Python class with a Qt-based C++ object (you won't have to translate your whole Python application). See <AHREF="a8834.htm">Appendix C</A> for information on the wrapping of new C++ objects with sip.</P><DIVCLASS="SECT1"><H1CLASS="SECT1"><ANAME="AEN7412">Working with painters and paint devices</A></H1><P>In <AHREF="c2591.htm">Chapter 10</A> we introduced <TTCLASS="CLASSNAME">QPainter</TT>, for creating our little scribbling example application, <TTCLASS="FILENAME">event1.py</TT>. In this section we will take a closer look at the organization and use of the PyQt painting mechanism.</P><P>Painting in PyQt involves the cooperation of two classes: a <TTCLASS="CLASSNAME">QPainter</TT> and a <TTCLASS="CLASSNAME">QPaintDevice</TT>. The first is a very efficient abstraction of various drawing operations. It provides the brushes and the letterpress, so to speak. The second provides the ‘paper' on which to draw.</P><P>There are four subclasses of <TTCLASS="CLASSNAME">QPaintDevice</TT>: <TTCLASS="CLASSNAME">QWidget</TT>, <TTCLASS="CLASSNAME">QPicture</TT>, <TTCLASS="CLASSNAME">QPixMap</TT> and <TTCLASS="CLASSNAME">QPrinter</TT>.</P><P>You use hand-coded painting with a <TTCLASS="CLASSNAME">QPainter</TT> object on a <TTCLASS="CLASSNAME">QWidget</TT> to determine the look of a widget. The place for the painting code is in re-implementations of the <TTCLASS="FUNCTION">paintEvent()</TT> function.</P><P><TTCLASS="CLASSNAME">QPicture</TT> is a kind of event recorder: it records every <TTCLASS="CLASSNAME">QPainter</TT> action, and can replay them. You can also save those actions to a platform independent file. This is useful if you want to implement rolling charts with a limited replay functionality (although <SPAN><ICLASS="EMPHASIS">I</I></SPAN> would prefer to save the underlying data and reconstruct the chart every time). You cannot alter anything in the sequence of events once it is recorded. Starting with Qt 3, <TTCLASS="CLASSNAME">QPicture</TT> has become quite powerful, with the ability to load and save industry standard <TTCLASS="FILENAME">.svg</TT> files - the scalable vector graphics format.</P><P>Painting on a <TTCLASS="CLASSNAME">QPixMap</TT> is extraordinarily useful. Painting is always a bit slow, especially if it is done line by line, dot by dot, and character by character. This can result in visible lag or flickering if you paint directly on an exposed <TTCLASS="CLASSNAME">QWidget</TT>. By first painting the complete drawing on a <TTCLASS="CLASSNAME">QPixMap</TT> object, and then using the <TTCLASS="FUNCTION">bitBlt()</TT> function to move the picture in one swoop the the widget, you will avoid this flickering. <TTCLASS="CLASSNAME">bitBlt()</TT> really <SPAN><ICLASS="EMPHASIS">is</I></SPAN> fast.</P><P>Finally, being able to paint on a <TTCLASS="CLASSNAME">QPrinter</TT> object means that anything you can draw on-screen can also be printed. However, printing is still quite a difficult subject — even if PyQt can generate your PostScript for you, you still have to layout everything yourself. You cannot, for instance, send the contents of a <TTCLASS="CLASSNAME">QSimpleRichText</TT> widget to a printer just like that... We'll discuss the basics of printing in <AHREF="c8100.htm">Chapter 24</A>.</P><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="CH16PAINTINGEXAMPLE">A painting example</A></H2><P>There is little we can do using <TTCLASS="CLASSNAME">QPainter</TT> and <TTCLASS="CLASSNAME">QPaintDevices</TT> in our <SPANCLASS="APPLICATION">Kalam</SPAN> project — but after long and hard thinking I thought a rolling chart that counts how many characters the user types per minute might be a nice, although completely useless (and possibly frustrating) except for <SPAN><ICLASS="EMPHASIS">real</I></SPAN> productivity freaks.</P><DIVCLASS="EXAMPLE"><ANAME="AEN7480"></A><P><B>Example 21-1. typometer.py - A silly type-o-meter that keeps a running count of how many characters are added to a certain document and shows a chart of the typerate...</B></P><PRECLASS="PROGRAMLISTING">"""typometer.pyA silly type-o-meter that keeps a running count of how many characters thereare in a certain document and shows a chart of the count..."""import sys, whrandomfrom qt import *FIVE_SECONDS = 1000 * 5 # 5 seconds in milli-secondsAVERAGE_TYPESPEED = 125 # kind of calibrationBARWIDTH = 3TRUE=1FALSE=0 </PRE></DIV><P>No surprises here—just some declarations. I like to work with names instead of magic numbers, and to conform to practice in other programming languages, those names are in all-caps, even though they are not constants.</P><PRECLASS="PROGRAMLISTING">class TypoGraph(QPixmap): """ TypoGraph is a subclass of QPixmap and draws a small graph of the current wordcount of a text. """ def __init__(self, count, w, h, *args): apply(QPixmap.__init__, (self, w, h) + args) self.count = count self.maxCount = AVERAGE_TYPESPEED if count != 0: self.scale = float(h) / float(count) else: self.scale = float(h) / float(AVERAGE_TYPESPEED) self.col = 0 self.fill(QColor("white")) self.drawGrid() </PRE><P>The general design of this chart drawing code consists of two parties: a specialized pixmap, descended from <TTCLASS="CLASSNAME">QPixmap</TT>, that will draw the chart and keep track of scrolling, and a widget that show the chart and can be used everywhere where you might want to use a widget.</P><P>In the constructor of <TTCLASS="CLASSNAME">TypoGraph</TT>, the specialized <TTCLASS="CLASSNAME">QPixMap</TT>, certain initial variables are set. One point of attention is scaling. The chart will have a certain fixed vertical size. It is quite possible that the plotted values won't fit into the available pixels.</P><P>This means that we have to scale the values to fit the pixels of the chart. This is done by arbitrarily deciding upon a maximum value, and dividing the height of the chart by that value. Any value greater than the maximum will go off the chart, but if you can type more than 125 characters in five seconds, you deserve to fly off the chart!</P><P>Because the scaling can be smaller than one but greater than zero, we need to use <SPAN><ICLASS="EMPHASIS">float</I></SPAN> numbers for our scale. Floats are notoriously slow, but believe me, your computer can handle more floats than you can throw at it per second, so you won't feel the penalty for not using integers.</P><P>Finally, we fill the pixmap with a background color (white in this case) and draw a nice grid:</P><PRECLASS="PROGRAMLISTING"> def drawGrid(self): p = QPainter(self) p.setBackgroundColor(QColor("white")) h = self.height() w = self.width() for i in range(1, h, h/5): p.setPen(QColor("lightgray")) p.drawLine(0, i, w, i) </PRE><P>This is the first encounter with <TTCLASS="CLASSNAME">QPainter</TT>. The basic procedure for working with painter objects is very simple: you create a painter for the right paintdevice. Here the paintdevice is <TTCLASS="VARNAME">self</TT> — our specialized <TTCLASS="CLASSNAME">QPixMap</TT>. After having created the <TTCLASS="CLASSNAME">QPainter</TT> you can mess about drawing lines, setting colors or throwing more complex shapes on the paper. Here, we draw four lines at equal distances using a
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?