Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Python (2005)

.pdf
Скачиваний:
177
Добавлен:
17.08.2013
Размер:
15.78 Mб
Скачать

Answers to Exercises

The following code shows you how to do this simple exercise. You’ve seen almost all of this code before. Of course, you import os.popen to do the work of running a command. This is done in the send method:

#!/usr/bin/env python import findgtk

import gtk import gtk.glade

class ex1gui:

def __init__(self):

self.wTree = gtk.glade.XML (“ex1.glade”, “window1”) dic={ “on_window1_destroy” : self.quit,

“on_button1_clicked” : self.send,

}

self.wTree.signal_autoconnect (dic) #setup the log window

self.logwindowview=self.wTree.get_widget(“textview1”) self.logwindow=gtk.TextBuffer(None) self.logwindowview.set_buffer(self.logwindow)

return

def send(self,obj): print “send called”

command=self.wTree.get_widget(“entry1”).get_text() print “Command: %s”%command

self.log(“Running: “+command,”black”) import os

fd=os.popen(command)

data=fd.read()

self.log(“Result: %s”%data,”red”)

return

def log(self,message,color,enter=”\n”): “””

logs a message to the log window and scrolls the window to the bottom “””

message=message+enter

buffer = self.logwindow iter = buffer.get_end_iter() #gtk versioning avoidance if color != “black”:

tag = buffer.create_tag() tag.set_property(“foreground”, color)

self.logwindow.insert_with_tags(buffer.get_end_iter(), message, tag) else:

self.logwindow.insert(iter, message) #gtk.FALSE and gtk.TRUE on older pyGTK

mark = buffer.create_mark(“end”, buffer.get_end_iter(), False) self.logwindowview.scroll_to_mark(mark,0.05,True,0.0,1.0) #print “Exited log function”

591

TEAM LinG

Appendix A

return

def quit(self,obj): import sys gtk.main_quit() sys.exit(1)

if __name__ == ‘__main__’:

#do splashscreen here maybe thegui=ex1gui()

try:

gtk.threads_init() except:

print “No threading was enabled when you compiled pyGTK!” sys.exit(1)

gtk.threads_enter() gtk.main () gtk.threads_leave()

If you click your buttons and nothing happens, you probably forgot to add the signals in the widget properties window in Glade. Or perhaps you forgot to save the glade file after you did so. If you did everything correctly, you should see the working GUI. Notice how easy it was to change colors in the text buffer.

Exercise 2 solution

Note that gui_queue.py requires timeoutsocket.py (see Appendix B). Therefore, if you haven’t downloaded that already, now’s your chance. It is hoped that you’ll end up with a result similar to what is shown in Figure A-2.

Figure A-2

The working code follows. There are only minor differences between this code and the code for exercise 1. We’ve added a thread that handles the command running. We’ve also added the code necessary to interact with gui_queue. Other than that, the code should be very familiar by now. Both of these exercises were completely written in less than an hour — the conversion from nonthreaded to threaded should take you only around 15 minutes. This is the power of pyGTK. Of course, these are trivial examples. You’ll come to truly appreciate pyGTK when you scale your applications up to the size of any major application. Time not spent laying out your GUI manually is time spent making that one killer feature!

592

TEAM LinG

Answers to Exercises

#!/usr/bin/env python import findgtk import gtk

import gtk.glade import gui_queue import threading import os import gobject

class commandRunner(threading.Thread): def __init__(self,command,gui):

threading.Thread.__init__(self) self.command=command self.gui=gui

return

def run(self): fd=os.popen(self.command) data=”A”

while data!=””: data=fd.readline()

self.gui.gui_queue_append(“log”,[data,”red”]) return

class ex2gui:

def __init__(self):

#we use the same GUI as the other program! self.wTree = gtk.glade.XML (“ex1.glade”, “window1”) dic={ “on_window1_destroy” : self.quit,

“on_button1_clicked” : self.send,

}

self.wTree.signal_autoconnect (dic) #setup the log window

self.logwindowview=self.wTree.get_widget(“textview1”) self.logwindow=gtk.TextBuffer(None) self.logwindowview.set_buffer(self.logwindow) self.gui_queue=gui_queue.gui_queue(self)

gobject.io_add_watch(self.gui_queue.listensocket,gobject.IO_IN,self.clearqueue) return

def send(self,obj): print “send called”

command=self.wTree.get_widget(“entry1”).get_text() print “Command: %s”%command

self.log(“Running: “+command,”black”) cR=commandRunner(command,self) cR.start() #start a new thread! return

def log(self,message,color,enter=”\n”): “””

logs a message to the log window and scrolls the window to the bottom “””

message=message+enter

593

TEAM LinG

Appendix A

buffer = self.logwindow iter = buffer.get_end_iter() #gtk versioning avoidance if color != “black”:

tag = buffer.create_tag() tag.set_property(“foreground”, color)

self.logwindow.insert_with_tags(buffer.get_end_iter(), message, tag) else:

self.logwindow.insert(iter, message) #gtk.FALSE and gtk.TRUE on older pyGTK

mark = buffer.create_mark(“end”, buffer.get_end_iter(), False) self.logwindowview.scroll_to_mark(mark,0.05,True,0.0,1.0) #print “Exited log function”

return

def quit(self,obj): import sys gtk.main_quit() sys.exit(1)

def clearqueue(self,source,condition): “””Our callback for gui events”””

self.gui_queue.clearqueue(source, condition) return 1

def handle_gui_queue(self,command, args): “””

Callback the gui_queue uses whenever it receives a command for us. command is a string

args is a list of arguments for the command “””

gtk.threads_enter() #print “handle_gui_queue”

if command==”log”: text=args[0] color=args[1] self.log(text,color=color)

else:

print “Did not recognize action to take %s: %s”%(command,args) #print “Done handling gui queue”

gtk.threads_leave() return 1

def gui_queue_append(self,command,args): self.gui_queue.append(command,args) return 1

if __name__ == ‘__main__’:

#do splashscreen here maybe thegui=ex2gui()

try:

594

TEAM LinG

Answers to Exercises

gtk.threads_init() except:

print “No threading was enabled when you compiled pyGTK!” sys.exit(1)

gtk.threads_enter() gtk.main () gtk.threads_leave()

Chapter 14

Exercise 1 solution

The choice is c, of course. Just joking. The most appropriate choice is b, with the keys being the person’s name and the values holding the pizza ingredients, perhaps using commas to separate the different ingredients.

Exercise 2 solution

You can use any alias you like. Here is one example:

select e.firstname, e.lastname, d.name from employee e, department d

where e.dept = d.departmentid order by e.lastname desc

Or, in a full example script:

import gadfly.dbapi20

connection = gadfly.dbapi20.connect(‘pydb’, ‘db’)

cursor = connection.cursor()

cursor.execute(“””

select e.firstname, e.lastname, d.name from employee e, department d

where e.dept = d.departmentid order by e.lastname desc

“””)

for row in cursor.fetchall(): print row

cursor.close()

connection.close()

Exercise 3 solution

You don’t have to change much. The changes are in bold:

import sys

import gadfly.dbapi20

595

TEAM LinG

Appendix A

connection = gadfly.dbapi20.connect(‘pydb’, ‘db’)

cursor = connection.cursor()

employee = sys.argv[1]

#Query to find the employee ID. query = “””

select e.empid

from user u, employee e

where username=? and u.employeeid = e.empid “””

cursor.execute(query,(employee,)); for row in cursor.fetchone():

if (row != None): empid = row

#Now, modify the employee.

cursor.execute(“delete from employee where empid=?”, (empid,)) cursor.execute(“delete from user where employeeid=?”, (empid,))

connection.commit()

cursor.close()

connection.close()

Chapter 15

Exercise 1 solution

from xml.dom.minidom import parse import xml.dom.minidom

# open an XML file and parse it into a DOM myDoc = parse(‘config.xml’)

myConfig = myDoc.getElementsByTagName(“config”)[0]

#Get utility directory myConfig.getElementsByTagName(“utilitydirectory”)[0].childNodes[0].data

#Get utility myConfig.getElementsByTagName(“utility”)[0].childNodes[0].data

#get mode myConfig.getElementsByTagName(“mode”)[0].childNodes[0].data

#.....Do something with data.....

Exercise 2 solution

#!/usr/bin/python

from xml.parsers.xmlproc import xmlval

class docErrorHandler(xmlval.ErrorHandler):

596

TEAM LinG

Answers to Exercises

def warning(self, message): print message

def error(self, message): print message

def fatal(self, message):

print message

parser=xmlval.XMLValidator() parser.set_error_handler(docErrorHandler(parser)) parser.parse_resource(“configfile.xml”)

Exercise 3 solution

#!/usr/bin/python

from

xml.sax

import

make_parser

from

xml.sax.handler

import

ContentHandler

#begin configHandler

class configHandler(ContentHandler): inUtildir = False

utildir = ‘’ inUtil = False util = ‘’ inMode = False mode = ‘’

def startElement(self, name, attributes):

if name == “utilitydirectory”: self.inUtildir = True

elif name == “utility”: self.inUtil = True

elif name == “mode”: self.inMode = True

def endElement(self, name):

if name == “utilitydirectory”: self.inTitle = False

elif name == “utility”: self.inUtil = False

elif name == “mode”: self.inMode = False

def characters(self, content): if self.inUtildir:

utildir = utildir + content elif self.inUtil:

util = util + content elif self.inMode:

mode = mode + content

597

TEAM LinG

Appendix A

#end configHandler

parser = make_parser() parser.setContentHandler(configHandler()) parser.parse(“configfile.xml”)

#....Do stuff with config information here

Chapter 16

Exercise 1 solution

RFC 2822 is a file format standard that describes what e-mail messages should look like.

MIME is a file format standard that describes how to create e-mail messages that contain binary data and multiple parts, while still conforming to RFC 2822.

SMTP is a protocol used to deliver an e-mail message to someone else.

POP is a protocol used to pick up your e-mail from your mail server.

IMAP is a newer protocol that does the same job as POP. It’s intended to keep the e-mail on the server permanently, instead of just keeping it until you pick it up.

Exercise 2 solution

Here’s a script that uses POP:

#!/usr/bin/python

from poplib import POP3

from email.Parser import Parser

#Connect to the server and parse the response to see how many messages there #are, as in this chapter’s previous POP example.

server = POP3(“pop.example.com”) server.user(“[user]”)

response = server.pass_(“[password]”) numMessages = response[response.rfind(‘, ‘)+2:]

numMessages = int(numMessages[:numMessages.find(‘ ‘)])

#Parse each email and put it in a file named after the From: header of #the mail.

parser = Parser() openFiles = {}

for messageNum in range(1, numMessages+1):

messageString = ‘\n’.join(server.retr(messageNum)[1]) message = email.parsestr(messageString, True) fromHeader = message[‘From’]

mailFile = openFiles.get(fromHeader) if not mailFile:

mailFile = open(fromHeader, ‘w’)

598

TEAM LinG

Answers to Exercises

openFiles[fromHeader] = mailFile mailFile.write(messageString) mailFile.write(‘\n’)

#Close all the files to which we wrote mail. for openFile in openFiles.values():

openFile.close()

Because IMAP enables you to sort messages into folders on the server, an IMAP version of this script can simply create new mailboxes and move messages into them. Here’s a script that does just that:

#!/usr/bin/python

from imaplib import IMAP4 import email

import re

#Used to parse the IMAP responses. FROM_HEADER = ‘From: ‘

IMAP_UID = re.compile(‘UID ([0-9]+)’)

#Connect to the server.

server = IMAP4(‘imap.example.com’) server.login(‘[username]’, ‘[password]’) server.select(‘Inbox’)

#Get the unique IDs for every message.

uids = server.uid(‘SEARCH’, ‘ALL’)[1][0].split(‘ ‘) uidString = ‘,’.join(uids)

#Get the From: header for each message

headers = server.uid(‘FETCH’, ‘%s’ % uidString, ‘(BODY[HEADER.FIELDS (FROM)])’)

for header in headers[1]: if len(header) > 1:

uid, header = header

#Parse the IMAP response into a real UID and the value of the #’From’ header.

match = IMAP_UID.search(uid) uid = match.groups(1)[0]

fromHeader = header[len(FROM_HEADER):].strip()

#Create the mailbox corresponding to the person who sent this #message. If it already exists the server will throw an error, #but we’ll just ignore it.

server.create(fromHeader)

#Copy this message into the mailbox. server.uid(‘COPY’, uid, fromHeader)

#Delete the messages from the inbox now that they’ve been filed. server.uid(‘STORE’, uidString, ‘+FLAGS.SILENT’, ‘(\\Deleted)’)

server.expunge()

599

TEAM LinG

Appendix A

Exercise 3 solution

In general, move as much text as possible out of the protocol and into the client software, which needs to be downloaded only once. Some specific suggestions:

Send short status codes instead of English sentences: for instance, send “HELLO” instead of “Hello [nickname], welcome to the Python Chat Server!”.

Assign a number to every user in the chat room, and send the number instead of their nickname whenever they do something — for instance, broadcast ‘4 Hello’ instead of ‘<user> Hello’ whenever a user sends a message.

Use a compression technique to make the chat text itself take up less bandwidth.

Exercise 4 solution

The easiest way is to simply define a method ‘msgCommand’ and let the _parseCommand dispatch it. Here’s a simple implementation of msgCommand:

def msgCommand(self, nicknameAndMsg):

“Send a private message to another user.” if not ‘ ‘ in nicknameAndMsg:

raise ClientError(‘No message specified.’) nickname, msg = nicknameAndMsg.split(‘ ‘, 1) if nickname == self.nickname:

raise ClientError(‘What, send a private message to yourself?’) user = self.server.users.get(nickname)

if not user:

raise ClientError(‘No such user: %s’ % nickname) msg = ‘[Private from %s] %s’ % (self.nickname, msg) user.write(self._ensureNewline(msg))

Exercise 5 solution

The peer-to-peer architecture is more general than the client-server architecture. The peer-to- peer design of TCP/IP makes it a flexible general-purpose protocol. It’s easier to implement a client-server protocol atop TCP/IP than it is to implement a peer-to-peer design on top of a client-server protocol. If you want a general-purpose protocol, try to preserve the peer-to-peer nature of TCP/IP.

Consider using peer-to-peer when it makes sense for a client to download some data from a server and then immediately start serving it to other clients. A peer-to-peer architecture for the distribution of e-mail doesn’t make sense, because most e-mail is addressed to one person only. Once that person has downloaded the e-mail, it shouldn’t be automatically distributed further. A peer-to-peer architecture for the distribution of newsletters makes more sense.

Peer-to-peer is most useful when you have some way of searching the network. When a network resource doesn’t have a single, unambiguous location (the way a file hosted on a web server does), it’s more difficult to find what you want, and search facilities are more important.

600

TEAM LinG