Chapter 4

Graphical Programming

IN THE LAST chapter, we dealt a lot with how to handle data, and how to process it. After all, manipulating data is the fundamental job of a computer. You saw how to build a simple text-driven menu to help control the program. However, such interfaces went out of style in the 1980s. They still have a use in some applications, but these days, most people want to use a mouse (or touch screen).

There are modules using three different graphical toolkits that you're likely to come across—Tk, GTK, and Qt. Tk is quite an old fashioned library that is still used, but lacks some modern features. GTK is a popular toolkit, and the one that LXDE (the default Raspbian desktop) is built in. Qt (sometimes pronounced cute) is a toolkit that was originally developed by Nokia for their ill-fated smartphones. Nokia has since sold it to Digia who continues to develop it. Both GTK and Qt are free to use, and to be honest, there's not much to choose between them. This chapter uses Qt because it's a bit more portable and it's a bit better supported.

You'll need to install the pyside module before you can start using it. In LXTerminal, enter the following:

sudo apt-get install python3-pyside

This may take a little while, so you might want to get a cup of tea.

Graphical User Interface (GUI) Programming

Throughout this book you're going to learn that you can create things in Python very easily if you let the right modules take care of the hard work. Graphical User Interface (GUI) programming is no different. All manner of useful widgets are available; all you have to do is pick which ones you want and add them to your project.

You can also use inheritance. In the last chapter, we introduced classes, and showed that you can create a new class that inherits all the features of a superclass. Here, you'll see how to use this to quickly create new classes that build upon the old ones.

Let's get straight into an example. In Chapter 2 you saw the turtle module, and even how to set it to listen for keypresses. This is a little better than basic text entry, but not by much, so in the first example here, you'll see how to create a simple GUI to control the turtle. Start with the following code (either enter it by hand, or find it in file chapter4-turtle-start.py on the website):

import turtle
import sys
from PySide.QtCore import *
from PySide.QtGui import *
 
class TurtleControl(QWidget):
    def _  _init_  _(self, turtle):
        super(TurtleControl, self)._  _init_  _()
        self.turtle = turtle
 
        self.left_btn = QPushButton("Left", self)
        self.right_btn = QPushButton("Right", self)
        self.move_btn = QPushButton("Move", self)
        self.distance_spin = QSpinBox()
        
        self.controlsLayout = QGridLayout()
        self.controlsLayout.addWidget(self.left_btn, 0, 0)
        self.controlsLayout.addWidget(self.right_btn, 0, 1)
        self.controlsLayout.addWidget(self.distance_spin,1 , 0)
        self.controlsLayout.addWidget(self.move_btn,1 , 1)
        self.setLayout(self.controlsLayout)
 
        self.distance_spin.setRange(0, 100)
        self.distance_spin.setSingleStep(5)
        self.distance_spin.setValue(20)
 
#set up turtle
window = turtle.Screen()
babbage = turtle.Turtle()
 
# Create a Qt application
app = QApplication(sys.argv)
control_window = TurtleControl(babbage)
control_window.show()
 
# Enter Qt application main loop
app.exec_()
sys.exit()

You can run it now, but none of the buttons will do anything (you'll add that in a bit). First of all, let's take a look at what's going on here. The main part of the code is in the class TurtleControl, which inherits from Qwidget (most of the Qt classes start with the letter Q). By extending from this class, you get all the basic functionality you need. All that you have to do is change it from a generic widget into one that fits the specific needs of this program. In short, you just have to tell it what items you want where.

There are three buttons and a spinbox (allows you to enter a number and raise and lower it—take a look at the running program to see how a spinbox works).

In addition to the items that the user will see, there's also a layout that you add these to. Qt has a few different layouts (you'll see another one later), but this program uses the QGridLayout. The grid layout is great for simple control panels like this one. It works on the basis of dividing the window up into a grid, and you tell Qt where you want the item to go in the grid. If the user resizes the window, Qt dynamically resizes the grid to take advantage of the extra space, but still keeping everything in the right portion of the grid.

To display any of the widgets on in the window, you have to add them to the layout. These are the lines like this:

self.controlsLayout.addWidget(self.right_btn, 0, 1)

The 0 and 1 are the horizontal and vertical coordinates taken from the top-left corner (that is, upside down when compared to graph coordinates). This button, then, is on the top line, one column across from the left side.

When everything's added to the layout, you need to tell the window to use that layout. This is done with the line:

self.setLayout(self.controlsLayout)

There are also some settings for widgets that you can change to alter their behavior. In this case, the spinbox is adjusted with the following:

self.distance_spin.setRange(0, 100)
self.distance_spin.setSingleStep(5)
self.distance_spin.setValue(20)

This sets the minimum and maximum values, the amount each click moves it, and the initial value.

Hopefully, you'll recognise the turtle code from before. The last five lines just create the control window and execute it.

Adding Controls

All this code has created a nice looking control window, but it doesn't actually do anything. The next stage, then, is to tell Qt what you want the controls to do. This is done by connecting an event with an action. The events here will be button clicks, and actions will be the methods you want to run when that event happens.

To set this up, add the following code to the end of the _  _init_  _ method of the TurtleControl class:

    self.move_btn.clicked.connect(self.move_turtle)
    self.right_btn.clicked.connect(self.turn_turtle_right)
    self.left_btn.clicked.connect(self.turn_turtle_left)
        
def turn_turtle_left(self):
    self.turtle.left(45)
    
def turn_turtle_right(self):
    self.turtle.right(45)
    
def move_turtle(self):
    self.turtle.forward(self.distance_spin.value())

You'll notice that in each of the connect calls, the method in the parameter doesn't have any brackets after it like methods normally do. That is, it's this:

self.move_btn.clicked.connect(self.move_turtle)

Rather than this:

self.move_btn.clicked.connect(self.move_turtle())

This is because when you put the brackets after it, you're telling Python to run the method and send the result as a parameter, as so:

def move_turtle(self):
    self.turtle.forward(self.distance_spin.value())

However, when you don't put the brackets after the method, you're telling Python to send the method itself as the parameter. That's what you need to do here so Qt knows what to run when the event happens.

You'll find the complete code on the website as chapter4-turtle.py. You can see it running in Figure 4-1.

Figure 4-1: A mouse-powered interface to a turtle.

9781118717059-fg0401.tif

That's more or less all the basics of PySide and Qt. It doesn't get much more complex, but there are a huge number of widgets. We won't be able to demonstrate them all, but in the next example, we'll try to show you a large enough range that you get a feel for the toolkit and you should then be able to use the other widgets as you need them.

Test Your Knowledge

Exercise 1

Extend the turtle controller program so that you can change the colour of the turtle as well as move it. We'll give you a hint to get you started. If you change the set up turtle lines to:

#set up turtle
window = turtle.Screen()
babbage = turtle.Turtle()
window.colormode(255)

Then you'll be able to set the turtle's colour with red, green, and blue values between 1 and 255, such as:

turtle.color(100,0,0)

Another thing you may find useful are QLablels. They let you add pieces of text to the window, and are created like this:

self.red_label = QLabel("Red", self)

They might be useful for labeling spinboxes (nudge, nudge, wink, wink).

Creating a Web Browser

In the previous example, you saw how easy it was to link things together to create an interface. In this example, we'll use Qt's widgets to build our own web browser (which is really just a set of widgets linked together). You'll see that you don't really need any programming at all; it'll just be linking together different parts of Qt.

First of all you need to create a window for the browser. In the previous example, you created a widget that Qt put in a window for you. That's fine for simple tools, but as you build more powerful applications, it can help to create the window explicitly and add everything to that. By starting with a QMainWindow, you can add things like menus. However, that's jumping ahead, and there's quite a bit to add before menus.

The first and most important part of any web browser is the bit that actually shows the web page. You'll learn a bit more about what's actually involved in web pages in Chapter 7, but for the purposes of this chapter, all you need to know is that the QWebView widget can take care of all that for you.

In the previous example, you used a grid layout. This works well for adding a lot of controls to a window, but in this application, you're going to use a box layout. This is a little different. It's created using two different layouts: QVBoxLayout and QHBoxLayout. These are vertical and horizontal boxes, respectively. As you add items to one of these box layouts, they are placed next to each other horizontally or vertically. To create complex layouts, you just need to nest these layouts inside each other in the appropriate way. It can take a little while to get used to this way of laying out widgets on windows, but once you become familiar with it, it's incredibly powerful.

The web browser will have a typical web browser layout. That is, a bar of controls along the top, then most of the window will be taken up with the web page you're viewing. To create this interface, you'll need two layouts—a QHBoxLayout for the controls, and then a QVBoxLayout that'll take both the previous layout box and the QWebView widget. As you resize the window, Qt will adjust the layouts so that the widgets always make the best use of the space.

Hopefully, this will all become clear as you create the browser, so let's get started! The following code creates the window, and adds the appropriate layouts (the file is on the website as chapter4-web-browser-begin.py).

import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *
 
class Browser(QWidget):
  
    def _  _init_  _(self):
        super(Browser, self)._  _init_  _()
 
        self.webview = QWebView(self)
        self.webview.load("http://www.google.com")
        self.setGeometry(0, 0, 800, 600)
 
        self.menu_bar = QHBoxLayout()
        self.main_layout = QVBoxLayout()
        self.main_layout.addLayout(self.menu_bar)
        self.main_layout.addWidget(self.webview)
        self.setLayout(self.main_layout)

class BrowserWindow(QMainWindow):
    def _  _init_  _(self):
        super(BrowserWindow, self)._  _init_  _()
        self.widget = Browser()
        self.setCentralWidget(self.widget)
 
 
# Create a Qt application
app = QApplication(sys.argv)
window = BrowserWindow()
window.show()
 
# Enter Qt application main loop
app.exec_()
sys.exit()

This is all the code you need for a really simple web browser. You can run it and it'll start with the Google home page, and you can navigate from there (see Figure 4-2). The code should look familiar to you. The only new pieces are the QMainWindow (which will allow you a bit more control later on), the QWebView (which, as you can see, is a really easy way to add web browsing), and the box layouts.

Figure 4-2: The basics of a web browser.

9781118717059-fg0402.tif

The box layouts are now fully set up; all you need to do is add items to Browser's self.menu_bar and they'll appear along the top of the screen.

The most basic controls for web browsing are back and forwards buttons. For this task, you can use QPushButtons in the same way you used them in the previous example. Update your code for the Browser class to the following by adding the lines in bold:

class Browser(QWidget):
  
    def _  _init_  _(self):
        super(Browser, self)._  _init_  _()
 
        self.webview = QWebView(self)
        self.webview.load("http://www.google.com")
        self.setGeometry(0, 0, 800, 600)
        
        self.back_btn = QPushButton("<", self)
        self.back_btn.clicked.connect(self.webview.back)
        self.back_btn.setMaximumSize(20,20)
 
        self.forward_btn = QPushButton(">", self)
        self.forward_btn.clicked.connect(self.webview.forward)
        self.forward_btn.setMaximumSize(20,20)
 
        self.menu_bar = QHBoxLayout()
        self.menu_bar.addWidget(self.back_btn)
        self.menu_bar.addWidget(self.forward_btn)
        self.main_layout = QVBoxLayout()
        self.main_layout.addLayout(self.menu_bar)
        self.main_layout.addWidget(self.webview)
 
        self.setLayout(self.main_layout)

You can now run the code, and you'll have a browser with a history that you can move back and forwards through. Here again, the QWebView did all of the hard work. It only required connecting the button clicks to the QWebView's forward and back methods.

Unlike the grid layout that you used previously, in box layout, Qt has more freedom to work out what size to draw particular widgets. Sometimes this is a good thing, but other times, you need to give it a bit of guidance. In a web browser, you want the buttons to take up as little space as possible, giving all the free screen area to the web page. To do this, we call the setMaximumSize() method on the widgets we add. In the case of the buttons, we make sure they get no bigger than 20×20.

The next feature of the web browser will be a text input where the users can type the address of a site they want to visit. There are a few different Qt widgets for text entry. The most common is QTextEdit. This allows the users to display and edit text. Actually, it does more than just straight text, and it can handle images, tables, headings, and other such things.

QPlainTextEdit is another common widget that works like QTextEdit except that it's designed for just plain text rather than rich text. Both of these are really powerful options that you'll probably use at some point in your programming career. However, they're a bit too much for an address bar since they're designed for multi-line text entry. For a single line of plain text entry (like a URL field), a QLineEdit is the best option.

You'll also need a Go button to tell the browser to load the page. To do all this, update the Browser class to the following (updates are in bold):

class Browser(QWidget):
  
   def _  _init_  _(self):
      super(Browser, self)._  _init_  _()
 
      self.webview = QWebView(self)
      self.webview.load("http://www.google.com")
      self.setGeometry(0, 0, 800, 600)
        
      self.back_btn = QPushButton("<", self)
      self.back_btn.clicked.connect(self.webview.back)
      self.back_btn.setMaximumSize(20,20)
 
      self.forward_btn = QPushButton(">", self)
      self.forward_btn.clicked.connect(self.webview.forward)
      self.forward_btn.setMaximumSize(20,20)

      self.url_entry = QLineEdit(self)
      self.url_entry.setMinimumSize(200,20)
      self.url_entry.setMaximumSize(300,20)
 
      self.go_btn = QPushButton("Go", self)
      self.go_btn.clicked.connect(self.go_btn_clicked)
      self.go_btn.setMaximumSize(20,20)
 
      self.menu_bar = QHBoxLayout()
      self.menu_bar.addWidget(self.back_btn)
      self.menu_bar.addWidget(self.forward_btn)
      self.menu_bar.addWidget(self.url_entry)
      self.menu_bar.addWidget(self.go_btn)
      self.menu_bar.addStretch()
      self.main_layout = QVBoxLayout()
      self.main_layout.addLayout(self.menu_bar)
      self.main_layout.addWidget(self.webview)
 
      self.setLayout(self.main_layout)
        
   def go_btn_clicked(self):
      self.webview.load(self.url_entry.text())

There are a few new bits here. With the URL entry bar, there's a call to the method setMinimumSize(). Like setMaximumSize, this gives Qt some extra information about how you want the window laid out. Another new piece to help Qt lay out the window properly is the addStretch()method call. This adds a pseudo-widget that just changes shape to fill up space. In this case, it takes up all the spare room on the right side of the menu bar so Qt pushes all the controls to the left.

You can run it now and try it out. The only thing to note is that it does need you to enter http:// at the start of the web address. (The technical reason for this is because a URL or Universal Resource Locater requires this as it specifies the protocol. For example, http://yoursite.com/document could point to something different than ftp://yoursite.com/document. Most modern browsers allow you to omit this and just assume you mean http. However, when a module asks for a URL, it usually needs the protocol prefix.)

In this case, we've added a new method to Browser called go_btn_clicked() because this gives us a little more power than just connecting methods to events. In this case, it allows you to add a parameter to the call to webview's load method with the parameter self.url_entry.text(), which just returns the text that the user typed.

At this point, you have what could realistically be called a web browser. There's nothing essential missing, although it's less powerful than mainstream browsers like Firefox, Chrome, or Midori. The next feature we decided to add is a bookmarks picker. We chose this partially because it's a useful feature, and partially because it gives us an excuse to show off another useful Qt widget, the QComboBox.

Combo box is an odd name for something you're almost certainly familiar with. They're boxes with a drop-down arrow that opens a set of choices that users can pick from. If that doesn't seem familiar now, it will be as soon as you see it.

Later on in the book we'll look at some ways you can store information between sessions, but to keep things simple, we won't let the user change or add to the bookmarks. After all, this is a chapter on user interfaces, and we want to stick to that topic.

Add the bold sections of the following to the Browser class (the non-bold sections will let you know where to add it):

self.go_btn = QPushButton("Go", self)
self.go_btn.clicked.connect(self.go_btn_clicked)
self.go_btn.setMaximumSize(20,20)
        
self.favourites = QComboBox(self)
self.favourites.addItems(["http://www.google.com",
                    "http://www.raspberrypi.org",
                    "http://docs.python.org/3/"])
self.favourites.activated.connect(self.favourite_selected)
self.favourites.setMinimumSize(200,20)
self.favourites.setMaximumSize(300,20)
 
self.menu_bar = QHBoxLayout()
self.menu_bar.addWidget(self.back_btn)
self.menu_bar.addWidget(self.forward_btn)
self.menu_bar.addWidget(self.url_entry)
self.menu_bar.addWidget(self.go_btn)
self.menu_bar.addStretch()
self.menu_bar.addWidget(self.favourites)
self.main_layout = QVBoxLayout()
self.main_layout.addLayout(self.menu_bar)
self.main_layout.addWidget(self.webview)
 
self.setLayout(self.main_layout)
   
def go_btn_clicked(self):
self.webview.load(self.url_entry.text())
   
def favourite_selected(self):
   self.webview.load(self.favourites.currentText())

This is all quite straightforward, and if you run the code, you'll see a QComboBox in action.

As with the URL entry, we just call self.webview.load, but this time with a parameter that grabs the currently selected text from the combo box.

There are only two controls left to add to the menu bar, so let's make them in one edit. The first is a search bar that lets users enter a search term and then press a button to run a Google search. The second is a zoom slider bar that lets the user zoom in and out of the page.

Update the Browser class with the bold text from the following:

self.favourites = QComboBox(self)
self.favourites.addItems(["http://www.google.com",
                          "http://www.raspberrypi.org",
                          "http://docs.python.org/3/"])
self.favourites.activated.connect(self.favourite_selected)
self.favourites.setMinimumSize(200,20)
self.favourites.setMaximumSize(300,20)
  
self.search_box = QLineEdit(self)
self.search_box.setMinimumSize(200,20)
self.search_box.setMaximumSize(300,20)

self.search_btn = QPushButton("Search", self)
self.search_btn.clicked.connect(self.search_btn_clicked)
self.search_btn.setMaximumSize(50,20)

self.zoom_slider = QSlider(Qt.Orientation(1),self)
self.zoom_slider.setRange(2, 50)
self.zoom_slider.setValue(10)
self.zoom_slider.valueChanged.connect(self.zoom_changed)


self.zoom_label = QLabel("Zoom:")

self.webview.loadStarted.connect(self.page_loading)

self.menu_bar = QHBoxLayout()
self.menu_bar.addWidget(self.back_btn)
self.menu_bar.addWidget(self.forward_btn)
self.menu_bar.addWidget(self.url_entry)
self.menu_bar.addWidget(self.go_btn)
self.menu_bar.addStretch()
self.menu_bar.addWidget(self.favourites)
self.menu_bar.addStretch()
self.menu_bar.addWidget(self.search_box)
self.menu_bar.addWidget(self.search_btn)
self.menu_bar.addWidget(self.zoom_label)
self.menu_bar.addWidget(self.zoom_slider)
self.main_layout = QVBoxLayout()
self.main_layout.addLayout(self.menu_bar)
self.main_layout.addWidget(self.webview)

self.setLayout(self.main_layout)
  
def go_btn_clicked(self):
self.webview.load(self.url_entry.text())
  
def favourite_selected(self):
self.webview.load(self.favourites.currentText())

def zoom_changed(self):
self.webview.setZoomFactor(self.zoom_slider.value()/10)

def search_btn_clicked(self):
self.webview.load("https://www.google.com/search?q="
                + self.search_box.text())

def page_loading(self):
self.url_entry.setText(self.webview.url().toString())

Whilst there are two controls, there are four widgets to make them happen. The search box has a QLineEntry and a QPushButton as well. Together, these work in a very similar way to the URL entry control that you added earlier, except that it adds https://www.google.com/search?q= to the start of whatever you enter. For example, if you search for Raspberries, it will go to the URL https://www.google.com/search?q=Raspberries and this tells Google to search for Raspberries. This has https:// at the start rather than http://. The s stands for secure, and if you use https:// then any data between your browser and the website is encrypted. However, not every website supports https. QWebView allows you to use either protocol as long as the server supports it.

The zoom slider is a QSlider, which is another type of control that you're probably familiar with. It takes a little more setting up, though, which is what the following lines do:

self.zoom_slider.setRange(2, 50)
self.zoom_slider.setValue(10)

The first sets the maximum and minimum values for the slider, and the second sets the initial value.

You connected the valueChanged action to the zoom_changed() method. Once again, this just links into one of QWebView's methods and lets it do all the hard work. The only thing zoom_changed() does is divide the value of the slider by 10 to make the zoom a bit more manageable.

If you were looking closely, you'll have noticed that this actually does a little more than adding two extra controls. It also has these lines:

  self.webview.loadStarted.connect(self.page_loading)
. . .
def page_loading(self):
  self.url_entry.setText(self.webview.url().toString())

Which will make sure the URL entry box is always updated with the address of the current page.

Adding Window Menus

The main browser layout is finished, but there's still a bit more to add before the application's done. Remember that at the start we said that we extended a QMainWindow so that we could add menus? Well now's the time to do that.

The Window already has a menu; all you need to do is add things to it. These menu items are similar to widgets, except they're made from QActions.

To add a file menu with an entry to close the window, change the BrowserWindow class to the following:

class BrowserWindow(QMainWindow):
  def _  _init_  _(self):
    super(BrowserWindow, self)._  _init_  _()
    self.widget = Browser()
    self.setCentralWidget(self.widget)
        
    self.exitAction = QAction(QIcon('exit.png'), '&Exit', self)
    self.exitAction.setShortcut('Ctrl+Q')
    self.exitAction.setStatusTip('Exit application')
    self.exitAction.triggered.connect(self.close)

    self.menu = self.menuBar()
    self.fileMenu = self.menu.addMenu('&File')
    self.fileMenu.addAction(self.exitAction)

There's one more menu entry to add, one to open a locally stored file. This is a bit different to everything in the web browser because it opens a new window. In the new window, the users will get to browse through their files and select the one they want to open. At this point, you might be thinking that it'll require quite a bit of work to create this new window, add a whole layout, and link up all the required widgets. However, this is another place where we can just let Qt do all the hard work for us. There are a range of Qt widgets known as dialogs. These are simple windows to perform common functions, and they can make your life a lot easier. To add an open file dialog to the web browser, update the following BrowserWindow class:

self.exitAction = QAction(QIcon('exit.png'), '&Exit', self)
self.exitAction.setShortcut('Ctrl+Q')
self.exitAction.setStatusTip('Exit application')
self.exitAction.triggered.connect(self.close)

self.openFile = QAction(QIcon('open.png'), 'Open', self)
self.openFile.setShortcut('Ctrl+O')
self.openFile.setStatusTip('Open new File')
self.openFile.triggered.connect(self.showDialog)

self.menu = self.menuBar()
self.fileMenu = self.menu.addMenu('&File')
self.fileMenu.addAction(self.openFile)
self.fileMenu.addAction(self.exitAction)
        
def showDialog(self):
   fname, _ = QFileDialog.getOpenFileName(self, 'Open file',
               '/home')
   self.widget.webview.load("file:///" + fname)

In this case, we don't have to create a new object, instead we can just call the getOpenFileName() method from QFileDialog. This will open a new window with the title "Open File in the directory /home" (see Figure 4-3). Once the users pick the file they want, it will return two things: the filename and the filter. However, since the web browser doesn't need to know the filter, assigning it to _ just drops it.

Figure 4-3: Opening a local file using QFileDialog.

9781118717059-fg0403.tif

The QWebView can open local files using the protocol file:/// (note the three slashes), so you just need to prefix this on the filename before you can use it.

That brings us to the end of the web browser. If you haven't been following along, the complete code is on the website as chapter4-web-browser-complete.py. As with every application, there's still plenty we could add, but we've shown enough to introduce the pyside module and the Qt toolkit. It's a huge toolkit, so out of necessity, we've only been able to show you the basics. Everything is documented at http://srinikom.github.io/pyside-docs/.

Test Your Knowledge

Exercise 2

As you've seen, dialogs are great ways to add functionality to your programs quickly. In this exercise, go back to the turtle program and add a button that launches a QColorDialog, which sets the colour of the turtle.

Here are a few hints to help you out. QColorDialog.getColor() will return a value with the type QColor. To get the RGB values out of a variable that holds a QColor, use variable_name.getRgb()[:3]. You need the [:3] at the end because it returns a tuple with four values (the final one being the transparency, which you don't need in this case).

Summary

After consuming this chapter, you should know the following:

  • PySide is a Python library that helps you write graphical user interfaces using the Qt toolkit.
  • Qt contains a wide range of widgets that you can add to your projects to quickly create powerful interfaces.
  • You can connect actions such as button presses, combo box changes, or slider movements to method calls.
  • There are several ways you can lay out your Qt windows, including grid and box.
  • If you build your interface on a class that extends QMainWindow, you can add menus.
  • Qt includes a range of dialogs that are ready-made windows that you can add to your project.

Solutions to Exercises

Exercise 1

import turtle
import sys
from PySide.QtCore import *
from PySide.QtGui import *
 
class TurtleControl(QWidget):
    def _  _init_  _(self, turtle):
        super(TurtleControl, self)._  _init_  _()
        self.turtle = turtle
 
        self.left_btn = QPushButton("Left", self)
        self.right_btn = QPushButton("Right", self)
        self.move_btn = QPushButton("Move", self)
        self.distance_spin = QSpinBox()
        self.red_spin = QSpinBox()
        self.green_spin = QSpinBox()
        self.blue_spin = QSpinBox()
        self.red_label = QLabel("Red", self)
        self.green_label = QLabel("Green", self)
        self.blue_label = QLabel("Blue", self)
        self.colour_btn = QPushButton("Colour", self)
        
        self.controlsLayout = QGridLayout()
        self.controlsLayout.addWidget(self.left_btn, 0, 0)
        self.controlsLayout.addWidget(self.right_btn, 0, 1)
        self.controlsLayout.addWidget(self.distance_spin, 1 , 0)
        self.controlsLayout.addWidget(self.move_btn, 1 , 1)
        self.controlsLayout.addWidget(self.red_spin, 2,1)
        self.controlsLayout.addWidget(self.green_spin, 3,1)
        self.controlsLayout.addWidget(self.blue_spin, 4,1)
        self.controlsLayout.addWidget(self.red_label, 2,0)
        self.controlsLayout.addWidget(self.green_label, 3,0)
        self.controlsLayout.addWidget(self.blue_label, 4,0)
        self.controlsLayout.addWidget(self.colour_btn, 5,0)
        self.setLayout(self.controlsLayout)
        
        self.distance_spin.setRange(0, 100)
        self.distance_spin.setSingleStep(5)
        self.distance_spin.setValue(20)
        
        for spinner in [self.red_spin, self.green_spin,
                        self.blue_spin]:
            spinner.setRange(0, 255)
            spinner.setSingleStep(5)
            spinner.setValue(150)
        
        self.move_btn.clicked.connect(self.move_turtle)
        self.right_btn.clicked.connect(self.turn_turtle_right)
        self.left_btn.clicked.connect(self.turn_turtle_left)
        self.colour_btn.clicked.connect(self.colour_turtle)
        
    def turn_turtle_left(self):
        self.turtle.left(45)
        
    def turn_turtle_right(self):
        self.turtle.right(45)
    
    def move_turtle(self):
        self.turtle.forward(self.distance_spin.value())
        
    def colour_turtle(self):
        self.turtle.color(self.red_spin.value(),
                    self.green_spin.value(),
                    self.blue_spin.value())
 
#set up turtle
window = turtle.Screen()
babbage = turtle.Turtle()
window.colormode(255)
 
# Create a Qt application
app = QApplication(sys.argv)
control_window = TurtleControl(babbage)
control_window.show()
 
# Enter Qt application main loop
app.exec_()
sys.exit()

Exercise 2

The following function will need to be connected to the clicked action of a button:

def colour_turtle(self):
   self.colour = QColorDialog.getColor()
   self.turtle.color(self.colour.getRgb()[:3])

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset