65.9K
CodeProject 正在变化。 阅读更多。
Home

在脚本语言中使用 COM 对象 - 第二部分(Python)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (7投票s)

2010年4月18日

CPOL

6分钟阅读

viewsIcon

88954

downloadIcon

665

本文演示了如何在 Python 中实例化 COM 对象并使用其方法和属性。

引言

在本系列的*第一部分*文章 TkCOMApplication 中,我们已经了解了如何在 Tcl/Tk 中实例化和使用 COM 对象。在本文中,我想向您展示如何在强大的面向对象脚本语言 Python 中使用它。本文假定读者已经对 COM 有了深刻的理解。本文也不能完全用于理解 Python。期望读者能通过其他可用资源掌握 Python。本文仅用于演示在 Python 中使用 COM 对象。在展示如何在 Python 中实例化 COM 组件并使用接口之前,我想对 Python 本身做一个简要介绍。

背景

当您需要花几分钟时间构建一个操作一些文件的简短脚本时,Python 是一个非常方便的工具。此外,它对于更大的项目也很有用,因为您可以获得数据结构、模块化、面向对象、单元测试、性能分析以及海量 API 的全部功能。

Python 几乎与一切事物都有连接。您拥有非常高级的string和正则表达式处理能力,拥有线程、网络(内置许多协议)、压缩、加密,您可以使用 Tcl/Tk 构建 GUI,而这仅仅是内置功能中的一小部分。

如果您环顾四周,您会惊讶地发现互联网上有多少应用程序和库提供了 Python 绑定:MySQL、ImageMagick、SVN、Qt、libXML 等等。

有一些应用程序通过 Python 提供插件接口,例如 Blender 和 GIMP。

更进一步,您甚至可以使用 Python/C API 使用 C 或 C++ 为 Python 编写扩展模块,或者反过来,您可以将 Python 解释器用作您原生应用程序的一个模块,也就是说,您可以将 Python 嵌入到您的软件中。我的文章 Python_Embedded_Dialog 展示了如何使用 Python 解释器解析数学表达式,并在 C/C++ 的 GUI 编程中利用其结果。

然而,与 Tcl/Tk 不同,Python 并没有内置 GUI 支持。它通过为现有工具包编写的扩展来支持 GUI 编程,例如 Tk (TkInter)、Qt (PyQt)、GTK+ (PyGtk)、Fltk (PyFltk)、wxWidgets (wxPython) —— 这些是一些流行的工具包。TkInter 是 Python 默认打包的实现。

Python 可用于大多数平台:例如 Linux、Solaris、Windows、Mac、AIX、BeOS、OS/2、DOS、QNX 或 PlayStation。

设置您的编程环境

要在 Windows 上使用 Python,请从 http://www.activestate.com 下载安装程序。

安装程序将安装一个 Python 解释器 shell 和一个名为 PythonWin editor 的实用编辑器。

我们还需要安装 comtypes 扩展,它将为 Python 添加 COM 支持。此扩展仅适用于 Windows 平台。您可以从 http://sourceforge.net/projects/comtypes/files/ 下载相应版本的 comtypes。

入门

由于本文无法过多解释 Python 的理论、语法等内容,让我们立即开始实践。让我们来看一个典型的 Python "Hello World" 程序:

启动 Python 解释器

01_Start.jpg

输入以下行,然后按回车键。

print "Hello World!" 

解释器将输出语句 "Hello world!"

现在让我们看看 Python 解释器作为数学计算器的强大功能。

输入以下各行,每行后按回车键,然后查看结果。

from math import*
x = sin(pi/4) 
print x
x = 10.0/(12.5*1.25)*log(2)
print x
pow(12, 2)  

现在我们来看一个小类。注意,Python 是一个高度依赖缩进的语言。正确的缩进是分隔代码块的唯一方法。

输入以下各行,每行后按回车键。为了清晰起见,我将“__”符号显示为一个缩进级别。

class Point:
__x = 100 
__y = 200
__def setCoord(self, x, y):
____self.x = x
____self.y = y
__def printCoord(self)
____print "X %0.3f Y %0.3f" % (float(self.x), float(self.y)) 

类的定义到此结束。最后再按一次回车键以恢复提示符。

现在让我们实例化一个类的对象并使用其方法,输入以下各行,每行后按回车键,然后查看结果。

pnt = Point()
pnt.printCoord() 
pnt.setCoord(10, 20) 
pnt.printCoord()  

02_Intro.jpg


所有这些都可以放入一个扩展名为.py的文件中,并通过在资源管理器中双击来执行。对于 GUI 应用程序,使用扩展名.pyw将只显示 GUI 窗口,而不会显示控制台窗口。希望您现在已经对 Python 语言有了一些了解。让我们看看如何使用 TkInter 创建一个类似于本系列第一部分中看到的 GUI,然后在SimpleCOM库中实例化一个 COM 对象并使用其方法。

在本文中,我们有一个名为SimpleCOM的 COM 库(源代码和 VS2008 项目包含在下载文件中),它有一个名为GraphicPoint的对象。GraphicPoint实现了三个接口,所有接口都派生自IDispatch以支持脚本......

  1. IPoint
    • 方法 -- SetCoordGetCoordDistance
    • 属性 -- XYZ
  2. IGraphicPoint
    • 方法 -- Draw
  3. IColor
    • 方法 -- SetColorGetColor (OLE_COLOR)

我们将实例化 2 个GraphicPoint对象,获取它们的IPoint接口,并使用SetCoord方法设置Points的坐标。坐标将通过我们使用 Python TkInter 开发的 GUI 从用户输入中获取。我们还将通过获取IColor接口来设置这两个点的颜色。在这里,我们将看到如何将从颜色对话框获得的 RGB 分量转换为OLE_COLOR。然后,我们将通过调用IPoint接口的Distance方法来计算两个点之间的距离。我们还将通过弹出显示每个点坐标和颜色的消息框来模拟点的绘制。为此,我们将调用IGraphicPoint接口的Draw方法。当从 Python 代码实例化点时,我们还将弹出一个messagebox显示我们设置的坐标。为此,我们将调用点对象的XYZ属性。所有这些都将涵盖实例化 COM 对象、查询相应接口以及使用方法和属性的活动。

脚本以必要的系统相关、TkInter 和 COM 相关扩展导入开始。

import sys

# for TkInter GUI support
from Tkinter import *
import tkMessageBox
import tkColorChooser

# for COM support
import comtypes.client as cc
import comtypes

# Load the typelibrary registered with the Windows registry
tlb_id = comtypes.GUID("{FA3BF2A2-7220-47ED-8F07-D154B65AA031}")
cc.GetModule((tlb_id, 1, 0)) 

# Alternately you can use this method also by direct loading the dll file
#cc.GetModule("SimpleCOM.dll") 

# Import the SimpleCOMLib library from comtypes
import comtypes.gen.SimpleCOMLib as SimpleCOMLib  

现在是应用程序 GUI 的类定义

# Application class for the GUI
class AppDlg:
	# member variables
	X1 = 0.0
	Y1 = 0.0
	Z1 = 0.0
	X2 = 0.0
	Y2 = 0.0
	Z2 = 0.0
	distVal = 0.0 
	# methods 
	# constructor
	def __init__(self, master): 
		master.title("Test COM InterOp in Python/Tkinter")		
		master.maxsize(400, 210)
		master.minsize(400, 210)

		frame1 = Frame(master, padx=5, pady=5)
		frame1.pack(anchor=N, side="top", fill=X, expand=Y)

		# Point 1 Data
		self.labelframe1 = LabelFrame
		(frame1, padx=5, pady=5, relief="groove", text="Point 1 Data")
		self.labelframe1.pack(side=LEFT, fill=BOTH, expand=Y)

		self.frameX1 = Frame(self.labelframe1)
		self.frameY1 = Frame(self.labelframe1)
		self.frameZ1 = Frame(self.labelframe1)
		self.frameX1.pack()
		self.frameY1.pack()
		self.frameZ1.pack()

		self.labelX1 = Label(self.frameX1, text="X")
		self.labelX1.pack(side=LEFT, padx=2, pady=2)
		self.entryX1 = Entry(self.frameX1)
		self.entryX1.insert(0, self.X1)
		self.entryX1.pack()
		...
		...
		<code skipped for brevity>
		...

 		# variable to store colors
		self.colorTuple1 = ((255, 255, 255), '#ffffff')
		self.colorTuple2 = ((255, 255, 255), '#ffffff')

	# Apply button callback
	def onApply(self):
		self.X1 = self.entryX1.get()
		self.Y1 = self.entryY1.get()
		self.Z1 = self.entryZ1.get()
		self.X2 = self.entryX2.get()
		self.Y2 = self.entryY2.get()
		self.Z2 = self.entryZ2.get()

		#print self.colorTuple1
		#print self.colorTuple2

		# Check if the user selects cancel on the color chooser
		# in that case the tuple will contain None, None values
		if self.colorTuple1[0] is None:
			r = 255
			g = 255
			b = 255
		else:
			r = self.colorTuple1[0][0]
			g = self.colorTuple1[0][1]
			b = self.colorTuple1[0][2]
		self.color1 = (((0xff & b) << 16) | ((0xff & g) << 8) | (0xff & r))
		#print "Color Point1 is %d\n" % self.color1

		if self.colorTuple2[0] is None:
			r = 255
			g = 255
			b = 255
		else:			
			r = self.colorTuple2[0][0]
			g = self.colorTuple2[0][1]
			b = self.colorTuple2[0][2]
		self.color2 = (((0xff & b) << 16) | ((0xff & g) << 8) | (0xff & r))
		#print "Color Point2 is %d\n" % self.color2
		
		# Create COM Point1
		self.aGrPoint = cc.CreateObject
		("SimpleCOM.GraphicPoint", None, None, SimpleCOMLib.IGraphicPoint)
		self.aPoint = self.aGrPoint.QueryInterface(SimpleCOMLib.IPoint)
		#help(self.aPoint)
		self.aPoint.SetCoord(float(self.X1), float(self.Y1), float(self.Z1))
		tkMessageBox.showinfo("From Python-Tkinter App", 
		"Point 2 Created At X%0.3f, Y%0.3f, Z%0.3f"\
		% (float(self.aPoint.X), float(self.aPoint.Y), float(self.aPoint.Z)))
		self.aColor = self.aGrPoint.QueryInterface(SimpleCOMLib.IColor)
		if self.colorTuple1:
			self.aColor.SetColor(self.color1)
		self.aGrPoint.Draw()

		# Create COM Point2
		self.aGrPoint2 = cc.CreateObject("SimpleCOM.GraphicPoint", 
			None, None, SimpleCOMLib.IGraphicPoint)
		self.aPoint2 = self.aGrPoint2.QueryInterface(SimpleCOMLib.IPoint)
		#help(self.aPoint2)
		self.aPoint2.SetCoord(float(self.X2), float(self.Y2), float(self.Z2))
		tkMessageBox.showinfo("From Python-Tkinter App", 
		"Point 2 Created At X%0.3f, Y%0.3f, Z%0.3f"\
		% (float(self.aPoint2.X), float(self.aPoint2.Y), float(self.aPoint2.Z)))
		self.aColor2 = self.aGrPoint2.QueryInterface(SimpleCOMLib.IColor)
		if self.colorTuple2:
			self.aColor2.SetColor(self.color2)
		self.aGrPoint2.Draw()

		self.distVal = self.aPoint.Distance(self.aPoint2)

		self.entryDist.delete(0, END)
		self.entryDist.insert(0, self.distVal)

	# Color selection button callbacks
	def onSelectColor1(self):
		# tkColorChooser returns a tuple containing the RGB tuple 
		and the html color value
		self.colorTuple1 = tkColorChooser.askcolor()
		if self.colorTuple1:
			self.colorbtn1.configure(bg=self.colorTuple1[1])

	def onSelectColor2(self):
		self.colorTuple2 = tkColorChooser.askcolor()
		if self.colorTuple2:
			self.colorbtn2.configure(bg=self.colorTuple2[1])

# Start the TkInter root window
rootwin = Tk()

# Instantiate the GUI class object
AppDlg(rootwin)

# Run the TkInter main event loop
rootwin.mainloop() 

03_App.jpg

颜色选择按钮的回调函数仅显示一个颜色选择器对话框,并将选定的颜色存储在一个全局变量中,该变量由“应用”按钮的回调过程访问。颜色存储为 RGB 的 3 个值的列表。我们获取每个元素,并使用以下代码将其转换为OLE_COLOR等效项。

# Check if the user selects cancel on the color chooser
# in that case the tuple will contain None, None values
if self.colorTuple1[0] is None:
	r = 255 
	g = 255
	b = 255
else:
	r = self.colorTuple1[0][0]
	g = self.colorTuple1[0][1]
	b = self.colorTuple1[0][2]
	# code for converting RGB values into OLE_COLOR
	self.color1 = (((0xff & b) << 16) | ((0xff & g) << 8) | (0xff & r)) 

要创建 COM 类的对象,请使用comtypes.client方法CreateObject,要访问接口,请在接口变量上调用QueryInterface方法。例如:

self.aGrPoint = cc.CreateObject("SimpleCOM.GraphicPoint", 
	None, None, SimpleCOMLib.IGraphicPoint)  
self.aPoint = self.aGrPoint.QueryInterface(SimpleCOMLib.IPoint)  
self.aPoint.SetCoord(float(self.X1), float(self.Y1), float(self.Z1))    
tkMessageBox.showinfo("From Python-Tkinter App", 
	"Point 2 Created At X%0.3f, Y%0.3f, Z%0.3f"\
% (float(self.aPoint.X), float(self.aPoint.Y), float(self.aPoint.Z))) 
self.aColor = self.aGrPoint.QueryInterface(SimpleCOMLib.IColor)
if self.colorTuple1:
	  self.aColor.SetColor(self.color1) 
self.aGrPoint.Draw()     

希望您喜欢这篇文章。本演示的 Python 脚本文件和 COM VS2008 项目文件包含在可下载的 zip 文件中。Windows 7 用户需要以管理员模式运行 Visual Studio 来构建项目并注册 COM DLL。

关注点

COM 的强大功能和IDispatch再次得到证明。它为从 RAD 工具和脚本访问组件提供了巨大的灵活性。Python 拥有许多用于开发稳健灵活应用程序的扩展,例如数值和科学应用程序、游戏、CAD 和图形。

参考文献

历史

  • 2010 年 4 月 18 日:首次发布
© . All rights reserved.