== Introduction ==

Embedding ActiveX controls in a wxFrame

== What Objects are Involved ==

Obviously this is win32 specific.

In the example given, I create a (very simple) wrapper around the Office Web Components chart control. Recent versions of MS Office (example was verified with Office 2000) should come with Office Web Components, although they may be an optional installation item.

== Process Overview ==

First thing to do is to create a wrapper around the desired ActiveX component. On my ActiveState Python install, the easiest way to do this is to use the makepy.py script (in my installation this is at c:\Python22\Lib\site-packages\win32com\client. This launches a selection box in which you can select the ActiveX/COM components for which you wish to generate a wrapper. Select Microsoft Office Web Components, then OK.

The makepy.py script outputs, in the win32com\gen_py directory, a Python module named after the GUID of the control in question (in my case with the snappy name 0002E540-0000-0000-C000-000000000046x0x1x0.py). Although win32com can use this directly in an automated fashion, if you wish to distribute a Python script using the ActiveX component, it may be easier to take a copy and give it a more friendly name (e.g. _OfficeWebComponents.py) and put it in a suitable directory with the other parts of your script. The example given does this.

Given all of the above, you can test the sample given.

== Special Concerns ==

Main issue is to ensure that your generated wrapper matches the version of a control installed on your machine.

== Code Sample ==

{{{
#!python
from wxPython.wx import *
if wxPlatform == '__WXMSW__':
	from wxPython.lib.activexwrapper import MakeActiveXClass
	import _OfficeWebComponents
	
	try:
		OWCModule = _OfficeWebComponents
	except:
		raise ImportError("Could not import Office Web Components")


"""
OWCChartWrapper provides a means to relatively straightforwardly display
plotted information by using the chart ActiveX control which forms part of
the Microsoft Office Web Controls to display chart data.

This file imports a wrapper generated by makepy.py, a tools for generating
Python wrappers to COM objects. The wrapper was copied from
c:\Python22\Lib\site-packages\win32com\gen_py (which is where makepy puts it,
under the name of the COM object class ID) and given a more sensible name.

DO NOT edit _OfficeWebComponents.py - it's a generated file.

This module was tested against Office 2000 Web Components, but should probably
work with newer versions of the components. You will need a license for Office
which matches the version of the Web Components you are using, or this won't
work.
"""

class lineChartPanel(wxPanel):
	def __init__(self, parent):
		wxPanel.__init__(self, parent, -1, style=wxCLIP_CHILDREN|wxNO_FULL_REPAINT_ON_RESIZE)
		
		# Create a class encapsulating the Office Web Components chart control
		cChart = MakeActiveXClass(OWCModule.ChartSpace, eventObj = self)
		
		# Create a local instance of the Chart Class
		self.oChart = cChart(self, -1)
		
		# Create an alias for the control properties
		self.c = self.oChart.Constants
		self.oChart.Charts.Add()
		#oChart.Charts[0].Type = c.chChartTypeSmoothLine
		self.series_data = {}
		
		# Sizer and so on...
		sizer = wxBoxSizer(wxVERTICAL)
		sizer.Add(self.oChart, 1, wxEXPAND)
		self.SetSizer(sizer)
		self.SetAutoLayout(true)
		EVT_SIZE(self, self.onSize)
		EVT_WINDOW_DESTROY(self, self.onDestroy)

	def onDestroy(self, evt):
		if self.oChart:
			self.oChart.Cleanup()
			self.oChart = None

	def onSize(self, evt):
		self.Layout()
		
	def setCaption(self, chart_type, caption):
		"""Set the chart caption"""
		self.oChart.Charts[0].Type = chart_type    
		self.oChart.Charts[0].HasTitle = True
		self.oChart.Charts[0].Title.Caption   = caption
		self.oChart.Charts[0].Title.Font.Name = "Helvetica"
		self.oChart.Charts[0].Title.Font.Size = 12
		self.oChart.Charts[0].Title.Font.Bold = True
		
	def setAxis(self, axis, text, major, minor):
		if axis == 'X':
			position = self.c.chAxisPositionBottom
		else:
			position = self.c.chAxisPositionLeft
		self.oChart.Charts[0].Axes(position).HasTitle = True
		self.oChart.Charts[0].Axes(position).Title.Caption = text
		self.oChart.Charts[0].Axes(position).Title.Font.Size = 8
		self.oChart.Charts[0].Axes(position).MajorUnit = major
		self.oChart.Charts[0].Axes(position).MinorUnit = minor
				
	def addDataSet(self, set_name, data_list):
		"""Add a category, with its data, to the chart."""
		self.series_data[set_name] = data_list
		
	def finaliseChartData(self):
		"""Once all data has been defined, insert all of the data sets."""
		ordered_keys = self.series_data.keys()
		ordered_keys.sort()
		self.oChart.Charts[0].SeriesCollection.Add()
		self.oChart.Charts[0].SeriesCollection[0].SetData(self.c.chDimCategories,
			                                              self.c.chDataLiteral,
														  ordered_keys)
		b = [self.series_data[k] for k in ordered_keys]
		self.oChart.Charts[0].SeriesCollection[0].SetData(self.c.chDimValues,
		                                                  self.c.chDataLiteral,
														  b)

#----------------------------------------------------------------------


if __name__ == '__main__':
	class TestFrame(wxFrame):
		def __init__(self):
			wxFrame.__init__(self, None, -1, "ActiveX test -- Internet Explorer",
                             size=(640, 480),
							 style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
			self.CreateStatusBar()
			self.tp = lineChartPanel(self)
			self.tp.setCaption(self.tp.c.chChartTypeSmoothLine, 'Test')
			self.tp.addDataSet(1, 6)
			self.tp.addDataSet(2, 8)
			self.tp.addDataSet(3, 10)
			self.tp.addDataSet(4, 12)
			self.tp.addDataSet(5, 14)
			self.tp.addDataSet(6, 16)
			self.tp.finaliseChartData()
			EVT_CLOSE(self, self.OnCloseWindow)

		def OnCloseWindow(self, evt):
			self.tp.Destroy()
			self.Destroy()

	app = wxPySimpleApp()
	frame = TestFrame()
	frame.Show(true)
	app.MainLoop()

}}}

=== Comments ===

You can't just use the downloaded version of OWC to test this - if you don't have an Office license, the Office Web Components run in a function-limited way.

The OWC documentation is stored (on an English version of Office) under "C:\Program Files\Microsoft Office\Office\1033\MSOWCVBA.CHM". Examples for Visual BASIC are not too difficult to translate into Python.

===============================================================================
Hi, I'm not familiar with the protocol on this site, so I thought I'd dump this here. I am using Python 2.5.4 with wxPython2.8-win32-unicode-2.8.11.0-py25.exe. I had to edit the above code as follows to get this to work. Please find every place a PTB is inserted (my initials) to see the changes.

{{{
#PTB: All true and false were changed to True and False
#PTB: was from wxPython.wx import *
import wx
#PTB was wxPlatform
if wx.Platform == '__WXMSW__':
        #PTB was from wxPython.lib.activexwrapper import MakeActiveXClass
        from wx.lib.activexwrapper import MakeActiveXClass
        import _OfficeWebComp11

        try:
                OWCModule = _OfficeWebComp11
        except:
                raise ImportError("Could not import Office Web Components")


"""
OWCChartWrapper provides a means to relatively straightforwardly display
plotted information by using the chart ActiveX control which forms part of
the Microsoft Office Web Controls to display chart data.

This file imports a wrapper generated by makepy.py, a tools for generating
Python wrappers to COM objects. The wrapper was copied from
c:\Python22\Lib\site-packages\win32com\gen_py (which is where makepy puts it,
under the name of the COM object class ID) and given a more sensible name.

DO NOT edit _OfficeWebComponents.py - it's a generated file.

This module was tested against Office 2000 Web Components, but should probably
work with newer versions of the components. You will need a license for Office
which matches the version of the Web Components you are using, or this won't
work.
"""

class lineChartPanel(wx.Panel): #PTB: was wxPanel
        def __init__(self, parent):
                #PTB: was wxPanel.__init__(self, parent, -1, style=wxCLIP_CHILDREN|wxNO_FULL_REPAINT_ON_RESIZE)
                wx.Panel.__init__(self, parent, -1, style=wx.CLIP_CHILDREN|wx.NO_FULL_REPAINT_ON_RESIZE)

                # Create a class encapsulating the Office Web Components chart control
                cChart = MakeActiveXClass(OWCModule.ChartSpace, eventObj = self)

                # Create a local instance of the Chart Class
                self.oChart = cChart(self, -1)

                # Create an alias for the control properties
                self.c = self.oChart.Constants
                self.oChart.Charts.Add()
                #oChart.Charts[0].Type = c.chChartTypeSmoothLine
                self.series_data = {}

                # Sizer and so on...
                """
                PTB: was
                sizer = wxBoxSizer(wxVERTICAL)
                sizer.Add(self.oChart, 1, wxEXPAND)
                """
                sizer = wx.BoxSizer(wx.VERTICAL)
                sizer.Add(self.oChart, 1, wx.EXPAND)
                self.SetSizer(sizer)
                self.SetAutoLayout(True)
                """
                PTB: was
                EVT_SIZE(self, self.onSize)
                EVT_WINDOW_DESTROY(self, self.onDestroy)
                """
                wx.EVT_SIZE(self, self.onSize)
                wx.EVT_WINDOW_DESTROY(self, self.onDestroy)

        def onDestroy(self, evt):
                if self.oChart:
                        self.oChart.Cleanup()
                        self.oChart = None

        def onSize(self, evt):
                self.Layout()

        def setCaption(self, chart_type, caption):
                """Set the chart caption"""
                self.oChart.Charts[0].Type = chart_type
                self.oChart.Charts[0].HasTitle = True
                self.oChart.Charts[0].Title.Caption   = caption
                self.oChart.Charts[0].Title.Font.Name = "Helvetica"
                self.oChart.Charts[0].Title.Font.Size = 12
                self.oChart.Charts[0].Title.Font.Bold = True

        def setAxis(self, axis, text, major, minor):
                if axis == 'X':
                        position = self.c.chAxisPositionBottom
                else:
                        position = self.c.chAxisPositionLeft
                self.oChart.Charts[0].Axes(position).HasTitle = True
                self.oChart.Charts[0].Axes(position).Title.Caption = text
                self.oChart.Charts[0].Axes(position).Title.Font.Size = 8
                self.oChart.Charts[0].Axes(position).MajorUnit = major
                self.oChart.Charts[0].Axes(position).MinorUnit = minor

        def addDataSet(self, set_name, data_list):
                """Add a category, with its data, to the chart."""
                self.series_data[set_name] = data_list

        def finaliseChartData(self):
                """Once all data has been defined, insert all of the data sets."""
                ordered_keys = self.series_data.keys()
                ordered_keys.sort()
                self.oChart.Charts[0].SeriesCollection.Add()
                self.oChart.Charts[0].SeriesCollection[0].SetData(self.c.chDimCategories,
                                                                      self.c.chDataLiteral,
                                                                                                                  ordered_keys)
                b = [self.series_data[k] for k in ordered_keys]
                self.oChart.Charts[0].SeriesCollection[0].SetData(self.c.chDimValues,
                                                                  self.c.chDataLiteral,
                                                                                                                  b)

#----------------------------------------------------------------------


if __name__ == '__main__':
        class TestFrame(wx.Frame): #PTB: was wxFrame
                def __init__(self):
                        #PTB was wxFrame
                        wx.Frame.__init__(self, None, -1, "ActiveX test -- Internet Explorer",
                             size=(640, 480),style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE)
                        #PTB was style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
                        self.CreateStatusBar()
                        self.tp = lineChartPanel(self)
                        self.tp.setCaption(self.tp.c.chChartTypeSmoothLine, 'Test')
                        self.tp.addDataSet(1, 6)
                        self.tp.addDataSet(2, 8)
                        self.tp.addDataSet(3, 10)
                        self.tp.addDataSet(4, 12)
                        self.tp.addDataSet(5, 14)
                        self.tp.addDataSet(6, 16)
                        self.tp.finaliseChartData()
                        #PTB was EVT_CLOSE(self, self.OnCloseWindow)
                        wx.EVT_CLOSE(self, self.OnCloseWindow)

                def OnCloseWindow(self, evt):
                        self.tp.Destroy()
                        self.Destroy()

        app = wx.PySimpleApp() #PTB: was app = wxPySimpleApp
        frame = TestFrame()
        frame.Show(True)
        app.MainLoop()
}}}