' Can add workbook open event to add menu ' Private Sub Workbook_Open() ' AddNewMenuItems ' End Sub Const outputdir = "C:\" Dim MnuEvt As VBECmdHandler Dim CmdItem As CommandBarControl Dim EvtHandlers As New Collection Dim RadioVariables As Collection '----------'----------'----------'----------'----------'----------'---------- ' Responsible for creating python script from form that is passed in. '----------'----------'----------'----------'----------'----------'---------- Sub OutputDlgInfo(myDlg As Object) Dim outputStr As String Dim objectMethods As String Set RadioVariables = New Collection ' Put in import statements outputStr = "from Tkinter import *" + vbLf outputStr = outputStr + "#from tkMessageBox import *" + vbLf outputStr = outputStr + "#from tkFileDialog import *" + vbLf + vbLf + vbLf Dim myControl As Control Dim myHeight, myWidth myHeight = myDlg.InsideHeight myWidth = myDlg.InsideWidth ' Class definition and constructor Dim className As String ' TypeName is the only generic thing ' we can get a hold of. This should be changed ' when your output script and class are final className = InputBox("Enter a name for your class", "Class name", TypeName(myDlg)) outputStr = outputStr + "class " + className + "(Frame):" + vbLf outputStr = outputStr + vbLf + "# Object constructor" + vbLf outputStr = outputStr + PyIndent(1) + "def __init__(self, parent=None):" + vbLf outputStr = outputStr + PyIndent(2) + "Frame.__init__(self, parent)" + vbLf ' Here we loop through each control on the form ' and add it. We also construct any event handlers ' we will use for buttons, radio buttons, check boxes, etc. For Each myControl In myDlg.Controls outputStr = outputStr + OutputControlInfo(myControl, 2) objectMethods = objectMethods + OutputControlMethod(myControl, 1) Next ' Now add the event handlers to the output outputStr = outputStr + vbLf + "# Methods (event handlers) of object" + vbLf outputStr = outputStr + objectMethods ' Now add the calls to run the dialog we created stand alone. outputStr = outputStr + vbLf + "# Method called if script is run directly" outputStr = outputStr + vbLf + "# instead of imported or used as a class" outputStr = outputStr + vbLf + "if __name__ == '__main__':" + vbLf outputStr = outputStr + PyIndent(1) + "root = Tk()" + vbLf outputStr = outputStr + PyIndent(1) + "myForm = " + className + "(root)" + vbLf outputStr = outputStr + PyIndent(1) + "myForm.pack()" + vbLf outputStr = outputStr + PyIndent(1) + "root.geometry(""" + CStr(Int(myWidth)) + "x" + _ CStr(Int(myHeight)) + """)" + vbLf outputStr = outputStr + PyIndent(1) + "root.minsize(" + CStr(Int(myWidth)) + "," + _ CStr(Int(myHeight)) + ")" + vbLf outputStr = outputStr + PyIndent(1) + "root.maxsize(" + CStr(Int(myWidth)) + "," + _ CStr(Int(myHeight)) + ")" + vbLf outputStr = outputStr + PyIndent(1) + "root.mainloop()" Open outputdir + className + ".py" For Output As #1 Print #1, outputStr Close #1 MsgBox "done" End Sub '----------'----------'----------'----------'----------'----------'---------- '----------'----------'----------'----------'----------'----------'---------- ' Constructs the controls as specified by the user. We do our best to translate ' from vb controls to Tk controls. In most cases this is straightforward. '----------'----------'----------'----------'----------'----------'---------- Function OutputControlInfo(ctl As Control, Optional indentLevel As Integer = 0) As String Select Case TypeName(ctl) ' Add button with text and an event handler Case "CommandButton" OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Button(parent,text='" + _ ctl.Caption + "', command=self." + ctl.Name + "Click)" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf ' Add a label with text in it Case "Label" OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Label(parent,justify=LEFT,anchor=W,text='" + _ ctl.Caption + "')" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf ' Add either a single text field or a multi line edit control ' We add the default text from the control in the form Case "TextBox" If ctl.MultiLine Then OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Text(parent" + _ ")" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".insert(1.0,'" + ctl.Text + "')" + vbLf Else OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Entry(parent" + _ ")" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".insert(0,'" + ctl.Text + "')" + vbLf End If ' Add a spinner with the range specified in the control ' along with the increment value and an event handler. Case "SpinButton" Dim SpinOrientation SpinOrientation = "from_=" + CStr(ctl.Min) + ", to=" + CStr(ctl.Max) + _ ", increment=" + CStr(ctl.SmallChange) OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Spinbox(parent, " + _ SpinOrientation + ", command=self." + ctl.Name + "Click)" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf ' Add a list box Case "ListBox" OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Listbox(parent" + _ ")" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf ' Add a checkbox with an event handler and a variable ' that we can use to store the value Case "CheckBox" OutputControlInfo = PyIndent(indentLevel) + "self.var_" + ctl.Name + " = IntVar()" + vbLf OutputControlInfo = OutputControlInfo + PyIndent(indentLevel) + "self." + ctl.Name + " = Checkbutton(parent,justify=LEFT,anchor=W,text='" + _ ctl.Caption + "', variable=self.var_" + ctl.Name + _ ", command=self." + ctl.Name + "Click)" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf ' If the check box is selected we should select it also by default If ctl.Value Then OutputControlInfo = OutputControlInfo + PyIndent(indentLevel) + ctl.Name + ".select()" + vbLf End If ' Add a radio button with an event handler ' and a variable to store the value. Note that we ' have a collection of variables used to store radio ' button values. This helps us keep track of when ' we need to instantiate them. Case "OptionButton" ' Create variable to be associated with button Dim OptionVar As String OptionVar = "DUMMY_OPTION_VARIABLE" If Len(ctl.GroupName) > 0 Then OptionVar = ctl.GroupName End If If Not (CollectionHasValue(RadioVariables, OptionVar)) Then OutputControlInfo = PyIndent(indentLevel) + _ "self." + OptionVar + " = StringVar()" + vbLf RadioVariables.Add OptionVar, OptionVar End If ' Add control OutputControlInfo = OutputControlInfo + PyIndent(indentLevel) + "self." + ctl.Name + " = Radiobutton(parent,justify=LEFT,anchor=W,text='" + _ ctl.Caption + "', variable=self." + OptionVar + ", value='" + ctl.Name + _ "', command=self." + ctl.Name + "Click)" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf ' Add a scroll bar. Note that Tk scroll bars are very ' different than vb scroll bars. We may want to add a ' Tk Scale widget instead Case "ScrollBar" Dim ScrollOrientation ScrollOrientation = "orient=HORIZONTAL, width=" + CStr(ctl.Height) If ctl.Height > ctl.Width Then ScrollOrientation = "orient=VERTICAL, width=" + CStr(ctl.Width) End If OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Scrollbar(parent, " + _ ScrollOrientation + ")" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf ' If the user put an image on the control we will add ' a canvas. Case "Image" OutputControlInfo = PyIndent(indentLevel) + "self." + ctl.Name + " = Canvas(parent, bg=""white"", width=" + _ CStr(ctl.Width) + ", height=" + CStr(ctl.Height) + ")" + vbLf + _ PyIndent(indentLevel) + "self." + ctl.Name + ".place(x=" + CStr(ctl.Left) + ",y=" + CStr(ctl.Top) + _ ",width=" + CStr(ctl.Width) + ",height=" + CStr(ctl.Height) + ")" + vbLf End Select OutputControlInfo = OutputControlInfo + vbLf End Function '----------'----------'----------'----------'----------'----------'---------- ' Adds event handlers for certain controls. '----------'----------'----------'----------'----------'----------'---------- Function OutputControlMethod(ctl As Control, Optional indentLevel As Integer = 0) As String Select Case TypeName(ctl) Case "CommandButton" OutputControlMethod = PyIndent(indentLevel) + "def " + ctl.Name + "Click(self):" + vbLf + _ PyIndent(indentLevel + 1) + "print '\n" + ctl.Name + " clicked'" + vbLf Case "Label" Case "TextBox" Case "SpinButton" OutputControlMethod = PyIndent(indentLevel) + "def " + ctl.Name + "Click(self):" + vbLf + _ PyIndent(indentLevel + 1) + "print '\n" + ctl.Name + " clicked'" + vbLf + _ PyIndent(indentLevel + 1) + "print 'value is ' + str(self." + ctl.Name + ".get())" + vbLf Case "ListBox" Case "CheckBox" OutputControlMethod = PyIndent(indentLevel) + "def " + ctl.Name + "Click(self):" + vbLf + _ PyIndent(indentLevel + 1) + "print '\n" + ctl.Name + " clicked'" + vbLf + _ PyIndent(indentLevel + 1) + "print 'value is ' + str(self.var_" + ctl.Name + ".get())" + vbLf Case "OptionButton" OutputControlMethod = PyIndent(indentLevel) + "def " + ctl.Name + "Click(self):" + vbLf + _ PyIndent(indentLevel + 1) + "print '\n" + ctl.Name + " clicked'" + vbLf OutputControlMethod = OutputControlMethod + PyIndent(indentLevel + 1) + "print 'output of all radio variables we have at this point'" + vbLf For Each optionval In RadioVariables OutputControlMethod = OutputControlMethod + PyIndent(indentLevel + 1) + _ "print '" + optionval + " is ' + self." + optionval + ".get()" + vbLf Next Case "ScrollBar" Case "Image" End Select OutputControlMethod = OutputControlMethod + vbLf End Function '----------'----------'----------'----------'----------'----------'---------- '----------'----------'----------'----------'----------'----------'---------- Function PyIndent(level As Integer) As String For x = 1 To level PyIndent = PyIndent + " " Next End Function '---------------------------------------------------------------- ' Adds item to the VB Editor Tools menu '---------------------------------------------------------------- Sub AddNewMenuItems() While EvtHandlers.Count > 0 EvtHandlers.Remove 1 Wend With Application.VBE.CommandBars("Menu Bar").Controls("Tools") .Reset Set CmdItem = .Controls.Add CmdItem.Caption = "Create Tkinter" CmdItem.BeginGroup = True CmdItem.OnAction = "'" & ThisWorkbook.Name & "'" & "!GeneratePythonCodeForForm" Set MnuEvt = New VBECmdHandler Set MnuEvt.EvtHandler = Application.VBE.Events.CommandBarEvents(CmdItem) EvtHandlers.Add MnuEvt End With End Sub '----------'----------'----------'----------'----------'----------'---------- '----------'----------'----------'----------'----------'----------'---------- Sub GeneratePythonCodeForForm() On Error GoTo errGeneratePythonCodeForForm If Application.VBE.ActiveWindow.Type = vbext_wt_Designer Then Dim qobj As Object Set qobj = Application.VBE.SelectedVBComponent.Designer ' qobj is a generic userform. OutputDlgInfo qobj 'Application.VBE.SelectedVBComponent End If Exit Sub errGeneratePythonCodeForForm: MsgBox "Error generating code " + Err.Description End Sub Function CollectionHasValue(col As Collection, val As String) As Boolean CollectionHasValue = False On Error GoTo exitCollectionHasValue Dim myVal As Variant myVal = col(val) CollectionHasValue = True Exit Function exitCollectionHasValue: CollectionHasValue = False End Function