燕之庐网站建设 - 优质网站设计公司

Archive for 1月, 2010

未分类

2010/01/31

linux – yum

yum是什么?(linux命令)
一. yum是什么
yum = Yellow dog Updater, Modified
主要功能是更方便的添加/删除/更新RPM包.
它能自动解决包的倚赖性问题.
它能便于管理大量系统的更新问题
注:为什么要使用yum而不用apt,最简单的原因,Fedora自带

二. yum特点
*可以同时配置多个资源库(Repository)
*简洁的配置文件(/etc/yum.conf)
*自动解决增加或删除rpm包时遇到的倚赖性问题
*使用方便
*保持与RPM数据库的一致性

三. yum安装
Fedora自带
#rpm -ivh yum-2.0.4-2.noarch.rpm

四. yum配置
注:修改和增加配置文件中的资源库,加快下载速度和拥有更多可更新的rpm包
将/etc/yum.conf的内容全部替换为
[main]
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=fedora-release
tolerant=1
exactarch=1

[fedora-us-1]
name=Fedora Core 1 — Fedora US mirror
baseurl=ftp://mirrors.kernel.org/fedora.us/fedora/fedora/1/i386/yum/os

[fedora-us-1-updates]
name=Fedora Core 1 updates — Fedora US mirror
baseurl=ftp://mirrors.kernel.org/fedora.us/fedora/fedora/1/i386/yum/updates

[fedora-us-1-stable]
name=Fedora Linux (stable) for Fedora Core 1 — Fedora US mirror
baseurl=ftp://mirrors.kernel.org/fedora.us/fedora/fedora/1/i386/yum/stable

[freshrpms]
name=Fedora Linux $releasever – $basearch – freshrpms
baseurl=http://ayo.freshrpms.net/fedora/linux/$releasever/$basearch/freshrpms
五. yum应用
注:当第一次使用yum或yum资源库有更新时,yum会自动下载所有所需的headers放置于/var/cache/yum目录下,所需时间可能较长.

检查有哪些可更新的rpm包
#yum check-update

安装rpm包,使xmms可以播放mp3
#yum install xmms-mp3

安装mplayer,同时自动安装相关的软件
#yum install mplayer

删除licq包,同时删除与该包有倚赖性的包
#yum remove licq
注:同时会提示删除licq-gnome,licq-qt,licq-text,非常方便

系统更新(更新所有可以升级的rpm包,包括kernel)
#yum -y update

每天定期执行系统更新
#chkconfig yum on
#service yum start
六. yum指令详解
*rpm包的更新

检查可更新的rpm包
#yum check-update

更新所有的rpm包
#yum update

更新指定的rpm包,如更新kernel和kernel source
#yum update kernel kernel-source

大规模的版本升级,与yum update不同的是,连旧的淘汰的包也升级
#yum upgrade
*rpm包的安装和删除

安装rpm包,如xmms-mp3
#yum install xmms-mp3

删除rpm包,包括与该包有倚赖性的包
#yum remove licq
注:同时会提示删除licq-gnome,licq-qt,licq-text
*yum暂存(/var/cache/yum/)的相关参数
清除暂存中rpm包文件
#yum clean packages

清除暂存中rpm头文件
#yum clean headers

清除暂存中旧的rpm头文件
#yum clean oldheaders

清除暂存中旧的rpm头文件和包文件
#yum clean 或#yum clean all
注:相当于yum clean packages + yum clean oldheaders
*rpm包列表

列出资源库中所有可以安装或更新的rpm包
#yum list

列出资源库中特定的可以安装或更新以及已经安装的rpm包
#yum list mozilla
#yum list mozilla*
注:可以在rpm包名中使用匹配符,如列出所有以mozilla开头的rpm包

列出资源库中所有可以更新的rpm包
#yum list updates

列出已经安装的所有的rpm包
#yum list installed

列出已经安装的但是不包含在资源库中的rpm包
#yum list extras
注:通过其它网站下载安装的rpm包
*rpm包信息显示(info参数同list)

列出资源库中所有可以安装或更新的rpm包的信息
#yum info

列出资源库中特定的可以安装或更新以及已经安装的rpm包的信息
#yum info mozilla
#yum info mozilla*
注:可以在rpm包名中使用匹配符,如列出所有以mozilla开头的rpm包的信息

列出资源库中所有可以更新的rpm包的信息
#yum info updates

列出已经安装的所有的rpm包的信息
#yum info installed

列出已经安装的但是不包含在资源库中的rpm包的信息
#yum info extras
注:通过其它网站下载安装的rpm包的信息
*搜索rpm包
搜索匹配特定字符的rpm包
#yum search mozilla
注:在rpm包名,包描述等中搜索

搜索有包含特定文件名的rpm包
#yum provides realplay

七. 安全的更新freshrpms.net的rpm包
安装freshrpms.net的GPG key
#rpm –import http://freshrpms.net/packages/RPM-GPG-KEY.txt

编辑/etc/yum.conf,增加以下信息到尾部
[freshrpms]
name=Fedora Linux $releasever – $basearch – freshrpms
baseurl=http://ayo.freshrpms.net/fedora/linux/$releasever/$basearch/freshrpms
gpgcheck=1

注:
检查GPG Key
# rpm -qa gpg-pubkey*

显示Key信息
#rpm -qi gpg-pubkey-e42d547b-3960bdf1

删除Key
#rpm -e gpg-pubkey-e42d547b-3960bdf1

yum是干什么的
使用redhat,fedora的linuxer肯定都为rpm著名的dependency hell而头疼(这也是所有基于rpm发行版都有的问题)。foo is needed by bar,bar is needed by foo。要装卸个软件,还得辛辛苦苦地梳理清楚所有依赖性问题,rpm的几个搜索站点成了时常光顾的场所,如果不幸碰到循环依赖,那就只有干瞪眼的分了。 有时加个–nodeps参数,倒是眼不见,心不烦,但这样装上的软件,你能保证正常使用吗,这样卸载软件,你能保证不影响其它软件工作吗,恐怕谁也不能 给出肯定的答案。每到这时,帽子们只有看着debian的 apt暗自羡慕了。好在,这些都已经成为过去时了。基于rpm的发行版现在也有了像apt那样自 动解决依赖关系的包管理工具了。其中一个著名的就是apt4rpm,这基本上是debian系统apt的在rpm发行版的移植。但是使用Redhat和 Fedora的linuxer你们知道吗,还有一个与apt功能类似的rpm包管理系统,那就是yum。Fedora系统已经自带,Redhat也有相应 的rpm下载。与apt相比,yum的功能一点也不弱,甚至还有许多胜过apt 之处。比如说,yum是Fedora系统自带的,因此它能使用fedora官方的软件源,完成各种官方发布的各种升级。对于第三方软件源的支持,yum也 不差,大多数支持apt的repository注,也能支持yum,比如说freshrpms,fedora.us,livna等等。此外yum有一个比较 详细的log,可以查看何时升级安装了什么软件包等。yum的代码较apt更为精简等。

linux

linux – sudo

Tags:

sudo

linux系统管理指令
sudo是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具,如 halt,reboot,su等等。这样不仅减少了root用户的登陆 和管理时间,同样也提高了安全性。Sudo不是对shell的一个代替,它是面向每个命令的。它的特性主要有这样几点:
§ Sudo能够限制用户只在某台主机上运行某些命令。
§ Sudo提供了丰富的日志,详细地记录了每个用户干了什么。它能够将日志传到中心主机或者日志服务器。
§ Sudo使用时间戳文件来执行类似的“检票”系统。当用户调用sudo并且输入它的密码时,用户获得了一张存活期为5分钟的票(这个值可以在编译的时候改变)。
§ Sudo的配置文件是sudoers文件,它允许系统管理员集中的管理用户的使用权限和使用的主机。它所存放的位置默认是在/etc/sudoers,属性必须为0411。
一,安装
检测是否一经安装了SUDO:
[root@localhost ~]# rpm -q sudo
sudo-1.6.8p12-4.1
如果没有安装,下载软件包进行安装:
对于大多数系统来说,sudo的配置都是相对比较简单的:
0) $> cd /;cp sudo-1.6.8p12.tar.gz /
1) $> tar vxzf sudo-1.6.8p12.tar.gz
2) 如果是从低版本升级的话,升级之前请仔细阅读UPGRADE文件。
3) 如果你在编译之前,已经为另外一台不同的主机运行了’configure’,你必须用make distclean清除’config.cache’文件。否则,’configure’将不能再运行。你也可以直接’rm config.cache’.
4) 阅读’OS dependent notes’看是否支持你的系统。
5) $> cd sudo-8p12
6) 阅读configure文件,仔细看其中的’Available configure options’部分,看是否要加一些特殊的选项。这里选项很多,不过常用的主要有这么几个主要的配置参数:
–with-pam 支持使用PAM,使用该选项时要求一个有效的/etc/pam.d/sudo文件
–cache-file=FILE 将缓存test的结果存到FILE文件中。
–help 打印帮助。
–no-create 不要输出.o文件
–quiet, –silent 不打印’checking…’信息
–exec-prefix=EPREFIX 设置包含sudo 和visudo命令的目录
–bindir=DIR 将sudo命令安装到EPREFIX/bin
–sbindir=DIR将visudo命令安装到EPREFIX/bin
–sysconfdir=DIR 将sudoers配置文件安装到DIR,默认为/etc.
–mandir=DIR将man文件安装到DIR,默认为/man.
–with-CC=path 指定你要使用的c编译器的路径。
–with-skey 支持S/Key OTP(一次使用密码)
–with-opie支持NRL OPIE OTP(一次使用密码)
–disable-shadow 不支持shadow密码的系统的选项开关,sudo默认是编译并使用shadow密码的。
–with-sudoers-mode=mode sudoers配置文件模式,默认为0440。
–with-sudoers-uid sudoers配置文件的所有者id,默认为0
–with-sudoers-gid sudoers配置文件的所有者的组id,默认为0
–without-passwd 用户认证无须密码。
–with-logging=TYPE 日志类型,可以选择’syslog’、’file’或者两种都可以。
–with-logpath=path 日志存放的路径和文件名,默认为/var/log/sudo.log
–with-umask 在运行root命令是用umask,默认的umask是0022
–with-passwd-tries=tries sudo写入log日志前提示输入密码的次数,默认为3次。
–with-timeout=minutes sudo提示输入密码之前的时间,默认为5分钟
–with-password-timeout=minutes 密码的有效期,默认为5分钟,0表示密码永远有效。
–without-lecture 第一次运行sudo不打印lecture信息
–disable-root-sudo root用户不能运行sudo。
–enable-log-host 记录主机host到日志文件
–disable-path-info 出错时,不显示sudo的路径。
如 输入:./configure –with-timeout=10 –without-lecture –disable-root-sudo –disable-path-info –sysconfdir=/home/config/ –bindir=/bin –sbindir=/sbin
7) make
8) make install
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
二,配置,
编辑配置文件命令:visudo
默认配置文件位置:/etc/sudoers
[root@localhost ~]# cat /etc/sudoers
# sudoers file.
#
# This file MUST be edited with the ‘visudo’ command as root.
#
# See the sudoers man page for the details on how to write a sudoers file.
#
# Host alias specification
# User alias specification
# Cmnd alias specification
# Defaults specification
# Runas alias specification
# User privilege specification
root ALL=(ALL) ALL
# Uncomment to allow people in group wheel to run all commands
# %wheel ALL=(ALL) ALL
# Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL
# Samples
# %users ALL=/sbin/mount /cdrom,/sbin/umount /cdrom
# %users localhost=/sbin/shutdown -h now
[root@localhost ~]#
可以用visudo编辑sudoers配置文件,不过也可以直接通过修改sudoers文件实现,不过编辑之前最好看一下它的sample.sudoers文件,里面有一个相当详细的例子可以参考。
#第一部分:用户定义,将用户分为FULLTIMERS、PARTTIMERS和WEBMASTERS三类。
User_Alias FULLTIMERS = millert, mikef, dowdy
User_Alias PARTTIMERS = bostley, jwfox, crawl
User_Alias WEBMASTERS = will, wendy, wim
#第二部分,将操作类型分类。
Runas_Alias OP = root, operator
Runas_Alias DB = oracle, sybase
#第三部分,将主机分类。这些都是随便分得,目的是为了更好地管理。
Host_Alias SPARC = bigtime, eclipse, moet, anchor :\
SGI = grolsch, dandelion, black :\
ALPHA = widget, thalamus, foobar :\
HPPA = boa, nag, python
Host_Alias CUNETS = 128.138.0.0/255.255.0.0
Host_Alias CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
Host_Alias SERVERS = master, mail, www, ns
Host_Alias CDROM = orion, perseus, hercules
#第四部分,定义命令和命令地路径。命令一定要使用绝对路径,避免其他目录的同名命令被执行,造成安全隐患 ,因此使用的时候也是使用绝对路径!
Cmnd_Alias DUMPS = /usr/bin/mt, /usr/sbin/dump, /usr/sbin/rdump,\
/usr/sbin/restore, /usr/sbin/rrestore
Cmnd_Alias KILL = /usr/bin/kill
Cmnd_Alias PRINTING = /usr/sbin/lpc, /usr/bin/lprm
Cmnd_Alias SHUTDOWN = /usr/sbin/shutdown
Cmnd_Alias HALT = /usr/sbin/halt, /usr/sbin/fasthalt
Cmnd_Alias REBOOT = /usr/sbin/reboot, /usr/sbin/fastboot
Cmnd_Alias SHELLS = /usr/bin/sh, /usr/bin/csh, /usr/bin/ksh, \
/usr/local/bin/tcsh, /usr/bin/rsh, \
/usr/local/bin/zsh
Cmnd_Alias SU = /usr/bin/su
# 这里是针对不同的用户采用不同地策略,比如默认所有的syslog直接通过auth 输出。FULLTIMERS组不用看到lecture(第一次运行时产生的消息);用户millert使用sudo时不用输入密码;以及logfile的 路径在/var/log/sudo.log而且每一行日志中必须包括年。
Defaults syslog=auth
Defaults:FULLTIMERS !lecture
Defaults:millert !authenticate
Defaults@SERVERS log_year, logfile=/var/log/sudo.log
#root和wheel组的成员拥有任何权利。 如果想对一组用户进行定义,可以在组名前加上%,对其进行设置.
root ALL = (ALL) ALL
%wheel ALL = (ALL) ALL
#FULLTIMERS可以运行任何命令在任何主机而不用输入自己的密码
FULLTIMERS ALL = NOPASSWD: ALL
#PARTTIMERS可以运行任何命令在任何主机,但是必须先验证自己的密码。
PARTTIMERS ALL = ALL
#jack可以运行任何命令在定义地CSNET(128.138.243.0, 128.138.242.0和128.138.204.0/24的子网)中,不过注意前两个不需要匹配子网掩码,而后一个必须匹配掩码。
jack CSNETS = ALL
#lisa可以运行任何命令在定义为CUNETS(128.138.0.0)的子网中主机上。
lisa CUNETS = ALL
#用户operator可以运行DUMPS,KILL,PRINTING,SHUTDOWN,HALT,REBOOT以及在/usr/oper/bin中的所有命令。
operator ALL = DUMPS, KILL, PRINTING, SHUTDOWN, HALT, REBOOT,\
/usr/oper/bin/
#joe可以运行su operator命令
joe ALL = /usr/bin/su operator
#pete可以为除root之外地用户修改密码。
pete HPPA = /usr/bin/passwd [A-z]*, !/usr/bin/passwd root
#bob可以在SPARC和SGI机器上和OP用户组中的root和operator一样运行如何命令。
bob SPARC = (OP) ALL : SGI = (OP) ALL
#jim可以运行任何命令在biglab网络组中。Sudo默认“+”是一个网络组地前缀。
jim +biglab = ALL
#在secretaries中地用户帮助管理打印机,并且可以运行adduser和rmuser命令。
+secretaries ALL = PRINTING, /usr/bin/adduser, /usr/bin/rmuser
#fred能够直接运行oracle或者sybase数据库。
fred ALL = (DB) NOPASSWD: ALL
#john可以在ALPHA机器上,su除了root之外地所有人。
john ALPHA = /usr/bin/su [!-]*, !/usr/bin/su *root*
#jen可以在除了SERVERS主机组的机器上运行任何命令。
jen ALL, !SERVERS = ALL
#jill可以在SERVERS上运行/usr/bin/中的除了su和shell命令之外的所有命令。
jill SERVERS = /usr/bin/, !SU, !SHELLS
#steve可以作为普通用户运行在CSNETS主机上的/usr/local/op_commands/内的任何命令。
steve CSNETS = (operator) /usr/local/op_commands/
#matt可以在他的个人工作站上运行kill命令。
matt valkyrie = KILL
#WEBMASTERS用户组中的用户可以以www的用户名运行任何命令或者可以su www。
WEBMASTERS www = (www) ALL, (root) /usr/bin/su www
#任何用户可以mount或者umount一个cd-rom在CDROM主机上,而不用输入密码。
ALL CDROM = NOPASSWD: /sbin/umount /CDROM,\
/sbin/mount -o nosuid\,nodev /dev/cd0a /CDROM
三,使用
指令名称:sudo
使用权限:在 /etc/sudoers 中有出现的使用者
使用方式:sudo -V
sudo -h
sudo -l
sudo -v
sudo -k
sudo -s
sudo -H
sudo [ -b ] [ -p prompt ] [ -u username/#uid] -s
用法:sudo command
说明:以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行
参数:
-V 显示版本编号
-h 会显示版本编号及指令的使用方式说明
-l 显示出自己(执行 sudo 的使用者)的权限
-v 因为 sudo 在第一次执行时或是在 N 分钟内没有执行(N 预设为五)会问密码,这个参数是重新做一次确认,如果超过 N 分钟,也会问密码
-k 将会强迫使用者在下一次执行 sudo 时问密码(不论有没有超过 N 分钟)
-b 将要执行的指令放在背景执行
-p prompt 可以更改问密码的提示语,其中 %u 会代换为使用者的帐号名称, %h 会显示主机名称
-u username/#uid 不加此参数,代表要以 root 的身份执行指令,而加了此参数,可以以 username 的身份执行指令(#uid 为该 username 的使用者号码)
-s 执行环境变数中的 SHELL 所指定的 shell ,或是 /etc/passwd 里所指定的 shell
-H 将环境变数中的 HOME (家目录)指定为要变更身份的使用者家目录(如不加 -u 参数就是系统管理者 root )
command 要以系统管理者身份(或以 -u 更改为其他人)执行的指令
范例:
sudo -l 列出目前的权限
sudo -V 列出 sudo 的版本资讯
指令名称:sudoers(在fc5下显示不能找到此命令,但用man可以查到其用法。)
用来显示可以使用sudo的用户

网站建设

MySQL Proxy 0.8.0发布

Tags: ,

MySQL Proxy 0.8.0版本发布了!

0.8.0新特性:

  • 增加了一个线程事件处理层
  • 支持auth-response改变
  • 新增了timing infrastruture
  • 新增了out-of-tree plugins build和pkg-config的支持
  • 以及一些bug的修复

MySQL-Proxy是处在你的MySQL数据库客户和服务端之间的程序,它还支持嵌入性脚本语言Lua。这个代理可以用来分析、监控和变换 (transform)通信数据,它支持非常广泛的使用场景:

  • 负载平衡和故障转移处理
  • 查询分析和日志
  • SQL宏(SQL macros)
  • 查询重写(query rewriting)
  • 执行shell命令

网站建设

网站建设

马云:不用在意商业模式是否清晰

Tags:

北京时间1月25日消 息,1月19日上午,两岸的重量级企业家,明基友达集团董事长李焜耀、台湾中华电信董事长吕学锦,以及阿里巴巴集团董事局主席兼首席执行官马云,参加台湾杂志举办的论坛。

对于众人关心 的大陆企业成功模式,马云认为:成功并 没有模式,每个人走的路都不一样。他说阿里巴巴只是活下来了,每次我有成功感时,麻烦就来了。大家只看到我们的成功,但事实上背后我们犯了一千个错。

马 云指出,企业家应该要想着如何将公司经营成艺术品,因为行为模式能被复制的公司,不会是好公司,千万别晚上想了千条路,早上起来走原路,一旦企业的成功模 式能够被讲清楚之后,可能你离企业的死亡也差不多了。

除了新的商 业模式革命,马云也认为未来亚洲的另两个重要趋势是小企业大梦想和年轻人才培育,现代企业家应该求义不求利,商机来自 于寻找、发现社会问题,然后从解决问题中创造财富。他相信成功的企业是把客户放在第一位、员工放在第二位,最后才是看股东,因为发生危机时,股东跑得最 快,员工会留下奋斗。

网站建设

产业新闻, 网站建设

Red Hat推出开源成果展示网站OpenSource.com

Tags:

Red Hat 近日推出了旨在介绍开源成果应 用的展示站点OpenSource.com。 OpenSource.com被用来作为开源项目实际应用的展示站点,分为商业、教育、政府机关、法律、生活五大门类。

开放公众注册,鼓励社区成员将自己的开源项 目实际应用经验分享,从实际应用的角度去诠释开源精神。相信该站点对于研究开源文化和希望寻找开源策略成功应用案例的朋友会有帮助。”

网站建设

网站建设

用telnet的方法导出、导入论坛mysql数据库

Tags:

1。这里介绍的方法主要适用于VB等论坛。

用TELNET的方法可将现有的服务器上的数据库导出,然后再将此数据导入另一台服务器,用这个方法可以实现论坛在两个服务器的迁移; 或者将服务器上的现有论坛搬到本机,来进行增加功能及 HACK 的安装调试, 而不会影响服务器上的论坛的运作。

我已用这个方法将我的整个网站(有论坛、新闻发布、及其他个人的主页)的文件和数据库全部搬到本机,可以实现文件和数据库的备份,同时可以在本 机进行多种功能、多操作系统的的测试。

2。有多种方法可以实现 mysql数据库的导出、导入,但我用PHPmyadmin没有成功,也许是数据库大于5MB的原因吧。没有进行详细分析。

3。运行telnet 程序连接本地机与远程机:

如果你用的是windows 系统:开始(Start)–〉运行(Run),键入telnet,你将看到:

telnet>

这是telnet提示符。它表明程序已运行并等待你录入一个命令。若要连接一台远程机,录入open,并附上远程机的地址及端口即可。例如:

open XXXX.com 23

提供用户名和密码

login: XXXXXX

Password:XXXXXX

我朋友所使用的Web Host服务器是Unix系统,要用SSH(Secure Shell)来连接,另一台使用的是SecureCRT3.3.

4.连接到主机后,备份现有的数据库:

键入指令 mysqldump -uUSERNAME -pPASSWORD databasename > /你的路径/dump.sql

这个过程大约30秒钟,取决于你的数据库的大小。

之后,检查一下/dump.sql文件是否存在。

5。传送dump.sql文件到新的服务器或本机:

可以用FTP的方式传送。不用详细讲了吧。

6。在新的服务器上复制VB论坛:

运行telnet 程序连接服务器(远程机)

键入指令 mysql -uUSERNAME -pPASSWORD newdbname < /你的路径/dump.sql

之后,可以用PHPmyadmin检查数据库导入的情况。

重新配置好 admin/config.php 文件,上传到服务器.

运行admin/index.php 进入管理员控制面板,–〉论坛选项 ,进行论坛设置:论坛URL,主页的URL等信息,然后“保存修改”。

OK!全搞定!

网站建设

网站建设

win7下的telnet

Tags: ,

今天在win7的命令控制台下想telnet路由器,突然发现竟然提示说没有telnet这条命令,我尴尬了一下。
后来发现其实win7上边是有telnet这条命令的,不过就是默认被关闭了。
开启telnet方法如下:
打开Control Panel,选择Program下的Turn Windows features on or off,在弹出的对话框中将telnet client打上勾就可以了。
打钩的时候注意别给telnet server打上了勾,否则结果~~~~~自己想象吧。
win7下的telnet命令的使用也有些特别。
首先得在cmd下输入telnet进入telnet模式,在telnet模式下可以输入好几条命令,
Microsoft Telnet> ?/help
Commands may be abbreviated. Supported commands are:
c     – close                     close current connection
d     – display                   display operating parameters
o     – open hostname [port]      connect to hostname (default port 23).
q     – quit                      exit telnet
set   – set                       set options (type ’set ?’ for a list)
sen   – send                      send strings to server
st    – status                    print status information
u     – unset                     unset options (type ‘unset ?’ for a list)
?/h   – help                      print help information

网站建设

python

2010/01/30

使用wxPython的其它功能

Tags:

本章内容:

  • 放置对象到剪贴板上
  • 拖放
  • 传送和获取自定义对象
  • 使用wx.Timer设置定时的事件
  • 编写多线程的wxPython应 用程序

放置对象到剪贴板上

wxPython中,剪贴板和拖放特性是紧密相关的。期间,内部窗口的通信是由使用wx.DataObject类 或它的子类的一个实例作为中介的。wx.DataObject是一个特殊的数据对象,它包含描述输出数据格式的元数据。我们将从剪贴板 入手,然后我们将讨论拖放的不同处理。

对于一个剪切和粘贴操作,有三 个元素:

  • source(源)
  • clipboard(剪 贴板)
  • target(目标)

如果source是 在你的应用程序中,那么你的应用程序负责创建wx.DataObject的一个实例并把它放到剪贴板对象。通常source都 是在你的应用程序的外部。

这里的clipboard是 一个全局对象,它容纳数据并在必要时与操作系统的剪贴板交互。

target对 象负责从剪贴板获取wx.DataObject并把它转换为对你的应用程序有用的那一类数据。

得到剪贴板中的数据

如果你想你的应用程序能够引起一个剪贴事件,也就是说你想能够将数据剪切或复制到剪贴板,把数据放置到一个wx.DataObject里 面。wx.DataObject知道自己能够被读写何种格式的数据。这点是比较重要的,例如如果你当时正在写一个词处理程序并希望给用 户在粘贴时选择无格式文本的粘贴或丰富文本格式的粘贴的情况。然而大多数时候,在你的剪贴板行为中不需要太强大或太灵活的性能。对于最常用的情况,wxPython提 供了三个预定义的wx.DataObject的子类:纯文本,位图图像和文件名。

要传递纯文本,可以创建类wx.TextDataObject的一个实例,使用它如下的构造器:

切换行号显示

   1 wx.TextDataObject(text="") 

参数text是你想 传递到剪贴的文本。你可以使用Text(text)方法来设置该文本,你也可以使用GetText()方法来得到该 文本,你还可以使用GetTextLength()方法来得到该文本的长度。

一旦你创建了这种数据对象后,接着你必须访问剪贴板。系统的剪贴板在wxPython中是一个全局性 的对象,名为wx.TheClipboard。要使用它,可以使用它的Open()方法来打开它。如果该剪贴板被打 开了则该方法返回True,否则返回False。如果该剪贴板正在被另一应用程序写入的话,该剪贴板的打开有可能会 失败,因此在使用该剪贴板之前,你应该检查打开方法的返回值。当你使用完剪贴板之后,你应该调用它的 Close()方法来关闭它。打开剪贴板会阻塞其它的剪贴板用户的使用,因此剪贴板打开的时 间应该尽可能的短。

处理剪贴板中的数据

一旦你有了打开的剪贴板,你就可以处理它所包含的数据对象。你可以使用SetData(data)来 将你的对象放置到剪贴板上,其中参数data是一个wx.DataObject实例。你可以使用方法Clear()方 法来清空剪贴板。如果你希望在你的应用程序结束后,剪贴板上的数据还存在,那么你必须调用方法Flush(),该方法命令系统维持你的 数据。否则,该wxPython剪贴板对象在你的应用程序退出时会被清除。

下面是一段添加文本到剪贴板的代码:

切换行号显示

   1 text_data = wx.TextDataObject("hi there")
   2 if wx.TheClipboard.Open():
   3     wx.TheClipboard.SetData(text_data)
   4     wx.TheClipboard.Close()

获得剪贴板中的文本数据

从剪贴板中获得文本数据也是很简单的。一旦你打开了剪贴板,你就可以调用GetData(data)方 法,其中参数datawx.DataObject的一些特定的子类的一个实例。如果剪贴板中的数据能够以与方法中 的数据对象参数相一致的某种格式被输出的话,该方法的返回值则为True。这里,由于我们传递进的是一个wx.TextDataObject, 那么返回值True就意味该剪贴板能够被转换到纯文本。下面是一段样例代码:

切换行号显示

   1 text_data = wx.TextDataObject()
   2 if wx.TheClipboard.Open():
   3     success = wx.TheClipboard.GetData(text_data)
   4     wx.TheClipboard.Close()
   5 if success:
   6     return text_data.GetText()

注意,当你从剪贴板获取数据时,数据并 不关心是哪个应用程序将它放置到剪贴板的。剪贴板中的数据本身被底层的操作系统所管理,wxPython的责任是确保格式的匹配及你能 够得到你能够处理的数据格式。

实战剪贴板

在这一节,我们将显示一个简单的例子,它演示了如何与剪贴板交换数据。它是一个有着两个按钮的框架,它使用户能够复制和 粘贴文本。当你运行这个例子时,结果将会如图18.1所示。

图18.1

w18.1.gif

例18.1是产生图18.1的代码。

例18.1 剪贴板交互示例

切换行号显示

   1 #-*- encoding:UTF-8 -*-
   2 import wx
   3 
   4 t1_text = """\
   5 The whole contents of this control
   6 will be placed in the system's
   7 clipboard when you click the copy
   8 button below.
   9 """
  10 
  11 t2_text = """\
  12 If the clipboard contains a text
  13 data object then it will be placed
  14 in this control when you click
  15 the paste button below.  Try
  16 copying to and pasting from
  17 other applications too!
  18 """
  19 
  20 class MyFrame(wx.Frame):
  21     def __init__(self):
  22         wx.Frame.__init__(self, None, title="Clipboard",
  23                           size=(500,300))
  24         p = wx.Panel(self)
  25 
  26         # create the controls
  27         self.t1 = wx.TextCtrl(p, -1, t1_text,
  28                               style=wx.TE_MULTILINE|wx.HSCROLL)
  29         self.t2 = wx.TextCtrl(p, -1, t2_text,
  30                               style=wx.TE_MULTILINE|wx.HSCROLL)
  31         copy = wx.Button(p, -1, "Copy")
  32         paste = wx.Button(p, -1, "Paste")
  33 
  34         # setup the layout with sizers
  35         fgs = wx.FlexGridSizer(2, 2, 5, 5)
  36         fgs.AddGrowableRow(0)
  37         fgs.AddGrowableCol(0)
  38         fgs.AddGrowableCol(1)
  39         fgs.Add(self.t1, 0, wx.EXPAND)
  40         fgs.Add(self.t2, 0, wx.EXPAND)
  41         fgs.Add(copy, 0, wx.EXPAND)
  42         fgs.Add(paste, 0, wx.EXPAND)
  43         border = wx.BoxSizer()
  44         border.Add(fgs, 1, wx.EXPAND|wx.ALL, 5)
  45         p.SetSizer(border)
  46 
  47         # Bind events
  48         self.Bind(wx.EVT_BUTTON, self.OnDoCopy, copy)
  49         self.Bind(wx.EVT_BUTTON, self.OnDoPaste, paste)
  50 
  51     def OnDoCopy(self, evt):#Copy按钮的事件处理函数
  52         data = wx.TextDataObject()
  53         data.SetText(self.t1.GetValue())
  54         if wx.TheClipboard.Open():
  55             wx.TheClipboard.SetData(data)#将数据放置到剪贴板上
  56             wx.TheClipboard.Close()
  57         else:
  58             wx.MessageBox("Unable to open the clipboard", "Error")
  59 
  60     def OnDoPaste(self, evt):#Paste按钮的事件处理函数
  61         success = False
  62         data = wx.TextDataObject()
  63         if wx.TheClipboard.Open():
  64             success = wx.TheClipboard.GetData(data)#从剪贴板得到数据
  65             wx.TheClipboard.Close()
  66 
  67         if success:
  68             self.t2.SetValue(data.GetText())#更新文本控件
  69         else:
  70             wx.MessageBox(
  71                 "There is no data in the clipboard in the required format",
  72                 "Error")
  73 
  74 
  75 app = wx.PySimpleApp()
  76 frm = MyFrame()
  77 frm.Show()
  78 app.MainLoop()

获得剪贴板中的文本数据

从剪贴板中获得文本数据也是很简单的。一旦你打开了剪贴板,你就可以调用GetData(data)方 法,其中参数datawx.DataObject的一些特定的子类的一个实例。如果剪贴板中的数据能够以与方法中 的数据对象参数相一致的某种格式被输出的话,该方法的返回值则为True。这里,由于我们传递进的是一个wx.TextDataObject, 那么返回值True就意味该剪贴板能够被转换到纯文本。下面是一段样例代码:

切换行号显示

   1 text_data = wx.TextDataObject()
   2 if wx.TheClipboard.Open():
   3     success = wx.TheClipboard.GetData(text_data)
   4     wx.TheClipboard.Close()
   5 if success:
   6     return text_data.GetText()

注意,当你从剪贴板获取数据时,数据 并不关心是哪个应用程序将它放置到剪贴板的。剪贴板中的数据本身被底层的操作系统所管理,wxPython的责任是确保格式的匹配及你 能够得到你能够处理的数据格式。

实战剪贴板

在这一节,我们将显示一个简单的例子,它演示了如何与剪贴板交换数据。它是一个有着两个按钮的框架,它使用户能够复制和 粘贴文本。当你运行这个例子时,结果将会如图18.1所示。

图18.1

w18.1.gif

例18.1是产生图18.1的代码。

例18.1 剪贴板交互示例

切换行号显示

   1 #-*- encoding:UTF-8 -*-
   2 import wx
   3 
   4 t1_text = """\
   5 The whole contents of this control
   6 will be placed in the system's
   7 clipboard when you click the copy
   8 button below.
   9 """
  10 
  11 t2_text = """\
  12 If the clipboard contains a text
  13 data object then it will be placed
  14 in this control when you click
  15 the paste button below.  Try
  16 copying to and pasting from
  17 other applications too!
  18 """
  19 
  20 class MyFrame(wx.Frame):
  21     def __init__(self):
  22         wx.Frame.__init__(self, None, title="Clipboard",
  23                           size=(500,300))
  24         p = wx.Panel(self)
  25 
  26         # create the controls
  27         self.t1 = wx.TextCtrl(p, -1, t1_text,
  28                               style=wx.TE_MULTILINE|wx.HSCROLL)
  29         self.t2 = wx.TextCtrl(p, -1, t2_text,
  30                               style=wx.TE_MULTILINE|wx.HSCROLL)
  31         copy = wx.Button(p, -1, "Copy")
  32         paste = wx.Button(p, -1, "Paste")
  33 
  34         # setup the layout with sizers
  35         fgs = wx.FlexGridSizer(2, 2, 5, 5)
  36         fgs.AddGrowableRow(0)
  37         fgs.AddGrowableCol(0)
  38         fgs.AddGrowableCol(1)
  39         fgs.Add(self.t1, 0, wx.EXPAND)
  40         fgs.Add(self.t2, 0, wx.EXPAND)
  41         fgs.Add(copy, 0, wx.EXPAND)
  42         fgs.Add(paste, 0, wx.EXPAND)
  43         border = wx.BoxSizer()
  44         border.Add(fgs, 1, wx.EXPAND|wx.ALL, 5)
  45         p.SetSizer(border)
  46 
  47         # Bind events
  48         self.Bind(wx.EVT_BUTTON, self.OnDoCopy, copy)
  49         self.Bind(wx.EVT_BUTTON, self.OnDoPaste, paste)
  50 
  51     def OnDoCopy(self, evt):#Copy按钮的事件处理函数
  52         data = wx.TextDataObject()
  53         data.SetText(self.t1.GetValue())
  54         if wx.TheClipboard.Open():
  55             wx.TheClipboard.SetData(data)#将数据放置到剪贴板上
  56             wx.TheClipboard.Close()
  57         else:
  58             wx.MessageBox("Unable to open the clipboard", "Error")
  59 
  60     def OnDoPaste(self, evt):#Paste按钮的事件处理函数
  61         success = False
  62         data = wx.TextDataObject()
  63         if wx.TheClipboard.Open():
  64             success = wx.TheClipboard.GetData(data)#从剪贴板得到数据
  65             wx.TheClipboard.Close()
  66 
  67         if success:
  68             self.t2.SetValue(data.GetText())#更新文本控件
  69         else:
  70             wx.MessageBox(
  71                 "There is no data in the clipboard in the required format",
  72                 "Error")
  73 
  74 
  75 app = wx.PySimpleApp()
  76 frm = MyFrame()
  77 frm.Show()
  78 app.MainLoop()

在下一节中,我们将讨论如何传递其它 格式的数据,如位图。

传递其它格式的数据

经由剪贴板交互位图几乎与传递文本相同。你所使用的相关的数据对象子类是wx.BitmapDataObject, 其get*方法和set*方法分别是GetBitmap()SetBitmap(bitmap)。 经由该数据对象与剪贴板交互的数据对象必须是wx.Bitmap类型的。

最后一个预定义的数据对象类型是wx.FileDataObject。通常该数据对象被用于拖放中 (将在18.2节中讨论),例如当你将一个文件从你的资源管理器或查找窗口放置到你的应用程序上时。你可以使用该数据对象从剪贴板接受文件名数据,并且你 可以使用方法GetFilenames()来从该数据对象获取文件名,该方法返回一个文件名的列表,列表中的每个文件名是已经被添加到 剪贴板的文件名。你可以使用该数据对象的AddFile(file)方法来将数据放置到剪贴板上,该方法将一个文件名字符串添加到该数 据对象。这里没有其它的方法用于直接处理列表,所以这就要靠你自己了。本章的稍后部份,我们将讨论如何经由剪贴板传送自定义对象,以及如何拖放对象。

拖放源

拖放是一个类似剪切和粘贴的功能。它是在你的应用程序的不同部分之间或两个不同的应用程序之间传送数据。由于管理数据和 格式几乎是相同的,所以wxPython同样使用wx.DataObject族来确保对格式作恰当的处理。

拖放和剪切粘贴的最大不同是,剪切粘贴信赖于中介剪贴板的存在。因为是剪贴板管理数据,所以源程序将数据传送后就不管 之后的事情了。这对于拖放却不然,源应用程序不仅虽要创建一个拖动管理器来服务于剪贴板,而且它也必须等待目标应用程序的响应。不同于一个剪贴板的操作, 在拖放中,是目标应用来决定操作是一个剪贴或拷贝,所以源应用必须等待以确定传送的数据所用的目的。

通常,对源的拖动操作是在一个事件处理函数中进行,通常是一个鼠标事件,因为拖动通常都随鼠标的按下事件发生。创建一个 拖动源要求四步:

1、创建数据对象 2、创建wx.DropSource实例 3、执行拖动操作 4、取消或允许释放

步骤1 创建一个数据对象

这第一步是创建你的数据对象。这在早先的剪贴板操作中有很好的说明。对于简单的数据,使用预定义的wx.DataObject的 子类是最简单的。有了数据对象后,你可以创建一个释放源实例

步骤2 创建释放源实例

接下来的步骤是创建一个wx.DropSource实例,它扮演类似于剪贴板这样的传送角色。wx.DropSource的 构造函数如下:

切换行号显示

   1 wx.DropSource(win, iconCopy=wx.NullIconOrCursor,
   2         iconMove=wx.NullIconOrCursor,
   3         iconNone=wx.NullIconOrCursor)

参数win是初始 化拖放操作的窗口对象。其余的三个参数用于使用自定义的图片来代表鼠标的拖动意义(拷贝、移动、取消释放)。如果这三个参数没有指定,那么使用系统的默认 值。在微软的Windows系统上,图片必须是wx.Cursor对象,对于Unix则应是wx.Icon对 象——Mac OS目前忽略你的自定义图片。

一旦你有了你的wx.DropSource实 例,那么就可以使用方法SetData(data)来将你的数据对象关联到wx.DropSource实例。接下来 我们将讨论实际的拖动。

步骤3 执行拖动

拖动操作通过调用释放源的方法DoDragDrop(flags=wx.Drag_CopyOnly)来 开始。参数flags表示目标可对数据执行的何种操作。取值有wx.Drag_AllowMove,它表示批准执行 一个移动或拷贝,wx.Drag_DefaultMove表示不仅允许执行一个移动或拷贝,而且做默认的移动操作,wx.Drag_CopyOnly表 示只执行一个拷贝操作。

步骤4 处理释放

DoDragDrop()方法直到释放被目标取消或接受才会返回。在此期间,虽然绘制事件会继续被发 送,但你的应用程序的线程被阻塞。DoDragDrop()的返回值基于目标所要求的操作,取值如下:

wx.DragCancel(对于取消操作而言)

wx.DragCopy (对于拷贝操作而言)

wx.DragMove (对于移动操作而言)

wx.DragNone (对于错误而言)

对这些返回值的响应由你的应 用程序来负责。通常对于响应移动要删除被拖动的数据外,对于拷贝则是什么也不用做。

实战拖动

例18.2显示了一个完整的拖动源控件,适合于通过拖动上面的箭头图片到你的系统的任何接受文本的应用上(如Microsoft word)。 图18.2图示了这个例子。

图18.2

w18.2.gif

例18.2 一个小的拖动源控件

切换行号显示

   1 #-*- encoding:UTF-8 -*-
   2 
   3 import wx
   4 
   5 class DragController(wx.Control):
   6     """
   7     Just a little control to handle dragging the text from a text
   8     control.  We use a separate control so as to not interfere with
   9     the native drag-select functionality of the native text control.
  10     """
  11     def __init__(self, parent, source, size=(25,25)):
  12         wx.Control.__init__(self, parent, -1, size=size,
  13                             style=wx.SIMPLE_BORDER)
  14         self.source = source
  15         self.SetMinSize(size)
  16         self.Bind(wx.EVT_PAINT, self.OnPaint)
  17         self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
  18 
  19     def OnPaint(self, evt):
  20         # draw a simple arrow
  21         dc = wx.BufferedPaintDC(self)
  22         dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
  23         dc.Clear()
  24         w, h = dc.GetSize()
  25         y = h/2
  26         dc.SetPen(wx.Pen("dark blue", 2))
  27         dc.DrawLine(w/8,   y,  w-w/8, y)
  28         dc.DrawLine(w-w/8, y,  w/2,   h/4)
  29         dc.DrawLine(w-w/8, y,  w/2,   3*h/4)
  30 
  31     def OnLeftDown(self, evt):
  32         text = self.source.GetValue()
  33         data = wx.TextDataObject(text)
  34         dropSource = wx.DropSource(self)#创建释放源
  35         dropSource.SetData(data)#设置数据
  36         result = dropSource.DoDragDrop(wx.Drag_AllowMove)#执行释放
  37 
  38         # if the user wants to move the data then we should delete it
  39         # from the source
  40         if result == wx.DragMove:
  41             self.source.SetValue("")#如果需要的话,删除源中的数据
  42 
  43 class MyFrame(wx.Frame):
  44     def __init__(self):
  45         wx.Frame.__init__(self, None, title="Drop Source")
  46         p = wx.Panel(self)
  47 
  48         # create the controls
  49         label1 = wx.StaticText(p, -1, "Put some text in this control:")
  50         label2 = wx.StaticText(p, -1,
  51            "Then drag from the neighboring bitmap and\n"
  52            "drop in an application that accepts dropped\n"
  53            "text, such as MS Word.")
  54         text = wx.TextCtrl(p, -1, "Some text")
  55         dragctl = DragController(p, text)
  56 
  57         # setup the layout with sizers
  58         sizer = wx.BoxSizer(wx.VERTICAL)
  59         sizer.Add(label1, 0, wx.ALL, 5)
  60         hrow = wx.BoxSizer(wx.HORIZONTAL)
  61         hrow.Add(text, 1, wx.RIGHT, 5)
  62         hrow.Add(dragctl, 0)
  63         sizer.Add(hrow, 0, wx.EXPAND|wx.ALL, 5)
  64         sizer.Add(label2, 0, wx.ALL, 5)
  65         p.SetSizer(sizer)
  66         sizer.Fit(self)
  67 
  68 
  69 app = wx.PySimpleApp()
  70 frm = MyFrame()
  71 frm.Show()
  72 app.MainLoop()

接下来,我们将给你展示目标处的拖 放。

拖放到的目标

实现拖放到的目标的步骤基本上借鉴了实现拖放源的步骤。其中最大的区别是,实现拖放源,你可以直接使用类wx.DropSource, 而对于目标,你首先必须写你的自定义的wx.DropTarget的子类。一旦你有了你的目标类,你将需要创建它的一个实例,并通过使 用wx.WindowSetDropTarget(target)方法将该实例与任一 wx.Window的实例关联起来。设置了目标后,wx.Window的 实例(不论它是一个窗口,一个按钮,一个文本域或其它的控件)就变成了一个有效的释放目标。为了在你的释放目标上接受数据,你也必须创建一个所需要类型的wx.DataObject对 象,并使用释放目标方法SetDataObject(data)wx.DataObject对象与释放目标关联起 来。在实际释放操作前,你需要预先定义数据对象,以便该释放目标能够正确地处理格式。要从目标获取该数据对象,有一个方法GetDataObject()。 下面的样板代码使得释放目标能够接受文本(仅能接受文本)。这是因为数据对象已经被设置为wx.TextDataObject的一个实 例。

切换行号显示

   1 class MyDropTarget(wx.DropTarget):
   2     def __init__(self):
   3         self.data = wx.TextDataObject()
   4         self.SetDataObject(data)
   5 target = MyDataTarget()
   6 win.SetDropTarget(target)

使用你的释放到的目标

当一个释放发生时,你的wx.DropTarget子类的各种事件函数将被调用。其中最重要的是OnData(x, y, default), 它是你必须在你自定义的释放目标类中覆盖的一个事件方法。参数x,y是释放时鼠标的位置。default参数是DoDragDrop()的 四个取值之一,具体的值基于操作系统,传递给DoDragDrop()标志和当释放发生时修饰键的状态。在且仅在OnData()方 法中,你可以调用GetData()GetData()方法要求来自释放源的实际的数据并把它放入与你的释放目标 对象相关联的数据对象中。GetData()不返回数据对象,所以你通常应该用一个实例变量来包含你的数据对象。下面是关于MyDropTarget.OnData()的 样板代码:

切换行号显示

   1 def OnData(self, x, y, default):
   2     self.GetData()
   3     actual_data = self.data.GetText()
   4     # Do something with the data here... 
   5     return default

OnData()的 返回值应该是要导致操作——你应该返回参数default的值,除非这儿有一个错误并且你需要返回wx.DragNone。 一旦你有了数据,你就可以对它作你想做的。记住,由于OnData()返回的是关于所导致操作的相关信息,而非数据本身,所以如果你想 在别处使用该数据的话,你需要将数据放置在一个实例变量里面(该变量在该方法外仍然可以被访问)。

在释放操作完成或取消后,返回自OnData()的导致操作类型的数据被从DoDragDrop()的 返回,并且释放源的线程将继续进行。

wx.DropTarget类 中有五个On...方法,你可以在你的子类中覆盖它们以在目标被调用时提供自定义的行为。我们已经见过了其中的OnData(), 另外的如下:

OnDrop(x, y) OnEnter(x, y, default) OnDragOver(x, y, default) OnLeave()

其中的参数x, y, defaultOnData()。你不必覆盖这些方 法,但是如果你想在你的应用程序中提供自定义的功能的话,你可以覆盖这些方法。

当鼠标进入释放到的目标区 域时,OnEnter()方法首先被调用。你可以使用该方法来更新一个状态窗口。该方法返回如果释放发生时要执行的操作(通常是default的 值)或wx.DragNone(如果你不接受释放的话)。该方法的返回值被wxPython用来指定当鼠标移动到窗 口上时,哪个图标或光标被用作显示。当鼠标位于窗口中时,方法OnDragOver()接着被调用,它返回所期望的操作或wx.DragNone。 当鼠标被释放并且释放(drop)发生时,OnDrop()方法被调用,并且它默认调用OnData()。 最后,当光标退出窗口时OnLeave()被调用。

与数据对象一同,wxPython提 供了两个预定义的释放到的目标类来涵盖最常见的情况。除了在这些情况中预定义的类会为你处理wx.DataObject,你仍然需要创 建一个子类并覆盖一个方法来处理相关的数据。关于文本,类wx.TextDropTarget提供了可覆盖的方法OnDropText(x, y, data), 你将使用通过覆盖该方法来替代覆盖OnData()。参数x,y是释放到的坐标,参数data是 被释放的字符串,该字符串你可以立即使用面不用必须对数据对象作更多的查询。如果你接受新的文本的话,你的覆盖应该返回True,否则 应返回False。对于文件的释放,相关的预定义的类是wx.FileDropTarget,并且可覆盖的方法是OnDropFiles(x, y, filenames), 参数filenames是被释放的文件的名字的一个列表。另外,必要的时候你可以处理它们,当完成时可以返回TrueFalse

实战释放

例18.3中的代码显示了如何创建一个框架(窗口)用以接受文件的释放。你可以通过从资源管理器或查找窗口拖动一个文件 到该框架(窗口)上来测试例子代码,并观查显示在窗口中的关于文件的信息。图18.3是运行后的结果。

图18.3

w18.3.gif

例18.3 文件释放到的目标的相关代码

切换行号显示

   1 #-*- encoding:UTF-8 -*-
   2 import wx
   3 
   4 class MyFileDropTarget(wx.FileDropTarget):#声明释放到的目标
   5     def __init__(self, window):
   6         wx.FileDropTarget.__init__(self)
   7         self.window = window
   8 
   9     def OnDropFiles(self, x, y, filenames):#释放文件处理函数数据
  10         self.window.AppendText("\n%d file(s) dropped at (%d,%d):\n" %
  11                                (len(filenames), x, y))
  12         for file in filenames:
  13             self.window.AppendText("\t%s\n" % file)
  14 
  15 
  16 class MyFrame(wx.Frame):
  17     def __init__(self):
  18         wx.Frame.__init__(self, None, title="Drop Target",
  19                           size=(500,300))
  20         p = wx.Panel(self)
  21 
  22         # create the controls
  23         label = wx.StaticText(p, -1, "Drop some files here:")
  24         text = wx.TextCtrl(p, -1, "",
  25                            style=wx.TE_MULTILINE|wx.HSCROLL)
  26 
  27         # setup the layout with sizers
  28         sizer = wx.BoxSizer(wx.VERTICAL)
  29         sizer.Add(label, 0, wx.ALL, 5)
  30         sizer.Add(text, 1, wx.EXPAND|wx.ALL, 5)
  31         p.SetSizer(sizer)
  32 
  33         # make the text control be a drop target
  34         dt = MyFileDropTarget(text)#将文本控件作为释放到的目标
  35         text.SetDropTarget(dt)
  36 
  37 
  38 app = wx.PySimpleApp()
  39 frm = MyFrame()
  40 frm.Show()
  41 app.MainLoop()

到目前为止,我们还是局限于对wxPython的预定义的对象的数据传送的讨论。接下来,我们将讨 论如何将你自己的数据放到剪贴板上。

传送自定义对象

使用wxPython的预定义的数据对象,你只能工作于纯文本、位图或文件。而更有创建性的是,你应 该让你自定义的对象能够在应用之间被传送。在这一节,我将给你展示如何给你的wxPython应用程序增加更高级的性能,如传送自定义 的数据对象和以多种格式传送一个对象。

传送自定义的数据对象

尽管文本、位图的数据对象和文件名的列表对于不同的使用已经足够了,但有时你仍然需要传送自定义的对象,如你自己的图形 格式或一个自定义的数据结构。接下来,在保留对你的对象将接受的数据的类型的控制时,我们将涉及传送自定义数据对象的机制。该方法的局限是它只能工作在wxPython内, 你不能使用这个方法来让其它的应用程序去读你的自定义的格式。要将RTF(丰富文本格式)传送给Microsoft Word, 该机制将不工作。

要实现自定义的数据传送,我们将使用wxPython的 类wx.CustomDataObject,它被设计来用于处理任意的数据。wx.CustomDataObject的 构造器如下:

切换行号显示

   1 wx.CustomDataObject(format=wx.FormatInvalid)

参数format技 术上应该是类wx.DataFormat的一个实例,但为了我们的目的,我们可以只给它传递一个字符串,数据类型的责任由wxPython来 考虑。我们只需要这个字符串作为自定义格式的一个标签,以与其它的区分开来。一旦我们有了我们自定义的数据实例,我们就可以使用方法SetData(data)将 数据放入到自定义的数据实例中。参数data必须是一个字符串。下面是一段样板代码:

切换行号显示

   1 data_object = wx.CustomDataObject("MyNiftyFormat")
   2 data = cPickle.dumps(my_object)
   3 data_object.SetData(data)

在这段代码片断之后,你可以将data_object传 递到剪贴板或另一个数据源,以继续数据的传送。

得到自定义对象

要得到该对象,需要执行相同的基本步骤。对于从剪贴板获取,先创建相同格式的一个自定义数据对象,然后得到数据并对得到 的数据进行逆pickle操作(pickle有加工的意思)。

切换行号显示

   1 data_object = wx.CustomDataObject("MyNiftyFormat")
   2 if wx.TheClipboard.Open():
   3     success = wx.TheClipboard.GetData(data_object)
   4     wx.TheClipboard.Close()
   5 if success:
   6     pickled_data = data_object.GetData()
   7     object = cPickle.loads(pickled_data)

拖放工作是类似的。使用已pickle的 数据设置释放源的数据对象,并将设置的数据对象给你的自定义的数据对象,数据的目标在它的OnData()方法中对数据进行逆pickle操 作并把数据放到有用的地方。

创建自定义对象的另一个方法 是建造你自己的wx.DataObject子类。如果你选择这条途径,那么你会希望实现你自己的诸如wx.PyDataObjectSimple(用 于通常的对象),或wx.PyTextDataObjectwx.PyBitmapDataObject, wx.PyFileDataObject的 一个子类。这将使你能够覆盖所有必要的方法。

以多种格式传送对象

使用wxPython的数据对象来用于数据传送的最大好处是,数据对象了解数据格式。一个数据对象甚 至能够用多种的格式来管理相同的数据。例如,你可能希望你自己的应用程序能够接受你的自定义的文本格式对象的数据,但是你仍然希望其它的应用能够以纯文本 的格式接受该数据。

管理该功能的机制是类wx.DataObjectComposite。 目前,我们所见过的所有被继承的数据对象都是wx.DataObjectSimple的子类。wx.DataObjectComposite的 目的是将任意数量的简单数据对象合并为一个数据对象。该合并后的对象能够将它的数据提供给与构成它的任一简单类型匹配的一个数据对象。

要建造一个合成的数据对象,首先要使用一个无参的构造器wx.DataObjectComposite()作 为开始,然后使用Add(data, preferred=False)分别增加简单数据对象。要建造一个合并了你的自定义格式和纯文 本的数据对象,可以如下这样:

切换行号显示

   1 data_object = wx.CustomDataObject("MyNiftyFormat")
   2 data_object.SetData(cPickle.dumps(my_object))
   3 text_object = wx.TextDataObject(str(my_object))
   4 composite = wx.DataObjectComposite()
   5 composite.Add(data_object)
   6 composite.Add(text_object)

此后,将这个合成的对象传递给剪贴板 或你的释放源。如果目标类要求一个使用了自定义格式的对象,那么它接受已pickle的对象。如果它要求纯文本的数据,那么它得到字符 串表达式。

下节内容:我们将给你展示如何使用一个定时器来管理定时事 件。

使用wx.Timer来设置定时事 件

有时你需要让你的应用程序产生基于时间段的事件。要得到这个功能,你可以使用类wx.Timer

产生EVT_TIMER事件

wx.Timer最灵活和最有效的用法是使它产生EVT_TIMER,并将该事 件如同其它事件一样进行绑定。

创建定时器

要创建一个定时器,首先要使用下面的构造器来创建一个wx.Timer的实例。

切换行号显示

   1 wx.Timer(owner=None, id=-1)

其中参数owner是 实现wx.EvtHandler的实例,即任一能够接受事件通知的wxPython控件或其它的东西。参数id用 于区分不同的定时器。如果没有指定id,则wxPython会为你生成一个id号。如果当你创建 定时器时,你不想设置参数ownerid,那么你可以以后随时使用SetOwner(owner=None, id=-1) 方法来设置,它设置同样的两个参数。

绑定定时器

在你创建了定时器之后,你可以如下面一行的代码来在你的事件处理控件中绑定wx.EVT_TIMER事 件。

切换行号显示

   1 self.Bind(wx.EVT_TIMER, self.OnTimerEvent)

如果你需要绑定多个定时器到多个处理 函数,你可以给Bind函数传递每个定时器的ID,或将定时器对象作为源参数来传递。

切换行号显示

   1 timer1 = wx.Timer(self)
   2 timer2 = wx.Timer(self)
   3 self.Bind(wx.EVT_TIMER, self.OnTimer1Event, timer1)
   4 self.Bind(wx.EVT_TIMER, self.OnTimer2Event, timer2)

启动和停止定时器

在定时器事件被绑定后,你所需要做的所有事情就是启动该定时器,使用方法Start(milliseconds=-1, oneShot=False)。其中参数milliseconds是毫秒数。这将在经过milliseconds时 间后,产生一个wx.EVT_TIMER事件。如果milliseconds=-1,那么将使用早先的毫秒数。如果oneShotTrue, 那么定时器只产生wx.EVT_TIMER事件一次,然后定时器停止。否则,你必须显式地使用Stop()方法来停 止定时器。 例18.4使用了定时器机制来驱动一个数字时钟,并 每秒刷新一次显示。

例18.4 一个简单的数字时钟

切换行号显示

   1 #-*- encoding:UTF-8 -*-
   2 import wx
   3 import time
   4 
   5 class ClockWindow(wx.Window):
   6     def __init__(self, parent):
   7         wx.Window.__init__(self, parent)
   8         self.Bind(wx.EVT_PAINT, self.OnPaint)
   9         self.timer = wx.Timer(self)#创建定时器
  10         self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)#绑定一个定时器事件
  11         self.timer.Start(1000)#设定时间间隔
  12 
  13     def Draw(self, dc):#绘制当前时间
  14         t = time.localtime(time.time())
  15         st = time.strftime("%I:%M:%S", t)
  16         w, h = self.GetClientSize()
  17         dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
  18         dc.Clear()
  19         dc.SetFont(wx.Font(30, wx.SWISS, wx.NORMAL, wx.NORMAL))
  20         tw, th = dc.GetTextExtent(st)
  21         dc.DrawText(st, (w-tw)/2, (h)/2 - th/2)
  22 
  23     def OnTimer(self, evt):#显示时间事件处理函数
  24         dc = wx.BufferedDC(wx.ClientDC(self))
  25         self.Draw(dc)
  26 
  27     def OnPaint(self, evt):
  28         dc = wx.BufferedPaintDC(self)
  29         self.Draw(dc)
  30 
  31 class MyFrame(wx.Frame):
  32     def __init__(self):
  33         wx.Frame.__init__(self, None, title="wx.Timer")
  34         ClockWindow(self)
  35 
  36 
  37 app = wx.PySimpleApp()
  38 frm = MyFrame()
  39 frm.Show()
  40 app.MainLoop()

确定当前定时器的状态

你可以使用方法IsRunning()来确定定时器的当前状态,使用方法GetInterval()来 得到当前的时间间隔。如果定时器正在运行并且只运行一次的话,方法IsOneShot()返回True

wx.TimerEvent几乎与它的父类wx.Event是一样的,除了它不 包括wx.GetInterval()方法来返回定时器的时间间隔外。万一你将来自多个定时器的事件绑定给了相同的处理函数,并希望根 据特定的定时器的事件来做不同的动作的话,可使用事件方法GetId()来返回定时器的ID,以区别对待。

学习定时器的其它用法

另一种使用定时器的方法是子类化wx.Timer。在你的子类中你可以覆盖方法Notify()。 在父类中,该方法每次在定时器经过指定的时间间隔后被自动调用,它触发定时器事件。然而你的子类没有义务去触发一个定时器事件,你可以在该Notify()方 法中做你想做的事,以响应定时器的时间间隔。

要在未来某时触发一个特定的 行为,有一个被称为wx.FutureCall的类可以使用。它的构造器如下:

切换行号显示

   1 wx.FutureCall(interval, callable, *args, **kwargs)

一旦它被创建后,wx.FutureCall的 实例将等待interval毫秒,然后调用传递给参数callable的对象,参数*args, **kwargscallable中 的对象所要使用的。wx.FutureCall只触发一次定时事件。

下节内容提示:创建一个多线程的wxPython应用程序

创建一个 多线程的wxPython应用程序

在大多数的GUI应用程序中,在应用程序的后台中长期执行一个处理过程而不干涉用户与应用程序的其它 部分的交互是有好处的。允许后台处理的机制通常是产生一个线程并在该线程中长期执行一个处理过程。对于wxPython的多线程,在这 一节我们有两点需要特别说明。

最重要的一点是,GUI的 操作必须发生在主线程或应用程序的主循环所处的地方中。在一个单独的线程中执行GUI操作对于无法预知的程序崩溃和调试来说是一个好的 办法。基于技术方面的原因,如许多UnixGUI库不是线程安全性的,以及在微软WindowsUI对 象的创建问题,wxPython没有设计它自己的发生在多线程中的事件,所以我们建议你也不要尝试。

上面的禁令包括与屏幕交互的任何项目,尤其包括wx.Bitmap对象。

对于wxPython应用程序,关于所有UI的更新,后台线程只负责发送消息给UI线 程,而不关心GUI的更新。幸运的是,wxPython没有强制限定你能够有的后台线程的数量。

在这一节,我们将关注几个wxPython中实现多线程的方法。最常用的技术是使用wx.CallAfter()函 数,一会我们会讨论它。然后,我们将看一看如何使用Python的队列对象来设置一个并行事件队列。最后,我们将讨论如何为多线程开发 一个定制的解决方案。

使用全局函数 wx.CallAfter()

例18.5显示了一个使用线程的例子,它使用了wxPython的全局函数wx.CallAfter(), 该函数是传递消息给你的主线程的最容易的方法。wx.CallAfter()使得主线程在当前的事件处理完成后,可以对一个不同的线程 调用一个函数。传递给wx.CallAfter()的函数对象总是在主线程中被执行。

图18.4显示了多线程窗口的运行结果。

图18.4

w18.4.gif

例18.5显示了产生图18.4的代码

例18.5 使用wx.CallAfter()来传递消息给主线程的一个线程例子

切换行号显示

   1 #-*- encoding:UTF-8 -*-
   2 import wx
   3 import threading
   4 import random
   5 
   6 class WorkerThread(threading.Thread):
   7     """
   8     This just simulates some long-running task that periodically sends
   9     a message to the GUI thread.
  10     """
  11     def __init__(self, threadNum, window):
  12         threading.Thread.__init__(self)
  13         self.threadNum = threadNum
  14         self.window = window
  15         self.timeToQuit = threading.Event()
  16         self.timeToQuit.clear()
  17         self.messageCount = random.randint(10,20)
  18         self.messageDelay = 0.1 + 2.0 * random.random()
  19 
  20     def stop(self):
  21         self.timeToQuit.set()
  22 
  23     def run(self):#运行一个线程
  24         msg = "Thread %d iterating %d times with a delay of %1.4f\n" \
              % (self.threadNum, self.messageCount, self.messageDelay)
  25         wx.CallAfter(self.window.LogMessage, msg)
  26 
  27         for i in range(1, self.messageCount+1):
  28             self.timeToQuit.wait(self.messageDelay)
  29             if self.timeToQuit.isSet():
  30                 break
  31             msg = "Message %d from thread %d\n" % (i, self.threadNum)
  32             wx.CallAfter(self.window.LogMessage, msg)
  33         else:
  34             wx.CallAfter(self.window.ThreadFinished, self)
  35 
  36 
  37 
  38 class MyFrame(wx.Frame):
  39     def __init__(self):
  40         wx.Frame.__init__(self, None, title="Multi-threaded GUI")
  41         self.threads = []
  42         self.count = 0
  43 
  44         panel = wx.Panel(self)
  45         startBtn = wx.Button(panel, -1, "Start a thread")
  46         stopBtn  = wx.Button(panel, -1, "Stop all threads")
  47         self.tc = wx.StaticText(panel, -1, "Worker Threads: 00")
  48         self.log = wx.TextCtrl(panel, -1, "",
  49                                style=wx.TE_RICH|wx.TE_MULTILINE)
  50 
  51         inner = wx.BoxSizer(wx.HORIZONTAL)
  52         inner.Add(startBtn, 0, wx.RIGHT, 15)
  53         inner.Add(stopBtn, 0, wx.RIGHT, 15)
  54         inner.Add(self.tc, 0, wx.ALIGN_CENTER_VERTICAL)
  55         main = wx.BoxSizer(wx.VERTICAL)
  56         main.Add(inner, 0, wx.ALL, 5)
  57         main.Add(self.log, 1, wx.EXPAND|wx.ALL, 5)
  58         panel.SetSizer(main)
  59 
  60         self.Bind(wx.EVT_BUTTON, self.OnStartButton, startBtn)
  61         self.Bind(wx.EVT_BUTTON, self.OnStopButton, stopBtn)
  62         self.Bind(wx.EVT_CLOSE,  self.OnCloseWindow)
  63 
  64         self.UpdateCount()
  65 
  66     def OnStartButton(self, evt):
  67         self.count += 1
  68         thread = WorkerThread(self.count, self)#创建一个线程
  69         self.threads.append(thread)
  70         self.UpdateCount()
  71         thread.start()#启动线程
  72 
  73     def OnStopButton(self, evt):
  74         self.StopThreads()
  75         self.UpdateCount()
  76 
  77     def OnCloseWindow(self, evt):
  78         self.StopThreads()
  79         self.Destroy()
  80 
  81     def StopThreads(self):#从池中删除线程
  82         while self.threads:
  83             thread = self.threads[0]
  84             thread.stop()
  85             self.threads.remove(thread)
  86 
  87     def UpdateCount(self):
  88         self.tc.SetLabel("Worker Threads: %d" % len(self.threads))
  89 
  90     def LogMessage(self, msg):#注册一个消息
  91         self.log.AppendText(msg)
  92 
  93     def ThreadFinished(self, thread):#删除线程
  94         self.threads.remove(thread)
  95         self.UpdateCount()
  96 
  97 
  98 app = wx.PySimpleApp()
  99 frm = MyFrame()
 100 frm.Show()
 101 app.MainLoop()

上面这个例子使用了Pythonthreading模 块。上面的代码使用wx.CallAfter(func,*args)传递方法给主线程。这将发送一个事件给主线 程,之后,事件以标准的方式被处理,并触发对func(*args)的调用。因些,在这种情况中,线程在它的生命周 期期间调用LogMessage(),并在线程结束前调用ThreadFinished()

使用队列对象管理线程的通 信

尽管使用CallAfter()是管理线程通信的最简单的方法,但是它并不是唯一的机制。你可以使用Python的 线程安全的队列对象去发送命令对象给UI线程。这个UI线程应该在wx.EVT_IDLE事件的 处理函数中写成需要接受来自该队列的命令。

本质上,你要为线程通信设置 一个并行的事件队列。如果使用这一方法,那么工作线程在当它们增加一个命令对象到队列时,应该调用全局函数wx.WakeUpIdle()以 确保尽可能存在在一个空闲事件。这个技术比wx.CallAfter()更复杂,但也更灵活。特别是,这个机制可以帮助你在后台线程间 通信,虽然所有的GUI处理仍在主线程上。

开发你自已的解决方案

你也可以让你自己的工作线程创建一个wxPython事件(标准的或自定义的),并使用全局函数wx.PostEvent(window, event)将 它发送给UI线程中的一个特定的窗口。该事件被添加到特定窗口的未决事件队列中,并且wx.WakeUpIdle自 动被调用。这条道的好处是事件将遍历的wxPython事件设置,这意味你将自由地得到许多事件处理能力,坏处是你不得不自已管理所有 的线程和wx.CallAfter()函数所为你做的事件处理。

本章小结

1、拖放和剪贴板事件是非常相似的,两者都使用了wx.DataObject来作为数据格式的媒介。 除了可以创建自定义的格式以外,还存在着默认的数据对象,包括文本,文件和位图。在剪贴板的使用中,全局对象wx.TheClipboard管 理数据的传送并代表底层系统的剪贴板。

2、对于拖放操作,拖动源和 拖动到的目标一起工作来管理数据传送。拖动源事件被阻塞直到拖动到的目标作出该拖动操作是否有效的判断。

3、类wx.Timer使你能够设置定时的事件。

4、线程在wxPython是可以实现的,但时确保所有的GUI活动发生在主线程 中是非常重要的。你可以使用函数wx.CallAfter()来管理内部线程的通信问题。

网站建设公司

python

wxPython的打印构架

Tags:

本章内容

  • wxPython打 印
  • 创建和显示打印对话框
  • 创建和显示页面设置对话框
  • 在你的程序中执行打印
  • 执行一个打印预览

在第16章中,我 们已经关注了wxPython的一打印方法:使用wx.HtmlEasyPrinting。如果你用该方法打印HTML(或 某些容易转换为HTML的文件)的话,这个方法将会工作的很好,但是要作为一个完善打印办法还是不够的。在wxPython中 还有一个更为通用的打印构架,你可以用它来打印你想打印的任何东西。基本上,该构架使你能够使用设备上下文和绘制操作来执行打印。你也可以创建打印预览。

本章将讨论该构架中最重要的类:wx.Printout, 它管理实际的图片部分。打印输出实例可以由一个代表打印机的wx.Printer对象或用于打印预览的wx.PrintPreview对 象来管理。多们也将讨论几个管理与打印相关的数据的类,以及用来显示信息给用户的对话框。

如何用wxPython打印?

我们将以类wx.Printout作为开始。首先你要创建你自定义的wx.Printout的 子类。接着你要覆盖wx.Printout的方法以定义你自定义的打印行为。wx.Printout有7个你可以覆 盖以自定义打印行为的方法。这些方法在一个打印会话过程期间被wxPython自动调用。图17.1其中的六个方法,它们被特定的事件 触发。在大多数情况下,你不需要全部覆盖它们。

图17.1

w17.1.gif

理解打印输出的生命周期

你通过创建一个你的打印输出对象的实例和一个类wx.Printer的实例启动一个打印会话:

wx.Printer(data=None)

可选的data参 数是wx.PrintDialogData的一个实例。要开始实际的打印,需要调用wx.PrinterPrint(parent, printout, prompt=True)方 法。参数parent是父窗口(它被用作对话框的窗口中)。参数printout是你的wx.Printout实 例。如果参数promptTrue,那么在打印之前,wxPython将显示打印对话框,否则 不显示。

Print()方法开始后,它调用wx.Printout的 第一个可被覆盖的方法OnPreparePrint()OnPreparePrint()方法在wx.Printout实 例做任何其它的事之前被确保调用,因此该方法是放置收集你的数据或做那些必须在打印开始之前所要做的计算的一个好的地方。实际的打印使用OnBeginPrinting()方 法开始,你可以对该方法进行覆盖,以自定主你想要的行为——默认情况下,该方法什么也不做。OnBeginPrinting()在整个 打印会话中只会被调用一次。

你希望打印的文档的每个单独的 拷贝触发对OnBeginDocument(startPage, endPage)的一个调用,其中参数startPage, endPage告 诉wxPython打印的起始页和最后一页。这两个参数都应该指定。如果你想覆盖这个方法,那么你必须调用它的基类的方法,因为基类的 方法要做一些重要的工作(如调用wx.DC.StartDoc())。在wxPython中,你可以使用base_OnBeginDocument(startPage, endPage)来 调用其父类的方法。如果OnBeginDocument返回False,那么将取消打印工作。

你最有可能去覆盖的方法是OnPrintPage(pageNum),该方法是你放置关于每一页的 绘制命令的地方。参数pageNum是要打印的页的页码。在这个方法中,你调用GetDC()GetDC()根 据你当前的系统平台返回一个适当的设备上下文。对于实际的打印,如果你是在一个微软的Windows系统上的话,那么GetDC()返 回的是类wx.PrinterDC的实例。对于其它的系统,返回的是类wx.PostScriptDC的实例。如果 你是处在一个打印预览操作中,那么对于任何的操作系统,GetDC()返回的都是一个wx.MemoryDC。一旦 你有了设备上下文,你就可以做你想做的设备上下文绘制操作,并且它们将被打印或预览。

在一个文档的副本打印结束后,一个OnEndDocument()调用被触发。另外,如果你要覆盖OnEndDocument()方 法,那么你必须调用其基类的方法base_OnEndDocument()base_OnEndDocument()将 调用wx.DC.EndDoc()方法。当你的所有的副本被打印完后,OnEndPrinting()方法被调用, 这样就结束了打印会话。

wx.Printout还 有另一个可被覆盖的方法:HasPage(pageNum)。该方法通常需要被覆盖,它被打印架构用于循环控制。如果参数pageNum存 在于文档中,那么该方法返回True,否则返回False

实战打印构架

下面我们将通过一个例子来展示打印构架实际上是如何工作的。这个例子由一个简单的用于打印文本文件的构架组成,并且应用 程序让你能够键入简单的文本。图17.2显示了这个应用程序的结果。

图17.1

w17.2.gif

例17.1显示了我们已经讨论过的打印构架和我们将要接触的打印对话框机制。

例17.1 打印构架的一个较长的例子

切换行号显示

   1 import wx
   2 import os
   3 
   4 FONTSIZE = 10
   5 
   6 class TextDocPrintout(wx.Printout):
   7     """
   8     A printout class that is able to print simple text documents.
   9     Does not handle page numbers o titles, and it assumes that no
  10     lines are longer than what will fit within the page width.  Those
  11     features are left as an exercise for the reader. ;-) 
  12     """
  13     def __init__(self, text, title, margins):
  14         wx.Printout.__init__(self, title)
  15         self.lines = text.split('\n')
  16         self.margins = margins
  17 
  18 
  19     def HasPage(self, page):
  20         return page  = self.numPages
  21 
  22     def GetPageInfo(self):
  23         return (1, self.numPages, 1, self.numPages)
  24 
  25 
  26     def CalculateScale(self, dc):
  27         # Scale the DC such that the printout is roughly the same as
  28         # the screen scaling.
  29         ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
  30         ppiScreenX, ppiScreenY = self.GetPPIScreen()
  31         logScale = float(ppiPrinterX)/float(ppiScreenX)
  32 
  33         # Now adjust if the real page size is reduced (such as when
  34         # drawing on a scaled wx.MemoryDC in the Print Preview.)  If
  35         # page width == DC width then nothing changes, otherwise we
  36         # scale down for the DC.
  37         pw, ph = self.GetPageSizePixels()
  38         dw, dh = dc.GetSize()
  39         scale = logScale * float(dw)/float(pw)
  40 
  41         # Set the DC's scale.
  42         dc.SetUserScale(scale, scale)
  43 
  44         # Find the logical units per millimeter (for calculating the
  45         # margins)
  46         self.logUnitsMM = float(ppiPrinterX)/(logScale*25.4)
  47 
  48 
  49     def CalculateLayout(self, dc):
  50         # Determine the position of the margins and the
  51         # page/line height
  52         topLeft, bottomRight = self.margins
  53         dw, dh = dc.GetSize()
  54         self.x1 = topLeft.x * self.logUnitsMM
  55         self.y1 = topLeft.y * self.logUnitsMM
  56         self.x2 = dc.DeviceToLogicalXRel(dw) - bottomRight.x * self.logUnitsMM
  57         self.y2 = dc.DeviceToLogicalYRel(dh) - bottomRight.y * self.logUnitsMM
  58 
  59         # use a 1mm buffer around the inside of the box, and a few
  60         # pixels between each line
  61         self.pageHeight = self.y2 - self.y1 - 2*self.logUnitsMM
  62         font = wx.Font(FONTSIZE, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
  63         dc.SetFont(font)
  64         self.lineHeight = dc.GetCharHeight()
  65         self.linesPerPage = int(self.pageHeight/self.lineHeight)
  66 
  67 
  68     def OnPreparePrinting(self):
  69         # calculate the number of pages
  70         dc = self.GetDC()
  71         self.CalculateScale(dc)
  72         self.CalculateLayout(dc)
  73         self.numPages = len(self.lines) / self.linesPerPage
  74         if len(self.lines) % self.linesPerPage != 0:
  75             self.numPages += 1
  76 
  77 
  78     def OnPrintPage(self, page):
  79         dc = self.GetDC()
  80         self.CalculateScale(dc)
  81         self.CalculateLayout(dc)
  82 
  83         # draw a page outline at the margin points
  84         dc.SetPen(wx.Pen("black", 0))
  85         dc.SetBrush(wx.TRANSPARENT_BRUSH)
  86         r = wx.RectPP((self.x1, self.y1),
  87                       (self.x2, self.y2))
  88         dc.DrawRectangleRect(r)
  89         dc.SetClippingRect(r)
  90 
  91         # Draw the text lines for this page
  92         line = (page-1) * self.linesPerPage
  93         x = self.x1 + self.logUnitsMM
  94         y = self.y1 + self.logUnitsMM
  95         while line   (page * self.linesPerPage):
  96             dc.DrawText(self.lines[line], x, y)
  97             y += self.lineHeight
  98             line += 1
  99             if line  = len(self.lines):
 100                 break
 101         return True
 102 
 103 
 104 class PrintFrameworkSample(wx.Frame):
 105     def __init__(self):
 106         wx.Frame.__init__(self, None, size=(640, 480),
 107                           title="Print Framework Sample")
 108         self.CreateStatusBar()
 109 
 110         # A text widget to display the doc and let it be edited
 111         self.tc = wx.TextCtrl(self, -1, "",
 112                               style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
 113         self.tc.SetFont(wx.Font(FONTSIZE, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
 114         filename = os.path.join(os.path.dirname(__file__), "sample-text.txt")
 115         self.tc.SetValue(open(filename).read())
 116         self.tc.Bind(wx.EVT_SET_FOCUS, self.OnClearSelection)
 117         wx.CallAfter(self.tc.SetInsertionPoint, 0)
 118 
 119         # Create the menu and menubar
 120         menu = wx.Menu()
 121         item = menu.Append(-1, "Page Setup...\tF5",
 122                            "Set up page margins and etc.")
 123         self.Bind(wx.EVT_MENU, self.OnPageSetup, item)
 124         item = menu.Append(-1, "Print Setup...\tF6",
 125                            "Set up the printer options, etc.")
 126         self.Bind(wx.EVT_MENU, self.OnPrintSetup, item)
 127         item = menu.Append(-1, "Print Preview...\tF7",
 128                            "View the printout on-screen")
 129         self.Bind(wx.EVT_MENU, self.OnPrintPreview, item)
 130         item = menu.Append(-1, "Print...\tF8", "Print the document")
 131         self.Bind(wx.EVT_MENU, self.OnPrint, item)
 132         menu.AppendSeparator()
 133         item = menu.Append(-1, "E ", "Close this application")
 134         self.Bind(wx.EVT_MENU, self.OnExit, item)
 135 
 136         menubar = wx.MenuBar()
 137         menubar.Append(menu, " ")
 138         self.SetMenuBar(menubar)
 139 
 140         # initialize the print data and set some default values
 141         self.pdata = wx.PrintData()
 142         self.pdata.SetPaperId(wx.PAPER_LETTER)
 143         self.pdata.SetOrientation(wx.PORTRAIT)
 144         self.margins = (wx.Point(15,15), wx.Point(15,15))
 145 
 146 
 147     def OnExit(self, evt):
 148         self.Close()
 149 
 150 
 151     def OnClearSelection(self, evt):
 152         evt.Skip()
 153         wx.CallAfter(self.tc.SetInsertionPoint,
 154                      self.tc.GetInsertionPoint())
 155 
 156 
 157     def OnPageSetup(self, evt):
 158         data = wx.PageSetupDialogData()
 159         data.SetPrintData(self.pdata)
 160 
 161         data.SetDefaultMinMargins(True)
 162         data.SetMarginTopLeft(self.margins[0])
 163         data.SetMarginBottomRight(self.margins[1])
 164 
 165         dlg = wx.PageSetupDialog(self, data)
 166         if dlg.ShowModal() == wx.ID_OK:
 167             data = dlg.GetPageSetupData()
 168             self.pdata = wx.PrintData(data.GetPrintData()) # force a copy
 169             self.pdata.SetPaperId(data.GetPaperId())
 170             self.margins = (data.GetMarginTopLeft(),
 171                             data.GetMarginBottomRight())
 172         dlg.Destroy()
 173 
 174 
 175     def OnPrintSetup(self, evt):
 176         data = wx.PrintDialogData(self.pdata)
 177         dlg = wx.PrintDialog(self, data)
 178         dlg.GetPrintDialogData().SetSetupDialog(True)
 179         dlg.ShowModal();
 180         data = dlg.GetPrintDialogData()
 181         self.pdata = wx.PrintData(data.GetPrintData()) # force a copy
 182         dlg.Destroy()
 183 
 184 
 185     def OnPrintPreview(self, evt):
 186         data = wx.PrintDialogData(self.pdata)
 187         text = self.tc.GetValue()
 188         printout1 = TextDocPrintout(text, "title", self.margins)
 189         printout2 = None #TextDocPrintout(text, "title", self.margins)
 190         preview = wx.PrintPreview(printout1, printout2, data)
 191         if not preview.Ok():
 192             wx.MessageBox("Unable to create PrintPreview!", "Error")
 193         else:
 194             # create the preview frame such that it overlays the app frame
 195             frame = wx.PreviewFrame(preview, self, "Print Preview",
 196                                     pos=self.GetPosition(),
 197                                     size=self.GetSize())
 198             frame.Initialize()
 199             frame.Show()
 200 
 201 
 202     def OnPrint(self, evt):
 203         data = wx.PrintDialogData(self.pdata)
 204         printer = wx.Printer(data)
 205         text = self.tc.GetValue()
 206         printout = TextDocPrintout(text, "title", self.margins)
 207         useSetupDialog = True
 208         if not printer.Print(self, printout, useSetupDialog) \
           and printer.GetLastError() == wx.PRINTER_ERROR:
 209             wx.MessageBox(
 210                 "There was a problem printing.\n"
 211                 "Perhaps your current printer is not set correctly?",
 212                 "Printing Error", wx.OK)
 213         else:
 214             data = printer.GetPrintDialogData()
 215             self.pdata = wx.PrintData(data.GetPrintData()) # force a copy
 216         printout.Destroy()
 217 
 218 
 219 app = wx.PySimpleApp()
 220 frm = PrintFrameworkSample()
 221 frm.Show()
 222 app.MainLoop()

例17.2中的打印输出类能够打印简 单的文本文档,但是不能处理页码或标题,并且它创假设了行的宽度没有超过页面的宽度。对于例子的完善就留给读者作为一个练习。

上面最重要的代码片断是在构架的OnPreparePrinting()OnPrintPage()以 及示例窗口的OnPrint()方法中。

使用wx.Printout的方法工作

wx.Printout中有几个get*方法,它们使你能够获取当前打印环境的 有关信息。表17.1列出了这些方法。

表17.1 wx.Printout的信息获取方法

GetDC() 该方法返回关于打印机或打印预览的用于绘制文档的设备上下文。
GetPageInfo() 返回一个含有四个元素的元组(minPage, maxPage, pageFrom, pageTo)minPage, maxPage分 别是所允许的最小和最大页码,默认是1和32000。pageFrom, pageTo是必须被打印的范围,默认为1。你可以在你的子 类中覆盖这个方法。
GetPageSizeMM() 返回包含一个页面的宽度和高度的一个(w, h)元组,以毫米为单位。
GetPageSizePixels() 返回一个页面的宽度和高度的一个(w, h)元组,以像素为单位。如果打印输出被 用于打印预览,那么像素数将反应当前的缩放比列,意思就是说像素将会随缩放比列而变。
GetPPIPrinter() 返回当前打印机在垂直和水平方向上每英寸的像素的一个(w, h)元组。在预览 中,这个值也是始终一致的,即使打印预览的缩放比列变化了。
GetPPIScreen() 返回当前屏幕在垂直和水平方向上每英寸的像素的一个(w, h)元组。在预览中, 这个值也是始终一致的,即使打印预览的缩放比列变化了。
GetTitle() 返回打印输出的标题。

在后面的几节中,我们将讨论如何呈现 打印对话框给用户。

如何显示打印对话框?

诸如要打印那些面面,要打印多少副本这些关于打印工作的数据是由标准的打印对话框来管理的。打印对话框是与字体和颜色对 话框类似的,wxPython中的打印对话框实例仅仅是对本地控件和一个储存了对话框数据的分离的数据对象的简单封装。

创建一个打印对话框

图17.3显示了打印设置对话框的样例。

图17.3

w17.3.gif

这里的对话框是类wx.PrintDialog的一个实例,你可以使用下面的构造函数来得到:

wx.PrintDialog(parent, data=None)

其中,参数parent是对话框的父框架,参数data是一个预先存在的wx.PrintDialogData实 例,它用于对话框的初始数据。

使用方法

一旦你有了打印对话框,你就可以使用标准的ShowModal()方法来显示它,ShowModal()方 法将根据用户关闭对话框的方式而返回wx.ID_OKwx.ID_CANCEL。 在你关闭了对话框之后,你可以使用GetPrintDialogData()方法来得到用户输入的数据。你也可以使用GetPrintDC()方 法得到与数据相关联的打印机的设备上下文,如果还没有内容被创建,那么GetPrintDC()方法返回None。 例17.1中的OnPrintSetup()方法显示了实际上对话框是如何被获取的。

使用属性

这个数据对象本身有几个属性,其中的一个是对wx.PrintData类 型的一个对象的引用,wx.PrintData有更多的属性。你可以使用构造函数wx.PrintDialogData()来 创建你的wx.PrintDialogData对象。这使得你能够在打开对话框之前预设属性。

wx.PrintDialogData对象有四个属性用于控制打印对话框的各个部分的有效性。方法EnableHelp(enable)用 于开关帮助性能。至于对话框的其它部分,EnablePageNumbers(enable)与页面数量输入框相关,EnablePrintToFile(enable)管 理实际的打印按钮,EnableSelection(enable)在打印所有和仅打印被选项之间作切换。

表17.2显示了对话框数据对象的其它属性,它们使你能够管理有关打印请求的信息。

表17.2 wx.PrintDialogData的 属性

GetAllPages() 如果用户选择了打印整个文档这一选项,则返回True
SetCollate(flag)
GetCollate() 如果用户选择了核对打印的页,则返回True
SetFromPage(page)
GetFromPage() 如果用户选择从某一页打印,那么方法返回打印的第一页的整数页码。
SetMaxPage(page)
GetMaxPage() 返回文档中最大的页码。
SetMinPage(page)
GetMinPage() 返回文档中最小的页码。
SetNoCopies()
GetNoCopies() 返回用户选择要打印的副本的数量。
SetPrintData(printData)
GetPrintData() 返回与对话框相关联的wx.PrintData对象。
SetPrintToFile(flag)
GetPrintToFile() 如果用户已经选择了打印到一个文件这一项,那么返回True。“打印到文件”这一 机制由wxPython管理。
SetSelection(flag)
GetSelection() 如果用户已经选择了只打印当前的选择这一项,那么返回True
SetToPage(page)
GetToPage() 如果用户指定了一个范围,那么返回打印的最后一页的页码。

GetPrintData()方法返回的wx.PrintData实例提供了 有关打印的更进一步的信息。通常这些信息是在你的打印对话框的打印设置子对话框中的。表17.3列出了wx.PrintData对象的 属性。

表17.3 wx.PrintData的属性

SetColour(flag)
GetColour() 如果当前的打印是用于颜色打印的,那么返回True
SetDuplex(mode)
GetDuplex() 返回当前关于双面打印的设置。值可以是wx.DUPLEX_SIMPLE(非双面 打印),wx.DUPLEX_HORIZONTAL(横向双面打印),wx.DUPLEX_VERTICAL(纵向 双面打印)。
SetOrientation(orientation)
GetOrientation() 返回纸张的打印定位(肖像或风景)。值可以是wx.LANDSCAPEwx.PORTRAIT
SetPaperId(paperId)
GetPaperId() 返回匹配纸张类型的标识符。通常的值有wx.PAPER_LETTER, wx.PAPER_LEGAL, wx.PAPER_A4。 完整的页面(纸张)ID的列表见wxWidgets文档。
SetPrinterName(printerName)
GetPrinterName() 返回被系统引用的当前打印机的名字。如果该值为空字符串,那么默认打印机被使用。
SetQuality(quality)
GetQuality() 返回打印机的当前品质值。set*方法仅接受如下取值 wx.PRINT_QUALITY_DRAFT, wx.PRINT_QUALITY_HIGH, wx.PRINT_QUALITY_MEDIUM, wx.PRINT_QUALITY_LOWget* 方法将返回上面的这些值之一,或一个代表每英寸点数设置的正整数。

如何显示页面设置对话框?

图17.4显示了页面设置对话框是如何让用户来设置与页面尺寸相关的数据的。

图17.4

w17.3.gif

创建页面设置对话框

你可以通过实例化一个wx.PageSetupDialog类来创建一个页面设置对话框。

wx.PageSetupDialog(parent, data=None)

参数parent是新的对话框的父窗口。参数datawx.PageSetupDialogData的 一个实例默认为None。一旦页面设置对话框被创建了,那么这个对话框的行为就和其它任何模式对话框一样,并且你可以使用ShowModal()来 显示它。通常,返回值表明了用户是否是使用wx.ID_OKwx.ID_CANCEL按钮关闭的对话框窗口。在对 话框关闭后,你可以通过调用GetPageSetupDialogData()来取得对数据对象的访问, GetPageSetupDialogData()返 回类wx.PageSetupDialogData的一个实例。

使用页面设置属性工作

wx.PageSetupDialogData类有几个必须与页面设置一起使用的属性。表17.4展 示了控制对话框自身显示的属性。除非有其它的指定,否则所有这些属性都默认为True

表17.4 wx.PageSetupDialogData的 对话框控制属性

GetDefaultMinMargins()
SetDefaultMinMargins(flag) 如果这个属性为True,并且你是在微软的Windows系统 上,那么页面设置将使用默认打印机的当前属性作为默认认的最小化页边距。否则,它将使用系统默认值。
GetDefaultInfo()
SetDefaultInfo(flag) 如果这个属性为True,并且你是在微软的Windows系统 上,那么这个页面设置对话框不会被显示。替而代之,当前打印机的所有默认值都将被放入数据对象。
EnableHelp(flag)
GetEnableHelp() 如果为True,那么对话框的帮助部分是有效的。
EnableMargins(flag)
GetEnableMargins() 如果为True,那么对话框的用于调整页边距的部分是有效的。
EnableOrientation(flag)
GetEnableOrientation() 如果为True,那么对话框的用于改变页面定位的部分是有效的。
EnablePaper(flag)
GetEnablePaper() 如果为True,那么对话框的用于允许用户改变页面(纸张)类型的部分是效的。
EnablePrinter(flag)
GetEnablePrinter() 如果为True,那么允许用户设置打印机的按钮是有效的。

表17.5显示了wx.PageSetupDialogData类的附加的属性,这些属性用于控制 页面的边距和尺寸。

表17.5 wx.PageSetupDialogData的页边距和尺寸属性

GetMarginTopLeft()
SetMarginTopLeft(pt) get*方法返回一个wx.Point,其中的值x是当前的左 边距,y是当前的上边距。set*方法允许你使用一个wx.Point或一个Python元组来 改变这些值。
GetMarginBottomRight()
SetMarginBottomRight(pt) get*方法返回一个wx.Point,其中的值x是当前的右 边距,y是当前的下边距。set*方法允许你使用一个wx.Point或一个Python元组来 改变这些值。
GetMinMarginTopLeft()
SetMinMarginTopLeft(pt) GetMarginTopLeft()中的一样,只是值是所允许的最小左边距和 上边距。
GetMinMarginBottomRight()
SetMinMarginBottomRight(pt) GetMarginBottomRight()中的一样,只是值是所允许的最小 右边距和下边距。
GetPaperId()
SetPaperId(id) 返回关于当前页面类型的wxPython标识符。同wx.PrinterData的 属性。
GetPaperSize()
SetPaperSize(size) get*方法返回包含页面的水平和坚直方向尺寸的一个wx.Size实 例。单位是毫米。
GetPrintData()
SetPrintData(printData) get*方法返回与当前打印会话相关的wx.PrintData实 例。

到目前为止,我们已经讨论了所有关于 数据对话框的整改,下面我们将重点放在打印上面。

如何打印?

到目前为止,我们已经见过了打印构架的所有部分,现是我们打印一些东西的时候了。实际的打印部分是由wx.Printer类 的一个实例来控制的。与已经说明的其它部分相比,打印并不更简单。接下来,我们将对在例17.1中的OnPrint()中的步骤作介 绍。

第一步 按顺序得到你的所有数据

这至少应该包括带有打印机 命令的wx.Printout对象,通常也要包括一个wx.PrintDialogData实例。

第二步 创建一个wx.Printer实例

创建该实例,要使用构造器wx.Printer(data=None)。 可选参数data是一个wx.PrintDialogData实例。该数据控制打印,通常,你会想使用它。

第三步 使用wx.PrinterPrint ()方法打印

Print()方 法如下:

切换行号显示

   1 Print(parent, printout, prompt=True)

其中参数parent是 当打印时所触发的对话框的父窗口。printout是用于打印的wx.Printout对象。如果参数promptTrue, 那么在打印之前显示打印对话框,否则将立即;启动打印。

如果打印成功,则Print()方 法返回True。你能够调用GetLastError()方法来得到下列常量之一:wx.PRINTER_CANCELLED(如 果失败是由于用户取消了打印所引起的),wx.PRINTER_ERROR(如果失败在打印期间由打印自身所引起的),或wx.PRINTER_NO_ERROR(如 果Print()返回True且没有错误发生)。

这儿还有另外两个你可以使用一个wx.Printer实例做的事:

* 你可以使用CreateAbortWindow(parent,printout)来显示中止 对话框,其中参数parentprintoutPrint()方法中的。如果用户已经中止打 印任务,你能够通过调用Abort()来发现,该方法在这种情况下返回True

* 你可以使用PrintDialog(parent)来显式地显示打印对话框,并且你可以使用GetPrintDialogData()来 得到活动的打印数据对象。

如何实现一个打印预览?

使用设备上下文的一个好处就是很容易管理打印预览,你可以使用一个屏幕设备上下文来代替打印机设备上下文。接下来的三个 部分将讨论打印预览的过程。

第一步 创建预览实例

在一个打印预览中的第一步 是创建类wx.PrintPreview的一个实例,wx.PrintPreview类似wx.Printer。 构造器如下:

切换行号显示

   1 wx.PrintPreview(printout, printoutForPrinting, data=None)

其中参数printout是 一个wx.Printout对象,用于管理预览。参数printoutForPrinting是另一个wx.Printout对 象。如果它不是None,那么当显示的时候,该打印预览窗口包含一Print按钮,该按钮启动打印。printoutForPrinting用 于实际的打印。如果参数printoutForPrintingNone,那么Print按钮 不显示。当然,你可以传递同一个实例或你的自定义打印输出类的相同版本的两个实例给参数printoutprintoutForPrinting。 参数data可以是一个wx.PrintData对象或一个wx.PrintDialogData对 象。如果参数data指定了的话,那么它被用于控制该打印预览。在例17.1中,我们显示了一个在OnPrintPreview()方 法中使用打印预览的例子。

第二步 创建预览框架

一旦你有了你的wx.PrintPreview, 你就需要一框架以在其中观看你的wx.PrintPreview。该框架由类wx.PreviewFrame提供,wx.PreviewFramewx.Frame的 一个子类,wx.Frame为预览提供基本的用户交互控件。wx.PreviewFrame的构造器如下:

切换行号显示

   1 wx.PreviewFrame(preview, parent, title, pos=wx.DefaultPosition,
   2         size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
   3         name="frame")

其中真正有意义的参数是preview, 它是要被预览的wx.PrintPreview实例。其它的参数都是标准的wx.Frame中的。wx.PreviewFrame不 定义任何自定义的样式或事件。

第三步 初始化框架

在你显示你的wx.PreviewFrame之前,你需要调用Initialize()方 法,该方法创建窗口的内部的部件并做其它的内部的计算。一旦你Show()了该框架,那么如果你想再改变预览窗口的感观,你可以使用考 虑CreateControlBar()CreateCanvas()方法,它们分别创建类wx.PreviewControlBarwx.PreviewCanvas的 对象。覆盖这些方法以创建你自己的画布(canvas)和/或控制栏对象,使得你能够定制你的打印预览窗口的感观。

本章小结

1、这是wxPython中的一个通用的打印构架,它不仅可以打印HTML,还可 以打印任何能够被绘制到设备上下文的东西。这个架构中的主要的类是wx.Printout,但是wx.Printerwx.PrintPreview也 是重要的。

2、wx.Printout类管理图形打 印的细节,并且它包含几个可以被覆盖来定制打印会话期间的行为和使用的数据的方法。打印发生在OnPrintPage()方法期间。

3、用于打印机设置和页面设置的标准的对话框是可以从wxPython来访问的。打印机设置对话框 是wx.PrintDialog的一个实例,页面设置对话框是wx.PageSetupDialog的一个实例。这 两个对话框都有相关的数据类,数据类使你的程序可以处理所有显示在对话框中的值。

4、一旦有了数据,那么实际将数据传送给打印机则是wx.Printer类的相对简单的应用。你可以 使用wx.PrintPreview类来管理一个打印预览会话,该类包括一个打印预览框架,和根据该框架指定通常打印行为的选项。

网站建设公司

python

wxPython在你的应用程序中加入HTML

Tags:

本章内容:

  • wxPython窗口中显示HTML
  • 处理和打印HTML窗口
  • 使用HTML分析器(parser)
  • 支持新的标记和其它的文件格式
  • HTML中使用控件

HTML最初是打算被作为超文本系统使用的一个简单的语义标记来使用的。迄今为止,HTML已 经变得更加的复杂和被广泛使用。HTML文档标记已经被证明在网页浏览器之外也是有用的。目前HTML文档标记通常 被用于文本标记(如在文本控件中),或用于管理一系列的超链接页面(帮助系统中)。在wxPython中,有许多专用于处理你的HTML需 求的特性。你可以在一个窗口中显示简单的HTML,并用超链接创建你自己的帮助页面,如果你需要的话,甚至你还可以嵌入一个功能更全的 浏览器。

下一节内容提示:如何在wxPython窗 口中显示HTML

显示HTML

wxPython中,你对HTML能做的最重要的事情就是将它显示在一个窗口 中。下面的两节,我们将讨论HTML窗口对象,以及给你展示如何对本地的文本或远程的URL使用它。

如 何在一个wxPython窗口中显示HTML?

正如我们在第六章中讨论的,对于使用样式文本或简单的网格来快速地描述文本的布局,wxPython中 的HTML是一个有用的机制。wxPythonwx.html.HtmlWindow类就是用 于此目的的。图16.1显示了一个例子。

图16.1

w16.1.gif

例16.1显示了用于产生图16.1的代码。

例16.1 显示简单地HtmlWindow

import wx

切换行号显示

   1 import wx.html
   2 
   3 class MyHtmlFrame(wx.Frame):
   4     def __init__(self, parent, title):
   5         wx.Frame.__init__(self, parent, -1, title)
   6         html = wx.html.HtmlWindow(self)
   7         if "gtk2" in wx.PlatformInfo:
   8             html.SetStandardFonts()
   9 
  10         html.SetPage(
  11             "Here is some  b formatted /b   i  u text /u  /i  "
  12             "loaded from a  font color=\"red\" string /font .")
  13 
  14 
  15 app = wx.PySimpleApp()
  16 frm = MyHtmlFrame(None, "Simple HTML")
  17 frm.Show()
  18 app.MainLoop()

wx.html.HtmlWindow的 构造函数基本上是与wx.ScrolledWindow相同的,如下所示:

切换行号显示

   1 wx.html.HtmlWindow(parent, id=-1, pos=wx.DefaultPosition,
   2         size=wx.DefaultSize, style=wx.html.HW_SCROLLBAR_AUTO,
   3         name="htmlWindow")

上 面的这些参数现在看着应该比熟悉。这最重要的不同点是默认样式wx.html.HW_SCROLLBAR_AUTO,它将告诉HTML窗 口在需要的时候自动增加滚动条。与之相反的样式是wx.html.HW_SCROLLBAR_NEVER,使用该样式将不会显示滚动 条。还有一个HTML窗口样式是wx.html.HW_NO_SelectION,它使得用户不能选择窗口中的文 本。

当在HTML窗口中写要显示的HTML时, 记住所写的HTML要是简单的。因为wx.html.HtmlWindow控件仅设计用于简单样式文本显示,而非用 于全功能的多媒体超文本系统。它只支持最基本的文本标记,更高级的特性如层叠样式表(css)JavaScript不 被支持。表16.1包含了官方支持的HTML标记。通常,这里的标记和它的属性的行为和web浏览器中的一样,但是 由于它不是一个完全成熟的浏览器,所以有时会出现一些奇怪行为的情况。表16.1中列出了后跟有属性的标记。

表16.1 用于HTML窗口控件的有 效的标记

文 档结构标记 a href name target   body alignment bgcolor
link text   meta content http-equiv  title
文本 结构标记 br   div align   hr align noshade size width   p
文本显示标记 address   b   big   blockquote   center   cite
code   em   font color face size  h1   h2   h3   h4   h5   h6
i   kbd   pre   samp   small   strike   string   tt   u
列表 标记 dd   dl   dt   li   ol   ul
图像和地图标记 area coords href shape   img align
height src width usemap   map name
表格标记 table align bgcolor border cellpadding
cellspacing valign width  td align bgcolor colspan
rowspan valign width nowrap   th align bgcolor colspan
valign width rowspan   tr align bgcolor valign

HTML窗口使用wx.Image来 装载和显示图像,所以它可以支持所有wx.Image支持的图像文件格式。

如何显示来自 一个文件或URL的HTML?

一旦你创建了一个HTML窗口,接下来就是在这个窗口中显示HTML文本。下面的 四个方法用于在窗口中得到HTML文本。

  • SetPage(source)
  • AppendToPage(source)
  • LoadFile(filename)
  • LoadPage(location)

其中最直接的方法是SetPage(source),参数source是一个字 符串,它包含你想显示在窗口中的HTML资源。

你可以使用方法AppendToPage(source)添 加HTML到窗口中的文本的后面。至于SetPage()AppendToPage()方法, 其中的参数source被假设是HTML,这意味着,如果你传递的是纯文本,那么其中的间距将被忽略,以符合HTML标 准。

如果你想让你的窗口在浏览外部的资源时更像一个浏览器,那么 你有两种方法。方法LoadFile(filename)读取本地文件的内容并将它们显示在窗口中。在这种情况中,窗口利用MIME文 件类型来装载一个图像文件或一个HTML文件。如果它不能确定文件是何种类型,那么它将以纯文本的方式装载该文件。如果被装载的文档包 含有相关图像或其它文档的链接,那么被用于解析那些链接的位置是原文件的位置。

当然,一个实际的浏览器不 会只局限于本地文件。你可以使用方法LoadPage(location)来装载一个远程的URL,其中参数location是 一个URL,但是对于本地文件,它是一个路径名。MIME类型的URL被用来决定页面如何被装 载。本章的稍后部分,我们将讨论如何增加对新文件类型的支持。

图16.2显示了被装载入HTML窗 口中的一个页面。

图16.2

w16.2.gif

例16.2显示了产生图16.2的代码

例16.2 从一个web页装载HTML窗口的内容

切换行号显示

   1 import wx
   2 import wx.html
   3 
   4 class MyHtmlFrame(wx.Frame):
   5     def __init__(self, parent, title):
   6         wx.Frame.__init__(self, parent, -1, title, size=(600,400))
   7         html = wx.html.HtmlWindow(self)
   8         if "gtk2" in wx.PlatformInfo:
   9             html.SetStandardFonts()
  10 
  11         wx.CallAfter(
  12             html.LoadPage, "http://www.wxpython.org")
  13 
  14 app = wx.PySimpleApp()
  15 frm = MyHtmlFrame(None, "Simple HTML Browser")
  16 frm.Show()
  17 app.MainLoop()

例16.2中关键的地方是方法LoadPage()。 拥有更完整特性的浏览器窗口还应有显示URL的文本框,并在当用户键入一个新的URL后,可以改变窗口中的内容。

管理HTML窗口

一旦你有了一个HTML窗口,你就可以通过不同的方法来管理它。你可以根据用户的输入来触发相应的动 作,处理窗口的内容,自动显示有关窗口的信息和打印页面等。在随后的几节中,我们将讨论如何实现这些。

如何响应用户在一 个链接上的敲击?

wx.html.HtmlWindow的用处不只限于显示。还可以用于响应用户的输入。在这种情况 下,你不需要定义你自己的处理器,你可以在你的wx.html.HtmlWindow的子类中覆盖一些处理函数。

表16.2说明了已定义的处理函数。wx.html.HtmlWindow类没有使用事件系统定义 事件,所以你必须使用这些重载的成员函数来处理相关的事件,而非绑定事件类型。

另外,如果你想让一个HTML窗 口响应用户的输入,你必须创建你自己的子类并覆盖这些方法。

表16.2 wx.html.HtmlWindow的事件处理函数

OnCellClicked(cell, x, y, event) 当用户在HTML文档中敲击时调用。参数cell是一个wx.html.HtmlCell对 象,该对象代表所显示的文档的一部分,诸如文本、单元格或图像等。wx.html.HtmlCell类被HTML解 析器创建,这将在本章后部分讨论。参数x,y是鼠标敲击的准确位置(像素单位),参数event是相关的鼠标敲击事 件。如果cell包含一个链接,那么这个方法的默认版本将简单地委托给OnLinkClicked(),否则它什么 也不做。
OnCellMouseHover(cell, x, y) 当鼠标经过一个HTML单元时调用。参数同OnCellClicked()
OnLinkClicked(link) 当用户在一个超链接上敲击时调用。该方法的默认版对链接的URL调用LoadPage。 覆盖该方法通常用于使用HtmlWindow来为应用程序制作一个关于框。在那种情况下,你可以改变行为以便用户通过敲击其中的主页来 使用Pythonwebbrowser模块去运行系统默认的浏览器。
OnOpeningURL(type, url) 当用户请求打开一个URL时调用,不管打开页面或页面中的一个图像。参数type可 以是wx.html.HTML_URL_PAGE, wx.html.HTML_URL_IMAGE, wx.html.HTML_URL_OTHER。 该方法返回下列值之一——wx.html.HTML_OPEN允许资源装载,wx.html.HTML_BLOCK; 阻止载入资源;或用于URL重定向的一个字符串,并且在重定向后该方法再一次被调用。该方法的默认版总是返回wx.html.HTML_OPEN
OnSetTitle(title) HTML源文件中有 title 标记时调用。通常用于在应 用程序中显示标题。

如何使用 编程的方式改变一个HTML窗口?

当你正显示一个HTML页时,你还可以改变你的窗口像浏览器样去显示其它的内容,如一另一个Web页, 或帮助文件或其它类型的数据,以响应用户的需要。

有两个方法来当HTML窗 口在运行时,访问和改变HTML窗口中的信息。首先,你可以使用GetOpenedPage()方法来得到当前打开 的页面的URL。该方法只在当前页是被LoadPage()方法装载的才工作。如果是这样的,那么方法的返回值是当 前页的URL。否则,或当前没有打开的页面,该方法返回一个空字符串。另一个相关的方法是GetOpenedAnchor(), 它返回当前打开页面中的锚点(anchor)。如果页面不是被LoadPage()打开的,你将得到一个空的字符 串。

要得到当前页的HTML标题,可以使用方 法GetOpenedPageTitle(),这将返回当前页的 title 标记中的值。如果当前页没有一个 title 标 记,你将得到一个空的字符串。

这儿有几个关于改变窗口中文 本的选择的方法。方法SelectAll()选择当前打开的页面中的所有文本。你可以使用SelectLine(pos)SelectWord(pos)做 更有针对性的选择。其中pos是鼠标的位置wx.Point,这两个方法分别选择一行或一个词。要取得当前选择中的 纯文本内容,可以使用方法SelectionToText(),而方法ToText()返回整个文档的纯文本内容。

wx.html.HtmlWindow维 护着历史页面的一个列表。使用下表16.3中的方法,可以如通常的浏览器一样浏览这个历史列表。

表16.3

HistoryBack() 装载历史列表中的前一项。如果不存在则返回False
HistoryCanBack() 如果历史列表中存在前一项,则返回True,否则返回False
HistoryCanForward() 如果历史列表中存在下一项,则返回True,否则返回False
HistoryClear() 清空历史列表。
HistoryForward() 装载历史列表中的下一项。如果不存在则返回False

要改变正在使用的字体,可以使用方法SetFonts(normal_face, fixed_face, sizes=None)。 参数normal_face是你想用在窗口显示中的字体的名字字符串。如果normal_face是一个空字符串, 则使用系统默认字体。参数fixed_face指定固定宽度的文本,类似于 pre 标记的作用。如果指定了fixed_face参 数,那么参数sizes则应是一个代表字体的绝对尺寸的包含7个整数的列表,它对应于HTML逻辑字体尺寸(如 font 标 记所使用的)-24之间。如果该参数没有指定或是None,则使用默认的。关于默认常量wx.html.HTML_FONT_SIZE_n,n 位于1~7之间。这些默认常量指定了对应于HTML逻辑字体尺寸所使用的默认字体。准确的值可能因不同的底层系统而不同。要选择一套基 于用户的系统的字体和尺寸,可以调用SetStandardFonts()。这在GTK2下运行wxPython时 是特别有用的,它能够提供一套更好的字体。

如果 由于某种原因,你需要改变窗口中文本边缘与窗口边缘之间的间隔的话,HTML窗口定义了SetBorders(b)方 法。参数b是间隔的像素宽度(整数值)。

如 何在窗口的标题栏中显示页面的标题?

在你的web浏览器中,你可能也注意到了一件事,那就是浏览器中不光只有显示窗口,还有标 题栏和状态栏。通常,标题栏显示打开页面的标题,状态栏在鼠标位于链接上时显示链接信息。在wxPython中有两个便捷的方法来实现 这些。图16.3对此作了展示。窗口显示的标题是基于web页面的标题的,状态栏文本也来自Html窗口。

例16.3是产生图16.3的代码。

图16.3 带有状态栏和标题栏的HTML窗 口

w16.3.gif

例16.3 从一个web页载入HTMLWindow的 内容

切换行号显示

   1 #-*- encoding:UTF-8 -*- 
   2 import wx
   3 import wx.html
   4 
   5 class MyHtmlFrame(wx.Frame):
   6     def __init__(self, parent, title):
   7         wx.Frame.__init__(self, parent, -1, title, size=(600,400))
   8         self.CreateStatusBar()
   9 
  10         html = wx.html.HtmlWindow(self)
  11         if "gtk2" in wx.PlatformInfo:
  12             html.SetStandardFonts()
  13         html.SetRelatedFrame(self, self.GetTitle() + " -- %s") #关联HTML到框架
  14         html.SetRelatedStatusBar(0) #关联HTML到状态栏
  15 
  16         wx.CallAfter(
  17             html.LoadPage, "http://www.wxpython.org")
  18 
  19 app = wx.PySimpleApp()
  20 frm = MyHtmlFrame(None, "Simple HTML Browser")
  21 frm.Show()
  22 app.MainLoop()

要设置标题栏的关联,使用 方法SetRelatedFrame(frame, format)。参数frame你想显示页面标题的框架。参数format是 你想在框架的标题栏中显示的字符串。通常的格式是这样:“My wxPython Browser: %s”。:%s前面的字符串可以 是你想要的任何字符串,%s将会被HTML页面的标题所取代。在窗口中,一个页面被载入时,框架的标题自动被新的页面的信息取代。

要设置状态栏,使用方法SetRelatedStatusBar(bar)。该方法必须 在SetRelatedFrame()之后调用。参数bar是状态栏中用于显示状态信息的位置。通常它是0,但是如 果状态栏中存在多个显示区域,那么bar可以有其它的值。如果bar的取值为-1,那么不显示任何信息。一旦与状态 栏的关联被创建,那么当鼠标移动到显示的页面的链接上时,相关链接的URL将显示在状态栏中。

如何打印一个HTML页面?

一旦HTML被显示在屏幕上,接下来可能做的事就是打印该HTML。类wx.html.HtmlEasyPrinting就 是用于此目的的。你可以使用下面的构造函数来创建wx.html.HtmlEasyPrinting的一个实例:

wx.html.HtmlEasyPrinting(name="Printing", parentWindow=None)

参数name只 是一个用于显示在打印对话框中的字符串。参数parentWindow如果被指定了,那么parentWindow就 是这些打印对话框的父窗口。如果parentWindowNone,那么对话框为顶级对话框。你只应该创建wx.html.HtmlEasyPrinting的 一个实例。尽管wxPython系统没有强制要这样做,但是该类是被设计为独自存的。

使 用wx.html.HtmlEasyPrinting的实例

从该类的名字可以看出,它应该是容易使用的。首先,通过使用PrinterSetup()PageSetup()方 法,你能够给用户显示用于打印设置的对话框。调用这些方法将导致相应的对话框显示给用户。实例将存储用户所做的设置,以备后用。如果你想访问这些设置数 据,以用于你自己特定的处理,你可以使用方法GetPrintData()GetPageSetupData()GetPrintData()方 法返回一个wx.PrintData对象,GetPageSetupData()方法返回一wx.PageSetupDialogData对 象,我们将在第17章中更详细地讨论。

设置字体

你可以使用方法SetFonts(normal_face, fixed_face, sizes)来 设置打印所使用的字体。这个方法的行为同用于HTML窗口的SetFonts()相同(在打印对象中的设置不会影响 到HTML窗口中的设置)。你可以使用方法SetHeader(header, pg)SetFooter(footer, pg)来 页眉和页脚。参数headerfooter是要显示的字符串。字符串中你可以使用点位符@PAGENUM@, 占位符在执行时被打印的页号替代。你也可以使用@PAGENUM@占位符,它是打印的页面总数。参数pg的取值可以 是这三个:wx.PAGE_ALLwx.PAGE_EVENwx.PAGE_ODD。它控制 页眉和页脚显示在哪个页上。通过对不同的pg参数多次调用该方法,可以为奇数页和偶数页设置单独的页眉和页脚。

输 出预览

如果 在打印前,你想预览一下输出的结果,你可以使用PreviewFile(htmlfile)方法。在这种情况下,参数htmlfile是 你本地的包含HTML的文件的文件名。另一是PreviewText(htmlText, basepath=“”)。 参数htmlText是你实际想打印的HTMLbasepath文件的路径或URL。 如预览成功,这两个方法均返回True,否则返回False。如果出现了错误,那么全局方法wx.Printer.GetLastError()将 得到更多的错误信息。关于该方法的更详细的信息将在第17章中讨论。

打印

现在你可能想知道如何简单地打印一个HTML页面。方法就是PrintFile(htmlfile)PrintText(htmlText, basepath)。 其中的参数同预览方法。所不同的是,这两个方法使用对话框中的设置直接让打印机打印。打印成功,则返回True

拓展HTML窗口

在这一节,我们将给你展示如何处理HTML窗口中的HTML标记,如何 创造你自己的标记,如何在HTML中嵌入wxPython控件,如何处理其它的文件格式,以及如何在你的应用程序中 创建一个真实的HTML浏览器。

HTML 解析器(parser)是如何工作的?

wxPython中,HTML窗口有它自己内在的解析器。实际上,这 里有两个解析器类,但是其中的一个是另一个的改进。通常,使用解析器工作仅在你想扩展wx.html.HtmlWindow自身的功能 时有用。如果你正在使用Python编程,并基于其它的目的想使用一个HTML解析器,那么我们建议你使用随同Python发 布的htmllibHTMLParser这两个解析器模块之一,或一个外部的Python工具 如“Beautiful Soup”。

两 个解析器类分别是wx.html.HtmlParser,它是一个更通用的解析器,另一个是wx.html.HtmlWinParser, 它是wx.html.HtmlParser的子类,增加了对在wx.html.HtmlWindow中显示文本的支 持。由于我们所关注的基本上是HTML窗口,所以我们将重点关注wx.html.HtmlWinParser

要创建一个HTML解析器,可以使用两个构造函数之一。其中基本的一个是wx.html.HtmlWinParser(), 没有参数。wx.html.HtmlWinParser的父类wx.html.HtmlParser也有一个没有参 数的构造函数。你可以使用另一个构造函数wx.html.HtmlWinParser(wnd)将一个wx.html.HtmlWinParser()与 一个已有的wx.html.HtmlWindow联系在一起,参数wndHTML窗口的实例。

要使用解析器,最简单的方法是调用Parse(source)方 法。参数source是要被处理的HTML字符串。返回值是已解析了的数据。对于一个wx.html.HtmlWinParser, 返回值是类wx.html.HtmlCell的一个实例。

HTML解 析器将HTML文本转换为一系列的单元,一个单元可以表示一些文本,一个图像,一个表,一个列表,或其它特定的元素。wx.html.HtmlCell的 最重要的子类是wx.html.HtmlContainerCell,它是一个可以包含其它单元在其中的一个单元,如一个表或一个带有 不同文本样式的段落。对于你解析的几乎任何文档,返回值都将是一个wx.html.HtmlContainerCell。每个单元都包 含一个Draw(dc, x, y, view_y1, view_y2)方法,这使它可以在HTML窗口中自动绘 制它的信息。

另一个重要的子类单元是wx.html.HtmlWidgetCell, 它允许一个任意的wxPython控件像任何其它单元一样被插入到一个HTML文档中。除了可以包括用于格式化显示 的静态文本,这也包括任何类型的用于管理HTML表单的控件。wx.html.HtmlWidgetCell的构造 函数如下:

wx.html.HtmlWidgetCell(wnd, w=0) 

其中参数wnd是要被绘制的wxPython控件。参数w是一个浮动 宽度。如果w不为0,那么它应该是介于1和100之间的一个整数,wnd控件的宽度则被动态地调整为相对于其父容器宽度的w%。

另外还有其它许多类型的用于显示HTML文档的部分的单元。更多的信息请参考wxWidget文 档。

如何增加对新标记的支持?

被解析器返回的单元是被标记处理器内在的创建的,通过HTML标记,一个可插入的结构与HTML解 析器单元的创建和处理相联系起来。你可以创建你自己的标记处理器,并将它与HTML标记相关联。使用这个机制,你可以扩展HTML窗 口,以包括当前不支持的标准标记,或你自己发明的自定义的标记。图16.4显示了自定义HTML标记的用法。

图16.4

w16.4.gif

下例16.4是产生图16.4的代码。

例16.4 定义并使用自定义的标记处理器

切换行号显示

   1 import wx
   2 import wx.html
   3 
   4 page = """ html  body 
   5 
   6 This silly example shows how custom tags can be defined and used in a
   7 wx.HtmlWindow.  We've defined a new tag,  blue  that will change
   8 the  blue foreground color /blue  of the portions of the document that
   9 it encloses to some shade of blue.  The tag handler can also use
  10 parameters specifed in the tag, for example:
  11 
  12  ul 
  13  li   blue shade='sky' Sky Blue /blue 
  14  li   blue shade='midnight' Midnight Blue /blue 
  15  li   blue shade='dark' Dark Blue /blue 
  16  li   blue shade='navy' Navy Blue /blue 
  17  /ul 
  18 
  19  /body  /html 
  20 """
  21 
  22 
  23 class BlueTagHandler(wx.html.HtmlWinTagHandler):#声明标记处理器
  24     def __init__(self):
  25         wx.html.HtmlWinTagHandler.__init__(self)
  26 
  27     def GetSupportedTags(self):#定义要处理的标记
  28         return "BLUE"
  29 
  30     def HandleTag(self, tag):#处理标记
  31         old = self.GetParser().GetActualColor()
  32         clr = "#0000FF"
  33         if tag.HasParam("SHADE"):
  34             shade = tag.GetParam("SHADE")
  35             if shade.upper() == "SKY":
  36                 clr = "#3299CC"
  37             if shade.upper() == "MIDNIGHT":
  38                 clr = "#2F2F4F"
  39             elif shade.upper() == "DARK":
  40                 clr = "#00008B"
  41             elif shade.upper == "NAVY":
  42                 clr = "#23238E"
  43 
  44         self.GetParser().SetActualColor(clr)
  45         self.GetParser().GetContainer().InsertCell(wx.html.HtmlColourCell(clr))
  46 
  47         self.ParseInner(tag)
  48 
  49         self.GetParser().SetActualColor(old)
  50         self.GetParser().GetContainer().InsertCell(wx.html.HtmlColourCell(old))
  51 
  52         return True
  53 
  54 
  55 wx.html.HtmlWinParser_AddTagHandler(BlueTagHandler)
  56 
  57 
  58 
  59 class MyHtmlFrame(wx.Frame):
  60     def __init__(self, parent, title):
  61         wx.Frame.__init__(self, parent, -1, title)
  62         html = wx.html.HtmlWindow(self)
  63         if "gtk2" in wx.PlatformInfo:
  64             html.SetStandardFonts()
  65         html.SetPage(page)
  66 
  67 
  68 app = wx.PySimpleApp()
  69 frm = MyHtmlFrame(None, "Custom HTML Tag Handler")
  70 frm.Show()
  71 app.MainLoop()

标记内在的由类wx.Html.Tag的 方法来表现,标记的实例由HTML解析器来创建,通常,你不需要自己创建。表16.4显示了wx.Html.Tag类 的方法,它们有用于检索标记的信息。

表16.4 wx.Html.Tag的一些方法

GetAllParams() 返回与标记相关的所有参数,返回值是一个字符串。出于某些目的,解析字符串比得到各个单独的参数更容 易。
GetName() 以大写的方式,返回标记的名字。
HasParam(param) 如果标记给定了参数,则返回True
GetParam(param, with_commas=False) 返回参数param的值。如果参数 with_commasTrue,那么你得到一个首尾都有引号的原始字符串。如果没有指定该参数,那么返回一个空字符串。方法GetParamAsColour(param)返 回的参数值是一个wx.Color,方法GetParamAsInt(param)返回整数值。
HasEnding() 如果标记有结束标记的话,返回True,否则返回false

用于扩展HTML窗 口的标记处理器都是wx.html.HtmlWinTagHandler的子类。你的子类需要覆盖两个方法,并且你需要知道进一步的方 法。需要覆盖的第一个方法是GetSupportedTags()。该方法返回由处理器管理的标记的列表。标记必需是大写的,并且标记 之间以逗号分隔,中间不能有空格,如下所示:

切换行号显示

   1 GetSupportedTags(self):
   2     return "MYTAG,MYTAGPARAM"

第二个你需要覆盖的方法是HandleTag(tag)。 在HandleTag(tag)方法中,你通过增加新的单元元素到解析器来处理标记(或者交替地改变解析器已经打开的容器单元)。你可 以通过调用标记处理器的GetParser()方法来得到解析器。

要写一个HandleTag(tag)方法,你应该像下面这样做:

1、得到解析器。 2、 对你的标记的参数做必要的处理,可能要改变或创建一个新的单元。 3、 如果被解析的标记包括着内在的文本,那么解析标记之间的文本。 4、 执行对于解析器所需要的任何清理工作。

如上 所述,你使用GetParser()方法得解析器。要添加或编辑解析器中的单元,你有三个可选方案。第一个,如果你想添加另一个单元到 容器中,你可以工作于当前的容器。第二个,你可以调用解析器的Container()方法,然后创建你的wx.html.HTMLCell子 类实例,并通过调用容器的InsertCell(cell)方法将它添加到容器。

有时,你可能想在当前打开的容器中创建一个附属的或内嵌的容器。例如内嵌于表的一行中的一个单元格。要实现 这个,你需要调用解析器的OpenContainer()方法。这个方法返回你的新的容器单元,你可以使用InsertCell(cell)方 法来插入显示单元到你的新的容器单元中。对于每个在你的标记处理器中打开的容器,你应该使用CloseContainer()方法来关 闭它。如果你没有成对的使用OpenContainer()CloseContainer(),那么这将导致解析 器解析余下的HTML文本时出现混乱。

第 三个方案是创建一个与解析器的当前容器同级的容器,意思是不是嵌入的。例如一个新的段落——它不是前一段的一部分,也不附属于前一段;它是该页中?囊桓鲂 碌氖堤濉N嗽诮馕銎髦惺迪终飧龇桨福阈枰乇障执娴娜萜鳎俅蛞桓鲂碌娜萜鳎缦滤荆?

parser = self.GetParser()
parser.CloseContainer()#关闭现存的容器
parser.OpenContainer()#打一个新的容器

# 添加或编辑解析器中的单元 

parser.CloseContainer()
parser.OpenContainer() 

如何支持其他的文件格式?

默认情况下,HTML窗口可以处理带有MIME类型text/html, text/txt, image/* (假设wxPython图像处理器已经被装载)的文件。当碰上一个不是图像或HTML文件的文件时,该HTML窗 口试图以纯文本的方式显示它。这可以不是你想要的行为。如果有一些文件你想以自定义的方式显示它的话,你可以创建一个wx.html.HtmlFilter来 处理它。比如,你可能想以源代码树的方式显示XML文件,或使用语法着色来显示Python源代码文件。

要创建一个筛选器(filter),你必须建造wx.html.HtmlFilter的 一个子类。wx.html.HtmlFilter类有两个方法,你必须都覆盖它们。这第一个方法是CanRead(file)。 参数filewx.FSFile(一个打开的文件的wxPython表示)的一个实例。类wx.FSFile有 两个属性,你可以用来决定你的筛选器是否能够读该文件。方法GetMimeType()以一个字符串的形式返回该文件的MIME类 型。MIME类型通常由文件的后缀所定义。方法GetLocation()返回带有相关文件位置的绝对路径或URL的 一个字符串。如果筛选器会处理该文件的话,CanRead()方法应该返回True,否则返回False。 处理Python源文件的CanRead()的一个示例如下:

切换行号显示

   1 CanRead(self, file):
   2     return file.GetLocation().endswith('.py')

第二个你需要覆盖的方法是ReadFile(file)。 这个方法要求一个同样的file参数,并返回该文件内容的一个字符串的HTML表达。如果你不想使用wxWidgets C++ 的文件机制来读该文件的话,你可以通过简单地打开位于file.GetLocation()的文件来使用Python的 文件机制。

一旦筛选器被创建了,那么它必须被注 册到wx.html.HtmlWindow,使用wx.html.HtmlWindow窗口的AddFilter(filter)静 态方法来实现。参数filter是你的新的wx.html.HtmlFilter类的一个实例。一旦注册了筛选器, 那么该窗口就可以使用筛选器来管理通过了CanRead()测试的文件对象。

如 何得到一个性能更加完整的HTML控件?

尽管wx.html.HtmlWindow不是一个完整特性的浏览器面板,但是这儿有一对 用于嵌入更加完整特性的HTML表现窗口的选择。如果你是在Windows平台上,你可以使用类wx.lib.iewin.IEHtmlWindow, 它是Internet Explorer ActiveX控件的封装。这使得你能够直接将ie窗口嵌入到你的应用程 序中。

使用IE控件比较 简单,类似于使用内部的wxPythonHTML窗口。它的构造函数如下:

切换行号显示

   1 wx.lib.iewin.IEHtmlWindow(self, parent, ID=-1,
   2         pos=wx.DefaultPosition, size=wx.DefaultSize, style=0,
   3         name='IEHtmlWindow')

其中参数parent是 父窗口,IDwxPython ID。对于IE窗口,这儿没有可用的样式标记。要装载HTMLIE组 件中,可以使用方法LoadString(html),其中参数html是要显示的一个HTML字 符串。你可以使用方法LoadStream(stream)装载自一个打开的文件,或一个Python文件对象;或 使用LoadString(URL)方法装载自一个URL。你能够使用GetText(asHTML)来 获取当前显示的文本。参数asHTML是布尔值。如果为True,则返回HTML形式的文本,否 则仅返回一个文本字符串。

在其 它平台上,你可以尝试一下wxMozilla项目(http://wxmozilla.sourceforge.net), 该项目尝试创建一个Mozilla Gecko表现器的wxPython封装。目前该项目仍在测试阶段。wxMozilla有 用于WindowsLinux的安装包,对Mac OS X的支持正在开发中。

本章小结

1、HTML不再是只用于Internet了。在wxPython中, 你可以使用一个HTML窗口来显示带有HTML标记的简单子集的文本。该HTML窗口属于wx.html.HtmlWindow类。 除了HTML文本,该HTML窗口还可以管理任一的图像(图像处理器已装载的情况下)。

2、你可以让HTML窗口显示一个字符串,一个本地文件或一个URL的 信息。你可以像通常的超文本浏览器的方式显示用户的敲击,或使用它自定义的响应。你也可以将HTML窗口与它的框架相连接起来,以便标 题和状态信息自动地显示在适当的地方。HTML窗口维护着一个历史列表,你可以对它进行访问和处理。你可以使用类wx.Html.HtmlEasyPrinting来 直接打印你的页面。

3、在wxPython中 有一个HTML解析器,你可以用来创建用于你自己窗口的自定义标记。你也可以配置自定义的文件筛选器来在一个HTML窗 口中表现其它的文件格式。

4、 最后,如果你对HTML窗口的局限性不太满意的话,那么你可以使用一个对IE控件的wxPython封 闭。如果你不在Windows上的话,这儿也有一个对Mozilla Gecko HTML表现器的wxPython的 封装。

北京网站制作公司