Introduction:This series of articles, composed of two parts, explores how to use python to create scripts to use KVM to manage virtual machines. In this section, you will learn how to add a GUI to expand simple state and display tools.
This series
Use python to write scripts for KVM. Part 1: libvirtlibvirt
Basic knowledge of writing kernel-based Virtual Machine (KVM) scripts with python. This section uses the concepts introduced in the previous section to build some utility applications and add a graphical user interface (GUI ). There are two main types of GUI toolkit that are both Python-bound and cross-platform. The first is QT, which is now owned by Nokia; the second is wxpython. Both have a large number of supporters, and many open-source projects are using them.
For personal preferences, I will focus on wxpython in this article. First, we will briefly introduce wxpython and the basic knowledge of correct settings. Provide some short sample programs, and thenlibvirt
Integration. This method should provide enough basic wxpython knowledge for you to build a simple program and expand the program to add features. We hope you can master these concepts and expand them to meet your specific needs.
Wxpython Basics
A good starting point is to start with some basic definitions. The wxpython library is actually based onC++
WxWidgets. In the context of creating a GUIPartsIn essence, it is a building block. The top layer of the Component Hierarchy contains five independent parts:
wx.Frame
,
wx.Dialog
,
wx.PopupWindow
,
wx.MDIParentFrame
And
wx.MDIChildFrame
.
Most examples here are based onwx.Frame
Because it essentially implements a single mode window.
In wxpython, You Can instantiate it as isFrame
Class, or inherit it to add or enhance features. Be sure to understand how components are displayed in a frame so that you know how to place them correctly. The layout is determined by absolute positioning or by using the regulator.RegulatorIt is a convenient tool. When you click and drag one side or one corner to change the size of the window, it will adjust the part size.
The simplest form of the wxpython program must have some code lines for setting. A typical main routine may be similar
List 1.
Listing 1. device XML definition
if __name__ == "__main__": app = wx.App(False)frame = MyFrame()frame.Show()app.MainLoop() |
Each wxpython application iswx.App()
Instance, which must be instantiated as shown in Listing 1. WhenFalse
Pass
wx.App
It indicates "Do not redirect stdout and stderr to a window ". The next line is instantiated.MyFrame()
Class to create a framework. Then the framework is displayed and the control is forwarded
app.MainLoop()
.MyFrame()
Class usually contains__init__
Function to use the part you selected to initialize the framework. You will also connect any part events here to their correct processing functions.
Now it is necessary to mention a convenient debugging tool with wxpython. This tool is calledPart Check Tool(See
Figure 1). You only need two lines of code. First, you must use the following code to import it:
Next, you only need to callShow()
Function:
wx.lib.inspectin.InspectionTool().Show() |
ClickEventsThe icon displays the event dynamically when the event is activated. If you are not sure what events are supported by a specific part, this is a real quick way to view events when they occur. When an application is running, it also helps you better understand what is happening behind the scenes.
Figure 1. wxpython part Check Tool
Back to Top
Add GUI to the command line tool
This series
Use python to write scripts for KVM. Part 1: libvirt provides a simple tool to display the status of all running virtual machines (VMS. With wxpython, you can easily change this tool to a GUI tool.wx.ListCtrl
A widget provides the functions required to display information in a list. To use
wx.ListCtrl
Part, you must add it to your framework using the following syntax:
self.list=wx.ListCtrl(frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER) |
You can choose from a variety of different styles, includingwx.LC_REPORT
Andwx.SUNKEN_BORDER
. The first option sets
wx.ListCtrl
Set to report mode, which is one of the four available modes. Other options include icons, icons, and lists. To addwx.SUNKEN_BORDER
You only need to use the vertical bar (|
). Some styles are mutually exclusive, such as different border styles, so if you have any concerns, please refer to wxpython Wiki (see
References ).
Instantiationwx.ListCtrl
After the widget, you can add content to it, such as the column title.InsertColumn
The method has two mandatory parameters and two optional parameters. The first is the column index, which starts from 0, followed by a string that sets the title. The third is used for formatting, which should be similar
LIST_FORMAT_CENTER
,_LEFT
Or_RIGHT
. Finally, you can input an integer to set the fixed width or use
wx.LIST_AUTOSIZE
Automatically adjust columns.
Now you have configuredwx.ListCtrl
Parts, you can useInsertStringItem
And
SetStringItem
Method to fill it with data.wx.ListCtrl
Every new line in the part must be usedInsertStringItem
Method. Two mandatory Parameters specify where to insert, including the value 0 inserted at the top of the list and the string to insert at this position.InsertStringItem
Returns an integer that indicates the number of rows inserted into the string. You can call
GetItemCount()
, Use the return value to append the index to the bottom, as shown in figure
List 2.
List 2. Gui version of the command line tool
import wximport libvirtconn=libvirt.open("qemu:///system")class MyApp(wx.App): def OnInit(self): frame = wx.Frame(None, -1, "KVM Info") id=wx.NewId() self.list=wx.ListCtrl(frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER) self.list.Show(True) self.list.InsertColumn(0,"ID") self.list.InsertColumn(1,"Name") self.list.InsertColumn(2,"State") self.list.InsertColumn(3,"Max Mem") self.list.InsertColumn(4,"# of vCPUs") self.list.InsertColumn(5,"CPU Time (ns)") for i,id in enumerate(conn.listDomainsID()): dom = conn.lookupByID(id) infos = dom.info() pos = self.list.InsertStringItem(i,str(id)) self.list.SetStringItem(pos,1,dom.name()) self.list.SetStringItem(pos,2,str(infos[0])) self.list.SetStringItem(pos,3,str(infos[1])) self.list.SetStringItem(pos,4,str(infos[3])) self.list.SetStringItem(pos,5,str(infos[2])) frame.Show(True) self.SetTopWindow(frame) return Trueapp = MyApp(0)app.MainLoop() |
Figure 2 shows the results of these jobs.
Figure 2. Gui KVM information tool
You can improve the appearance of the table. A major improvement may be the re-adjustment of columns. For this reason, you canwidth =
Add parametersInsertColumn
Calling or using a line of code, such:
self.ListCtrl.SetColumnWidth(column,wx.LIST_AUTOSIZE) |
Another thing you can do is add a regulator so that the control can be adjusted according to the parent window. Therefore, you can usewxBoxSizer
. First, create the regulator and add the part you want to adjust for the main window to it. The possible code is as follows:
self.sizer = wx.BoxSizer(wx.VERTICAL)self.sizer.Add(self.list, proportion=1,flag=wx.EXPAND | wx.ALL, border=5)self.sizer.Add(self.button, flag=wx.EXPAND | wx.ALL, border=5)self.panel.SetSizerAndFit(self.sizer) |
Last pairself.panel.SetSizerAndFit()
Wxpython is required to set the initial size of the pane based on the minimum size of the embedded part regulator. This helps you provide an initial screen of reasonable size based on the screen content.
Back to Top
Control flow based on user operations
Aboutwx.ListCtrl
One benefit of a widget is that you can detect when a user clicks a specific part of the widget and perform an operation based on the information. This feature allows you to sort columns in a forward or backward direction alphabetically by clicking the column title. A callback mechanism is used to complete this task. You must provide a function that binds parts with the processing method to process each operation you want to process. Therefore
Bind
Method.
Each part has a certain number of associated events. There are also events associated with objects such as the mouse. The mouse event hasEVT_LEFT_DOWN
,EVT_LEFT_UP
And
EVT_LEFT_DCLICK
Such name and naming conventions are the same as those of other buttons. You canEVT_MOUSE_EVENTS
Type to process all mouse events. The difficulty lies in capturing events in the context of the application or window you are interested in.
When the control is passed to the event processing function, it must perform the necessary steps to process the operation, and then return the control to its previous location. This is an event-driven programming model. Every GUI must implement it to process user operations in a timely manner. Many modern GUI applications implement multithreading to avoid making the user feel that the program has no response. This topic will be briefly introduced later.
A timer represents another type of event that a program may have to handle. For example, you may want to perform regular monitoring at user-defined intervals. You will need to provide a screen where you can specify the interval and then start a timer to trigger an event when it expires. An event is triggered when the timer expires. You can use this event to activate a piece of code. You may need to set or re-start timing based on your preferences. You can easily use this technology to develop VM monitoring tools.
Listing 3 provides a simple demo application that contains buttons and static text lines. Use
wx.StaticText
Is an easy way to output strings to a window. The idea is to click this button once to start a timer, record the start time, and change the label
Stop. Click the button again to enter the end time text box and change the button back.Start.
Listing 3. Simple Applications that contain buttons and static text
import wxfrom time import gmtime, strftimeclass MyForm(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, "Buttons") self.panel = wx.Panel(self, wx.ID_ANY) self.button = wx.Button(self.panel, id=wx.ID_ANY, label="Start") self.button.Bind(wx.EVT_BUTTON, self.onButton) def onButton(self, event): if self.button.GetLabel() == "Start": self.button.SetLabel("Stop") strtime = strftime("%Y-%m-%d %H:%M:%S", gmtime()) wx.StaticText(self, -1, 'Start Time = ' + strtime, (25, 75)) else: self.button.SetLabel("Start") stptime = strftime("%Y-%m-%d %H:%M:%S", gmtime()) wx.StaticText(self, -1, 'Stop Time = ' + stptime, (25, 100)) if __name__ == "__main__": app = wx.App(False) frame = MyForm() frame.Show() app.MainLoop() |
Back to Top
Enhanced monitoring Gui
Now you can add the simple monitoring GUI described above. Before you have everything you need to create an application, you need to understand another aspect of wxpython. Directionwx.ListCtrl
Add a check box on the first line of the widget to perform operations on multiple lines based on the check box status. Therefore, you can use
Mixin. In essence,MixinIs a helper class that adds a certain type of functionality to the parent component. To add a check box Mixin, you only need to use the following code to instantiate it:
listmix.CheckListCtrlMixin.__init__(self) |
You can also use the event to add and click the column title to select or clear all check boxes. In this way, you can start or stop all VMS with just a few clicks. You need to write some event handler functions to respond to the appropriate events in the same way as changing the button tag. The following is the code line required to set the column to click the event processing function:
self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list) |
wx.EVT_LIST_COL_CLICK
Triggered when you click any column title. You can useevent.GetColumn()
Method. Below is
OnColClick
A simple event processing function:
def OnColClick(self, event): print "column clicked %d\n" % event.GetColumn()event.Skip() |
If you want to spread events to other processing functions,event.Skip()
Call is very important. Although it may not be needed in this instance, when multiple processing functions need to process the same event, it may cause problems if they are not used. The wxpython wiki site has a good discussion of event propagation, which is much more detailed than what I have described here.
Finally, add code to the two buttons to start or stop all selected VMS. Just a few lines of code can be iteratedwx.ListCtrl
And obtain the vm id, as shown in figure
List 4.
Listing 4. Start and Stop the selected VM
#!/usr/bin/env pythonimport wximport wx.lib.mixins.listctrl as listmiximport libvirtconn=libvirt.open("qemu:///system")class CheckListCtrl(wx.ListCtrl, listmix.CheckListCtrlMixin, listmix.ListCtrlAutoWidthMixin): def __init__(self, *args, **kwargs): wx.ListCtrl.__init__(self, *args, **kwargs) listmix.CheckListCtrlMixin.__init__(self) listmix.ListCtrlAutoWidthMixin.__init__(self) self.setResizeColumn(2)class MainWindow(wx.Frame): def __init__(self, *args, **kwargs): wx.Frame.__init__(self, *args, **kwargs) self.panel = wx.Panel(self) self.list = CheckListCtrl(self.panel, style=wx.LC_REPORT) self.list.InsertColumn(0, "Check", width = 175) self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list) self.list.InsertColumn(1,"Max Mem", width = 100) self.list.InsertColumn(2,"# of vCPUs", width = 100) for i,id in enumerate(conn.listDefinedDomains()): dom = conn.lookupByName(id) infos = dom.info() pos = self.list.InsertStringItem(1,dom.name()) self.list.SetStringItem(pos,1,str(infos[1])) self.list.SetStringItem(pos,2,str(infos[3])) self.StrButton = wx.Button(self.panel, label="Start") self.Bind(wx.EVT_BUTTON, self.onStrButton, self.StrButton) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.list, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self.sizer.Add(self.StrButton, flag=wx.EXPAND | wx.ALL, border=5) self.panel.SetSizerAndFit(self.sizer) self.Show() def onStrButton(self, event): if self.StrButton.GetLabel() == "Start": num = self.list.GetItemCount() for i in range(num): if self.list.IsChecked(i): dom = conn.lookupByName(self.list.GetItem(i, 0).Text) dom.create() print "%d started" % dom.ID() def OnColClick(self, event): item = self.list.GetColumn(0) if item is not None: if item.GetText() == "Check": item.SetText("Uncheck") self.list.SetColumn(0, item) num = self.list.GetItemCount() for i in range(num): self.list.CheckItem(i,True) else: item.SetText("Check") self.list.SetColumn(0, item) num = self.list.GetItemCount() for i in range(num): self.list.CheckItem(i,False) event.Skip() app = wx.App(False)win = MainWindow(None)app.MainLoop() |
In terms of VM status in KVM, there are two highlights: when usinglibvirt
InlistDomainsID()
Method, the running VM is displayed. To view machines that are not running, you must use
listDefinedDomains()
. To know which VMS can be started and which ones can be stopped, you must keep the two parts independent.
Back to Top
Conclusion
This document describes how to use wxpython to build a GUI package.libvirt
To manage KVM. The wxpython library has rich functions and provides many components to support building Gui-based applications with professional appearances. This article only introduces a small part of the features, and we hope you can further explore them. Please refer to more
References to help your applications run properly.
References
Learning