Baixe o app para aproveitar ainda mais
Prévia do material em texto
Page 1 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Designing Tkinter forms with Page/Tcl If you are familiar with designing forms for Visual Basic and C# using Visual Studio, or even Eclipse FormsBuilder for Java, you will appreciate the convenience of these IDE development tools. When it comes to Python GUIs then it becomes a whole different ballgame. There are a number of GUI designers but they all have one major problem, which is they require different libraries that need to be installed to use them and run the code afterwards. This is OK at home, but what about at a school, where you do not have priveleges to install these libraries? Usually the only GUI there is Tkinter, as it is part of the original Python distribution. Luckily there is a solution, which does not need anything to be ‘installed’ ie registry entries, .dll files in System32, desktop icons etc. etc. It runs from a USB drive, or any location where you can run .exe executable files. This often includes your Documents user space! If you want to set it up yourself from scratch here’s how: Download the following: 1) Get Tcl from http://www.activestate.com/activetcl/downloads version 8.6.4.1 at the time of writing. Use either 32bit or 64bit depending on your system. 2) Get PAGE (Python Automatic (gui ) GEnerator) from https://sourceforge.net/projects/page/files/latest/download version 4.8.6 at the time of writing 3) Documentation on http://page.sourceforge.net/html/index.html Useful tutorial on http://page.sourceforge.net/pdf/python-page.pdf 4) If you want to use SharpDevelop to create your own Page/Tcl launcher in C# for use in the PortableApps distribution, and to test the comparisons detailed below go to: http://www.icsharpcode.net/OpenSource/SD/Default.aspx This is a 50MB application and runs from any location, including USB The documentation and tutorial links above are helpful, but for a quick guide to setting up and use read on: http://www.activestate.com/activetcl/downloads https://sourceforge.net/projects/page/files/latest/download http://page.sourceforge.net/html/index.html http://page.sourceforge.net/pdf/python-page.pdf http://www.icsharpcode.net/OpenSource/SD/Default.aspx Page 2 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ ‘Install’ ActiveTcl by double-clicking the executable file: Ignore all the text as usual and click ‘Next’ Ignore the text again, click the ‘Accept’ radio button and click ‘Next’ You cannot change any options here except the path, so leave it at C:\Tcl (as it is going to be moved later anyway), unless on a school system, when you should use choose the root of your documents folder (usually U:\ or N:\ which represent mapped drive letters to your Documents share on the server) Click ‘Next’ Leave the demos directory alone. (C:\Tcl\Demos or YourDocumentsMappedDrive:\Tcl\demos) Page 3 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Click ‘Next’. Click ‘Next’ again You may get errors at school due to not being able to modify the start menu All Done. The highlighted text informs you the only way to un-install is through the uninstall entry added to the start menu. Nonsense: Select the folder and press the Delete key… (Only if you want to ‘uninstall’ it!) Hit ‘Finish’ Install Page-4.8.6.exe by double-clicking it: Click ‘Yes’ A full-window installer emerges with only the option for changing the path. Leave it at C:\page unless on a school system then use the root of your Documents, similar to Tcl above. Again there may be errors due to shortcut and start menu creation on school systems. Once completed drag’n’drop the Tcl folder into the page folder. The complete page folder should contain the following: Page 4 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ If you managed to get a desktop icon for page. The properties page for this icon (right-click properties) show the following Target: C:\page\winpage.bat When the icon is double-clicked it says: Open winpage.bat in a text editor: start /min wish.exe c:\page\page.tcl %1 Change it to: start /min Tcl\bin\wish.exe page.tcl %1 Save ad try again. This time a bunch of separate dialogs will open at the corners of the screen. As all absolute pathnames have been removed, this will now work wherever you move the page folder. Copy the page folder to a usb or any convenient location and you can run it directly from there. Page 5 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Adding Page to the PortableApps distribution If you use PortableApps, you will be famiiar with the interface: If you drop the page folder into the PortableApps folder, it will not show up in the menu, as there is no associated .exe file with a suitable icon. You have to make your own using C# in either SharpDevelop or Visual Studio. Create a new C# Console project in the page directory called PageLauncher. Use the icon located in \page\WIN_INSTALL as the project icon and use the following code in Program.cs: using System; using System.Linq; using System.IO; using System.Threading; namespace PageLauncher { class Program { public static void Main(string[] args) { // Make sure the source code of this development is located within the \page folder // else paths/files will not be found! Console.WriteLine("C# Launcher for Page"); string driveLetter = Path.GetPathRoot(Environment.CurrentDirectory); // X:\ Console.WriteLine("Running from drive: " + driveLetter); /* full startup of .exe eg in \bin folder possible locations: X:\PortableApps\page\PageLauncher.exe X:\PortableApps\page\PageLauncher\PageLauncher\bin\Debug\PageLauncher.exe X:\page\PageLauncher.exe */ string AppPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); Page 6 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Console.WriteLine("PageLauncher.exe (C# launcher) located at:\n\t" + AppPath + @"\PageLauncher.exe"); string PagePath = AppPath.Substring(0, AppPath.IndexOf(@"\page")) + @"\page"; string TclPath = PagePath + @"\Tcl"; if(Directory.Exists(TclPath)) { Console.WriteLine("Wish.exe (executable) located at:\n\t" + TclPath + @"\bin\wish.exe"); Console.WriteLine("\nLaunching Page\n\nHold tight!"); try { //Mimic batch file contents: start /min Tcl\bin\wish.exe page.tcl %1 System.Diagnostics.Process startPage = new System.Diagnostics.Process(); startPage.StartInfo.FileName = TclPath + @"\bin\wish.exe"; startPage.StartInfo.Arguments = PagePath + @"\page.tcl"; startPage.Start(); Console.WriteLine("\nClosing in 10 seconds..."); Thread.Sleep(10000); } catch (Exception e) {Console.WriteLine(e.Message); Console.WriteLine("Press enter to exit"); Console.ReadLine(); } } else { Console.WriteLine("Cannot find directory " + TclPath); Console.WriteLine("Install or copy TCL into page directory"); Console.WriteLine("Available from: http://www.activestate.com/activetcl/downloads"); Console.WriteLine("Press enter to exit"); Console.ReadLine(); } } } } Copy the compiled .exe from the \bin folder to the root of the \page folder. It will now have an entry in the PortableApps Menu: Page 7 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Comparison of the use of Page for those familiar with Visual Studio and Java Forms Builder C# Take an example of a simple form with a button, label and textbox: This was made on the SharpDevelop IDE in a few minutes. (Launch from PortableApps) - Use the default 300 x 300 design form - Add a button and set the following properties from the property panel: Name btnDemo Font Segoe UI, 9.75pt Location 70,35 Size 150,40 Text Click Me! Click (Event) BtnDemoClick - Add a TextBox and set the propeties: Name txtDemo Font Segoe UI, 8.25pt Location 12,100 Size 270,22 - Add a label: Name lblDemo BorderStyle Fixed3D Font Segoe UI, 8.25pt Location 12,170 Size 270,30 Text Clicking the button will copy the textbox text here TextAlign MiddleCenter Page 8 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ This generates the designer code behind the scenes as follows: private System.Windows.Forms.TextBox txtDemo; private System.Windows.Forms.Label lblDemo; private System.Windows.Forms.Button btnDemo; private void InitializeComponent() { this.txtDemo = new System.Windows.Forms.TextBox(); this.lblDemo = new System.Windows.Forms.Label(); this.btnDemo = new System.Windows.Forms.Button(); this.SuspendLayout(); // txtDemo this.txtDemo.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.txtDemo.Location = new System.Drawing.Point(12, 100); this.txtDemo.Name = "txtDemo"; this.txtDemo.Size = new System.Drawing.Size(270, 22); this.txtDemo.TabIndex = 0; // lblDemo this.lblDemo.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.lblDemo.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblDemo.Location = new System.Drawing.Point(12, 170); this.lblDemo.Name = "lblDemo"; this.lblDemo.Size = new System.Drawing.Size(270, 30); this.lblDemo.TabIndex = 1; this.lblDemo.Text = "Clicking the button will copy the textbox text here"; this.lblDemo.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // btnDemo this.btnDemo.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnDemo.Location = new System.Drawing.Point(70, 35); this.btnDemo.Name = "btnDemo"; this.btnDemo.Size = new System.Drawing.Size(150, 40); this.btnDemo.TabIndex = 2; this.btnDemo.Text = "Click Me!"; this.btnDemo.UseVisualStyleBackColor = true; this.btnDemo.Click += new System.EventHandler(this.BtnDemoClick); // MainForm this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(296, 261); this.Controls.Add(this.btnDemo); this.Controls.Add(this.lblDemo); this.Controls.Add(this.txtDemo); this.Name = "MainForm"; this.Text = "Cross Platform Demo"; this.ResumeLayout(false); this.PerformLayout(); } If you study this code it follows the pattern: - Declare the GUI element as an object variable: private System.Windows.Forms.Button btnDemo; - Create the object: this.btnDemo = new System.Windows.Forms.Button(); - Set the properties of this variable: this.btnDemo.Name = "btnDemo"; etc. etc. - Create the event handler: this.btnDemo.Click += new System.EventHandler(this.BtnDemoClick); - Add the GUI element to the layout: this.Controls.Add(this.btnDemo); Page 9 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Repeat using Eclipse WindowBuilder Java The same project created with the same properties takes a bit longer, but gives the following GUI designer code (The declarations have been moved out of ‘ initialise()’ so they can be accessed from outside the function. Highlighted lines have been added or modified): private JFrame frame; private JTextField txtDemo; private JButton btnDemo; private JLabel lblDemo; private void initialize() { frame = new JFrame(); frame.setTitle("Cross Platform Demo"); frame.setResizable(false); frame.setBounds(100, 100, 310, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setLayout(null); btnDemo = new JButton("Click Me!"); btnDemo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { lblDemo.setText(txtDemo.getText()); } }); btnDemo.setFont(new Font("Segoe UI", Font.PLAIN, 12)); btnDemo.setBounds(70, 35, 150, 40); frame.getContentPane().add(btnDemo); lblDemo = new JLabel("Clicking the button will copy the textbox text here"); lblDemo.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null)); lblDemo.setHorizontalTextPosition(SwingConstants.CENTER); lblDemo.setHorizontalAlignment(SwingConstants.CENTER); lblDemo.setBounds(12, 170, 270, 30); frame.getContentPane().add(lblDemo); txtDemo = new JTextField(); txtDemo.setFont(new Font("Segoe UI", Font.PLAIN, 11)); txtDemo.setBounds(12, 100, 270, 22); frame.getContentPane().add(txtDemo); txtDemo.setColumns(10); } If you study this code it follows the pattern: - Declare the GUI element as an object variable: private JButton btnDemo; - Create the object: btnDemo = new JButton("Click Me!"); - Set the properties of this variable: btnDemo.setBounds(70, 35, 150, 40); - Create the event handler: btnDemo.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0){}}); - Add the GUI element to the layout: frame.getContentPane().add(btnDemo); The main difference here is the events for the button Click in C# are declared in the designer, but coded in the MainForm source file, to encourage programmers NOT to alter the design code. In Java it is all thrown together and left to the programmer to create new classes or functions to handle the event code. The line lblDemo.setText(txtDemo.getText()); is written directly into the event Page 10 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ handler. You can of course write a new function to do this, and call the function in the event handler instead. The equivalent line lblDemo.Text = txtDemo.Text; in C# is found in the separate source file Program.cs: using System; using System.Windows.Forms; namespace Cross_Platform_Demo { public partial class MainForm : Form { public MainForm() { InitializeComponent(); this.CenterToScreen();} void BtnDemoClick(object sender, EventArgs e) { lblDemo.Text = txtDemo.Text; } } } Repeat using Page and Tkinter Python/Tkinter Start Page and click the Toplevel icon in the Widget Toolbar: Find the Attribute Editor and change the following: Widget: Alias MainForm Attributes: title Cross Platform Demo Geometry:width 300 Geometry:height 270 Geometry:resize width No Geometry:resize height No Go back to the Widget Toolbar and Click on the Themed widgets Tbutton icon Click inside your new window, now resized and re-titled. The button will be drawn. From the Attribute Editor change: Page 11 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Alias btnDemo command btnDemoClick text Click Me! Geometry: x position 70 Geometry: y position 35 Geometry:width 150 Geometry:height 40 You should now have: Go back to the Widget Toolbar and Click on the Themed widgets TEntry icon (The Tkinter designers obviously did not think of using TextBox as a name used since the invention of Visual basic in the 1980s) Click in your design window to draw the textbox (Entry). From the Attribute Editor change: Alias txtDemo font Segoe UI 10 Geometry: x position 12 Geometry: y position 100 Geometry:width 270 Geometry:height 30 Page 12 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Go back to the Widget Toolbar and Click on the Themed widgets TLabel icon Click in your design window to draw the Label From the Attribute Editor change: Alias lblDemo Anchor center relief sunken text Clicking the button will copy the textbox text here Geometry: x position 12 Geometry: y position 170 Geometry:width 270 Geometry:height 30 On the main PAGE menu Gen_Python Generate Python Gui This will also open a file selector dialog prompting you to save the project. Page 13 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ Choose an appropriate folder and filename to save the design in .tcl format. (I used demo as the filename) The Python file will open with the same name as the .tcl file you just saved. (demo.py) Go back to the menu and use Gen_Python Generate Support Module. Click the Save button on both these Python code windows. Going through the code in sections: #! /usr/bin/env python # # GUI module generated by PAGE version 4.8.6 # In conjunction with Tcl version 8.6 # Jan 14, 2017 07:33:42 PM import sys The above code is not required and can be deleted try: from Tkinter import * except ImportError: from tkinter import * try: import ttk py3 = 0 except ImportError: import tkinter.ttk as ttk py3 = 1 The above code checks which version of Python you are using. Assuming you are on Python 3 this can be changed to: from tkinter import * import tkinter.ttk as ttk The next statement imports the second Python file created by Page: import demo_support The following three functions are used when starting from the support module: def vp_start_gui(): '''Starting point when module is the main routine.''' global val, w, root root = Tk() top = Cross_Platfom_Demo (root) demo_support.init(root, top) root.mainloop() w = None def create_Cross_Platfom_Demo(root, *args, **kwargs): '''Starting point when module is imported by another program.''' global w, w_win, rt rt = root w = Toplevel (root) top = Cross_Platfom_Demo (w) demo_support.init(w, top, *args, **kwargs) return (w, top) def destroy_Cross_Platfom_Demo(): global w w.destroy() w = None Page 14 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ The above lines can be deleted, as the Demo project will always start from the main module. The class below is the impotrant bit: class Cross_Platfom_Demo: def __init__(self, top=None): '''This class configures and populates the toplevel window. top is the toplevel containing window.''' self._bgcolor = '#d9d9d9' # X11 color: 'gray85' self._fgcolor = '#000000' # X11 color: 'black' self._compcolor = '#d9d9d9' # X11 color: 'gray85' self._ana1color = '#d9d9d9' # X11 color: 'gray85' self._ana2color = '#d9d9d9' # X11 color: 'gray85' self.font3 = "-family {Segoe UI} -size 10 -weight normal -slant roman -underline 0 -overstrike 0" self.style = ttk.Style() if sys.platform == "win32": self.style.theme_use('winnative') self.style.configure('.',background=self._bgcolor) self.style.configure('.',foreground=self._fgcolor) self.style.configure('.',font="TkDefaultFont") self.style.map('.',background=[('selected', self._compcolor), ('active',self._ana2color)]) top.geometry("300x270+0+0") top.title("Cross Platfom Demo") top.configure(background="#d9d9d9") top.configure(highlightbackground="#d9d9d9") top.configure(highlightcolor="black") self.btnDemo = ttk.Button(top) self.btnDemo.place(relx=0.23, rely=0.12, height=40, width=150) self.btnDemo.configure(command=demo_support.btnDemoClick) self.btnDemo.configure(takefocus="") self.btnDemo.configure(text='''Click Me!''') self.txtDemo = ttk.Entry(top) self.txtDemo.place(relx=0.03, rely=0.33, relheight=0.1, relwidth=0.9) self.txtDemo.configure(font=self.font3) self.txtDemo.configure(takefocus="") self.txtDemo.configure(cursor="ibeam") self.lblDemo = ttk.Label(top) self.lblDemo.place(relx=0.03, rely=0.57, height=30, width=270) self.lblDemo.configure(background="#d9d9d9") self.lblDemo.configure(foreground="#000000") self.lblDemo.configure(relief=SUNKEN) self.lblDemo.configure(anchor=CENTER) self.lblDemo.configure(text='''Clicking the button will copy the textbox text here''') The code below is used to check if the script is launched from this module, then runs the function above: vp_start_gui() if __name__ == '__main__': vp_start_gui() At first, using the two scripts seems to run along the same lines as the C# version, with the GUI design in one, and the events in the other, but it does not work well when trying to fill the label with the contents of the textbox, as you are constantly cross-referencing the two modules. It makes much more sense to keep all the code used in the GUI design class, including events, similar to the Java equivalent used earlier Page 15 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ To make this script work change the Demo.py script: 1) Move the text in the function vp_start_gui() to the bottom of the script and delete the function definition and the call to it after if __name__ == '__main__': 2) Delete everything above the class declaration except the top two lines highlighted 3) Modify self.btnDemo.configure() as highlighted 4) Add the function def btnDemoClick() as highlighted below (It can be cut from the Demo_support.py file and modified) from tkinter import * import tkinter.ttk as ttk class Cross_Platfom_Demo: def __init__(self, top=None): '''This class configures and populates the toplevel window. top is the toplevel containingwindow.''' self._bgcolor = '#d9d9d9' # X11 color: 'gray85' self._fgcolor = '#000000' # X11 color: 'black' self._compcolor = '#d9d9d9' # X11 color: 'gray85' self._ana1color = '#d9d9d9' # X11 color: 'gray85' self._ana2color = '#d9d9d9' # X11 color: 'gray85' self.font3 = "-family {Segoe UI} -size 10 -weight normal -slant roman -underline 0 -overstrike 0" self.style = ttk.Style() if sys.platform == "win32": self.style.theme_use('winnative') self.style.configure('.',background=self._bgcolor) self.style.configure('.',foreground=self._fgcolor) self.style.configure('.',font="TkDefaultFont") self.style.map('.',background=[('selected', self._compcolor), ('active',self._ana2color)]) top.geometry("300x270+0+0") top.title("Cross Platfom Demo") top.configure(background="#d9d9d9") top.configure(highlightbackground="#d9d9d9") top.configure(highlightcolor="black") self.btnDemo = ttk.Button(top) self.btnDemo.place(relx=0.23, rely=0.12, height=40, width=150) self.btnDemo.configure(command=self.btnDemoClick) self.btnDemo.configure(takefocus="") self.btnDemo.configure(text='''Click Me!''') self.txtDemo = ttk.Entry(top) self.txtDemo.place(relx=0.03, rely=0.33, relheight=0.1, relwidth=0.9) self.txtDemo.configure(font=self.font3) self.txtDemo.configure(takefocus="") self.txtDemo.configure(cursor="ibeam") self.lblDemo = ttk.Label(top) self.lblDemo.place(relx=0.03, rely=0.57, height=30, width=270) self.lblDemo.configure(background="#d9d9d9") self.lblDemo.configure(foreground="#000000") self.lblDemo.configure(relief=SUNKEN) self.lblDemo.configure(anchor=CENTER) self.lblDemo.configure(text='''Clicking the button will copy the textbox text here''') def btnDemoClick(self): self.lblDemo.configure(text=self.txtDemo.get()) if __name__ == '__main__': global root root = Tk() top = Cross_Platfom_Demo(root) root.mainloop() Page 16 of 16 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License http://creativecommons.org/licenses/by-nc-sa/4.0/ The Demo_support.py file can now be deleted. You now have a skeleton design, ready for filling in the class with additional methods to handle the widgets you place in the designer. The output from the three versions side by side: C# Java Tkinter The use of the Tkinter .place() method is close to the positioning methods used in C# and Java WindowBuilder.
Compartilhar