📄 index.lxp@lxpwrap=x7601_252ehtm.htm
字号:
<table border="0" cellspacing="0" cellpadding="3" width="100%"><tr><td> <div align="center" id="bldcontent"> <a href="../default.htm"><img src="../images/opendocs.png" width="63" height="76" border="0"></a> <br> <div class="symbol">Your OpenSource Publisher™</div> </div> </td></tr></table> <div align="center" class="author"> <a href="../products.lxp">Products</a> | <a href="../wheretobuy.lxp">Where to buy</a> | <a href="../bookstore.lxp">Retailers</a> | <a href="../faq.lxp">FAQ</a> | <a href="../writeforus.lxp">Write for Us.</a> | <a href="#contact">Contact Us.</a> </div> <table border="0" cellspacing="3" cellpadding="0" width="100%"><tr><td width="100%"> <div class="content"> <table border="0" cellspacing="2" cellpadding="0" width="100%"><tr><td width="100%"> <div align="center"><H4 CLASS="AUTHOR"><A NAME="AEN5">Boudewijn Rempt</A><br><a href="../../https@secure.linuxports.com/opendocs/default.htm"><img src=odpyqt125.png></a><br>ISBN: 0-97003300-4-4<br><a href="../../https@secure.linuxports.com/opendocs/default.htm">Available from bookstores everywhere or you can order it here.</a><p>You can download the source files for the book <a href="pyqtsrc.tgz">(code / eps) here.</a><hr></div> <HTML><HEAD><TITLE>QCanvas</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="Drawing on Painters and Canvases"HREF="c7391.htm"><LINKREL="PREVIOUS"TITLE="Drawing on Painters and Canvases"HREF="c7391.htm"><LINKREL="NEXT"TITLE="Conclusion"HREF="x7875.htm"></HEAD><BODYCLASS="SECT1"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"><A accesskey="P" href="index.lxp@lxpwrap=c7391_252ehtm.htm">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">Chapter 21. Drawing on Painters and Canvases</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><A accesskey="N" href="index.lxp@lxpwrap=x7875_252ehtm.htm">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1">QCanvas</A></H1><P>The other way of pushing pixels on the screen is using the <TTCLASS="CLASSNAME">QCanvas</TT> class. This is rather more complicated than simply painting what you want, but offers the unique capability of accessing the individual elements of the composition. Not only that, but you can also determine whether elements overlap each other, set them moving across the canvas at a predefined rate, and show and hide them at will.</P><P>In working with <TTCLASS="CLASSNAME">QCanvas</TT>, three classes play an essential role: the <TTCLASS="CLASSNAME">QCanvas</TT> itself, which is a receptacle for <TTCLASS="CLASSNAME">QCanvasItem</TT> objects — or rather, their descendants, and one or more <TTCLASS="CLASSNAME">QCanvasView</TT> widgets are used to show the canvas and its contents on screen.</P><DIVCLASS="MEDIAOBJECT"><P><DIVCLASS="CAPTION"><P>the relation between QCanvas, QCanvasItems and QCanvasView</P></DIV></P></DIV><P>The class <TTCLASS="CLASSNAME">QCanvasItem</TT> is rather special: you cannot instantiate objects from it, nor can you directly subclass it. You <SPAN><ICLASS="EMPHASIS">can</I></SPAN> instantiate and subclass the subclasses of <TTCLASS="CLASSNAME">QCanvasItem</TT>: <TTCLASS="CLASSNAME">QCanvasPolygonalItem</TT>, <TTCLASS="CLASSNAME">QCanvasSprite</TT> and <TTCLASS="CLASSNAME">QCanvasText</TT>.</P><P>Even <TTCLASS="CLASSNAME">QCanvasPolygonalItem</TT> itself is not terribly useful: the derived classes <TTCLASS="CLASSNAME">QCanvasEllipse</TT>, <TTCLASS="CLASSNAME">QCanvasLine</TT>, <TTCLASS="CLASSNAME">QCanvasPolygon</TT> and <TTCLASS="CLASSNAME">QCanvasRectangle</TT> can be used to draw ellipses, lines, polygons and rectangles on the canvas. Interestingly, these items can have a non-square bounding box.</P><P>This means that two circles won't touch if they overlap the box that contains them: only if the circles themselves touch. This is quite special, and if you create new derivations of these classes yourself, you should take care to carefully calculate the area your object occupies.</P><DIVCLASS="MEDIAOBJECT"><P><DIVCLASS="CAPTION"><P>Overlapping and non-overlapping circles.</P></DIV></P></DIV><P>A <TTCLASS="CLASSNAME">QCanvasSprite</TT> should be familiar to anyone who has ever played with an 8-bit home computer. A <TTCLASS="CLASSNAME">QCanvasSprite</TT> is an animated pixmap, and can move (like any <TTCLASS="CLASSNAME">QCanvasItem</TT>) across the canvas under its own steam. You fill the <TTCLASS="CLASSNAME">QCanvasSprite</TT> with a <TTCLASS="CLASSNAME">QPixMapArray</TT>. This class contains a list of <TTCLASS="CLASSNAME">QPixmap</TT>s and a list of <TTCLASS="CLASSNAME">QPoint</TT>s. These define how the sprite looks and where its hot spots are. If you want to create a game using PyQt you'll probably want to use this class.</P><P>Lastly, the <TTCLASS="CLASSNAME">QCanvasText</TT> can draw a single line of text on the canvas. Let me repeat that: you can not create a whole column of text, put it in a <TTCLASS="CLASSNAME">QCanvasText</TT> object, and paste it on the canvas. This makes creating a PageMaker clone just a little bit more difficult.</P><P>Nevertheless, it is <TTCLASS="CLASSNAME">QCanvasText</TT> which we are going to use in the next section. Another example of the use <TTCLASS="CLASSNAME">QCanvasText</TT> is the <SPANCLASS="APPLICATION">Eric</SPAN> debugger, which is part of the PyQt source distribution.</P><DIVCLASS="SECT2"><H2CLASS="SECT2">A simple Unicode character picker</A></H2><P>The goal of this example is to provide a point-and-click way of entering characters from the <SPAN><ICLASS="EMPHASIS">complete</I></SPAN> unicode range in <SPANCLASS="APPLICATION">Kalam</SPAN>. The Unicode range is divided into a few hundred scripts. What I want is a window that shows a clickable table of one of those scripts, with a combo-box that allows me to select the script I need. And when I click on a character, that character should be inserted into the current document.</P><DIVCLASS="MEDIAOBJECT"><P><DIVCLASS="CAPTION"><P>A Unicode character picker</P></DIV></P></DIV><P>The underlying data can be retrieved from the Unicode consortium website. They provide a file, <TTCLASS="FILENAME">Blocks.txt</TT>, that gives you the range each script occupies:</P><PRECLASS="SCREEN"># Start Code; End Code; Block Name0000; 007F; Basic Latin0080; 00FF; Latin-1 Supplement0100; 017F; Latin Extended-A0180; 024F; Latin Extended-B0250; 02AF; IPA Extensions02B0; 02FF; Spacing Modifier Letters0300; 036F; Combining Diacritical Marks...F900; FAFF; CJK Compatibility IdeographsFB00; FB4F; Alphabetic Presentation FormsFB50; FDFF; Arabic Presentation Forms-AFE20; FE2F; Combining Half MarksFE30; FE4F; CJK Compatibility FormsFE50; FE6F; Small Form VariantsFE70; FEFE; Arabic Presentation Forms-BFEFF; FEFF; SpecialsFF00; FFEF; Halfwidth and Fullwidth FormsFFF0; FFFD; Specials </PRE><P>This file can be used to fill a combobox with all different scripts:</P><DIVCLASS="EXAMPLE"></A><P><B>Example 21-2. charmap.py - a Unicode character selection widget</B></P><PRECLASS="PROGRAMLISTING">"""charmap.py - A unicode character selectorcopyright: (C) 2001, Boudewijn Remptemail: boud@rempt.xs4all.nl"""import string, os.pathfrom qt import *TRUE=1FALSE=0class CharsetSelector(QComboBox): def __init__(self, datadir, *args): apply(QComboBox.__init__,(self,)+args) self.charsets=[] self.connect(self, SIGNAL("activated(int)"), self.sigActivated) f=open(os.path.join(datadir,"Blocks.txt")) f.readline() # skip first line for line in f.readlines(): try: self.charsets.append((string.atoi(line[0:4],16) ,string.atoi(line[6:10],16))) self.insertItem(line[12:-1]) except: pass def sigActivated(self, index): begin, start=self.charsets[index] self.emit(PYSIGNAL("sigActivated"),(begin, start)) </PRE></DIV><P>This is simple enough: the location of <TTCLASS="FILENAME">Blocks.txt</TT> is retrieved, and each line is read. Every line represents one script, and for every line an entry is inserted into the <TTCLASS="CLASSNAME">QComboBox</TT>. In a separate list, <TTCLASS="VARNAME">self.charsets</TT>, we keep a tuple with the begin and the end of each range, converted to integers from their hexadecimal representation. Python is a great language for this kind of data massaging.</P><P>Whenever the user selects an item from the combobox, a signal is emitted, <TTCLASS="VARNAME">sigActivated</TT>, that carries the begin and endpoint of the range.</P><DIVCLASS="SECT3"><H3CLASS="SECT3">The canvas</A></H3><P>Working with <TTCLASS="CLASSNAME">QCanvas</TT> entails handling two classes: <TTCLASS="CLASSNAME">QCanvas</TT> and <TTCLASS="CLASSNAME">QCanvasView</TT>. In this section, we'll lay out the Unicode table on the <TTCLASS="CLASSNAME">QCanvas</TT>. From PyQt 3.0 onwards, the canvas classes are in a separate module: <TTCLASS="FILENAME">qtcanvas</TT>, which has to be imported separately.</P><P>You can think of a <TTCLASS="CLASSNAME">QCanvas</TT> as a virtually boundless two-dimensional paste-board, which you can fill with <TTCLASS="CLASSNAME">QCanvasItems</TT>. The main difference between a <TTCLASS="CLASSNAME">QCanvas</TT> with <TTCLASS="CLASSNAME">QCanvasItem</TT>s on it and a <TTCLASS="CLASSNAME">QWidget</TT> with a lot of sub-widgets, is that the first is a lot more efficient in terms of memory-use, and offers easy collision detection. Of course, <TTCLASS="CLASSNAME">QCanvasItem</TT>s are not widgets, so you don't have easy event handling — but you can fake it easily enough, by catching mouse presses on individual <TTCLASS="CLASSNAME">QCanvasItem</TT>s.</P><P>Here, we will create a <TTCLASS="CLASSNAME">QCanvasText</TT> for every Unicode glyph. In the <TTCLASS="CLASSNAME">QCanvasView</TT> mouse-clicks on those items will be caught.</P><PRECLASS="PROGRAMLISTING">class CharsetCanvas(QCanvas): def __init__(self, parent, font, start, end, maxW, *args): apply(QCanvas.__init__,(self, ) + args) self.parent=parent self.start=start self.end=end self.font=font self.drawTable(maxW) def drawTable(self, maxW): self.maxW=maxW self.items=[] x=0 y=0 fontMetrics=QFontMetrics(self.font) cell_width=fontMetrics.maxWidth() + 3 if self.maxW < 16 * cell_width: self.maxW = 16 * cell_width cell_height=fontMetrics.lineSpacing() for wch in range(self.start, self.end + 1): item=QCanvasText(QString(QChar(wch)),self) item.setFont(self.font) item.setX(x) item.setY(y) item.show() self.items.append(item) x=x + cell_width if x >= self.maxW: x=0 y=y+cell_height if self.parent.height() > y + cell_height: h = self.parent.height() else: h = y + cell_height self.resize(self.maxW + 20, h) self.update() def setFont(self, font): self.font=font self.drawTable(self.maxW) </PRE><P>Most of the real work is done in the <TTCLASS="FUNCTION">drawTable()</TT> method. The <TTCLASS="VARNAME">maxW</TT> parameter determines how wide the canvas will be. However, if there is not place enough for at least sixteen glyphs, the width is adjusted.</P><P>Then the <TTCLASS="CLASSNAME">QCanvasText</TT> items are created, in a plain loop, starting at the beginning of the character set and running to the end. You must give these items an initial position and size, and explicitly call <TTCLASS="FUNCTION">show()</TT> on each item. If you forget to do this, all you will see is a very empty canvas.</P><P>You will also be greeted by an equally empty canvas if you do not keep a Python reference to the items — here a list of <TTCLASS="CLASSNAME">QCanvasText</TT> items is kept in <TTCLASS="VARNAME"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -