Page 1 of 1

A Basic PyQt Tutorial (Notepad)

#1 SegFaulty  Icon User is offline

  • New D.I.C Head
  • member icon

Reputation: 14
  • View blog
  • Posts: 35
  • Joined: 11-October 10

Posted 28 December 2011 - 03:22 PM

Today we're going to delve into the world of GUI programming with PyQt. PyQt is a wrapper for the C++ GUI framework Qt. Qt is a crossplatform framework that uses the native widgets to draw its user interfaces. One of the nice things about Qt and PyQt is that if you already know Qt you can pick up PyQt right away and verse visa!

Ok, so lets get our imports straight. Enter the following code into your file:
#! /usr/bin/python 

import sys 
import os
from PyQt4 import QtGui 



Windows users need not pay attention to the first line (as I would assume most people in this tutorial would know). It is for *nix systems to find python. Now, we need to import sys, but this won't become important until later on in the program. Finally, you'll notice our import statement for Qt. You could also use the following line:
from PyQt4 import *



However, I find that since we don't need all of PyQt4, then we don't need to import it. Now, let's get ourselves a basic window up and running! First of all, we are going to need a class to hold our application. One of the easiest ways to set up our main window is to have our application inherit the QMainWindow from Qt. So add the following to your code:
class Notepad(QtGui.QMainWindow):
    def __init__(self):
        super(Notepad, self).__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300,300,300,300)
        self.setWindowTitle('Notepad')
        self.show() 

def main():
    app = QtGui.QApplication(sys.argv)
    notepad = Notepad()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()



Ok, we have some explaining to do now. First off, you'll notice that the import used the prefix Qt- while our mainwindow and application classes use the prefix Q-. There is a very important distinction here and all the other widgets also use the Q- prefix. Next you'll notice that we initialize our super class (in this case the QMainWindow) and give it our new class then self. Finally we use a new method, initUI(), to set up our actual GUI. Within that method we use setGeometry() to set the size of our window. Then we give our window a title to display to the user, and tell it to show itself. Finally, we get to our main method. The line
app = QtGui.QApplication(sys.argv)
sets up our application to run and grabs any command line arguments to use with the application. We then initialize our class which initializes our GUI, and tell the program to wait for us to close our application before actually ending the program.

Ok, so now that we have a basic window up and running, let's get our menubar set up with a close action. Add the following code to the beginning of our initUI() function so it looks like this:
def initUI(self):
    closeAction = QtGui.QAction('Close', self)
    closeAction.setShortcut('Ctrl+Q')
    closeAction.setStatusTip('Close Notepad')
    closeAction.triggered.connect(self.close)

    menubar = self.menuBar()
    fileMenu = menubar.addMenu('&File')
    fileMenu.addAction(closeAction)

    self.setGeometry(300,300,300,300) 
    self.setWindowTitle('Notepad') 
    self.show()



Okay, time for some dissection. So what exactly is a QAction? It is a widget for Qt that acts as an option in a menu. So, when we create our closeAction, we give it the text that we want it to show and then the parent widget for it. Then, we can give it a few options to spruce it up. First, we give it a shortcut so people don't necessarily have to use the menu to access it by using the setShortcut() method. Then, we give it a status tip that pops up when a user hovers over our new action telling them what it does using setStatusTip().
Finally, we get to connecting it so when the user clicks something actually happens. Qt uses a model called Signals and Slots. Basically what happens is when something happens to an object (such as our QAction) it emits a Signal (in this case “triggered”). That Signal can be sent to the slot of another object, and that object can then run something (in this case, close). Note that when we give the connect method a method to use when connected, we do NOT use parentheses. The newest API for PyQt has made the way we connect Signals and Slots different. The proper way for us to connect such things is
QObject.Signal.connect(doSomething())

The next section of code now sets up our actual menubar to add our action. Our menubar is added to the window by calling on self.menuBar(). Next, we create a menu to add to the menubar. You may notice the funky '&File' in there. What the ampersand (&) does in this case is set our shortcut to access it to Control+the letter directly after it. This is just another way to add a shortcut if the letter you'd like to use is in the word you are using. Finally, we add our action to the file menu with add action.
If you run your program again, you will now notice a menubar at the top of the screen. Go ahead and click on “Close”; it works!
“But what about the text? I want to write in my notepad!” you might be saying to yourself. Well, let's add that to our structure! In order to write text, we must use the QTextEdit widget. In order to add this to our code, add the following lines to your initUI() method after our menubar is completed:
self.text = QtGui.QTextEdit(self)
self.setCentralWidget(self.text)



The first line is rather self explanatory: we make a new QTextEdit and declare its parent to be the main window. The next line, however, may not be. Because we are only using one widget here, we have no need to use a layout manager. Therefore, we just set the main windows main focus (and central widget) to the QTextEdit. Now our QTextEdit will by default take up all the room in the program that is not being taken up by the menubar, making our life much easier.
“But what kind of notepad can't open or save a file?” you might now be asking yourself. Let's use the techniques previously shown to make some options on our menu to do such things! Add the following code to the part of initUI() that deals with the menubar:
        newAction = QtGui.QAction('New', self) 
        newAction.setShortcut('Ctrl+N') 
        newAction.setStatusTip('Create new file') 
        newAction.triggered.connect(self.newFile) 
        
        saveAction = QtGui.QAction('Save', self) 
        saveAction.setShortcut('Ctrl+S') 
        saveAction.setStatusTip('Save current file') 
        saveAction.triggered.connect(self.saveFile) 
        
        openAction = QtGui.QAction('Open', self) 
        openAction.setShortcut('Ctrl+O') 
        openAction.setStatusTip('Open a file') 
        openAction.triggered.connect(self.openFile)
        …
        fileMenu.addAction(newAction) 
        fileMenu.addAction(saveAction) 
        fileMenu.addAction(openAction)


Now, we need to make our methods newFile, saveFile, and openFile so that they will run when we trigger our options. Add the methods to your class:

def newFile(self):
    self.text.clear()

def saveFile(self):
        filename = QtGui.QFileDialog.getSaveFileName(self, 'Save File', os.getenv('HOME')) 
        f = open(filename, 'w') 
        filedata = self.text.toPlainText() 
        f.write(filedata) 
        f.close()

def openFile(self):
        filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME')) 
        f = open(filename, 'r') 
        filedata = f.read() 
        self.text.setText(filedata) 
        f.close()



Hold up a minute, though; there's new things in this code that aren't straight up python! First off, for our new method, we simply clear away any text that was in the QTextEdit at the time. However, when we go to open or save a file we need to use a dialog to get the file address that we want. Thankfully, Qt comes with a file dialog that we can use to get those filenames! Now, let's look at that first line in saveFile(). First, we say we want a variable (in this case a string) to equal our filename. Then we ask Qt to open a QFileDialog and then return to us the name of the file that is returned. Then we must pass a couple arguments to it. First, we give it the parent widget (in this case, our main window), then we give it a title for the dialog, then we give it a place to open up to (in this case, the user's home directory). The same happens in our openFile() method, however, we give the window title a different name.
Let's take a look at two more lines:
filedata = self.text.toPlainText()

This piece of code gives us all the text that is inside the QTextEdit in plain text format. This makes is easy for us to place this in a file.
self.text.setText(filedata)

This line of code takes the data we read from the file and sets the text within our QTextEdit to it. Our final code should look like this:
#! /usr/bin/python

import sys
import os
from PyQt4 import QtGui

class Notepad(QtGui.QMainWindow):

    def __init__(self):
        super(Notepad, self).__init__()
        self.initUI()
        
    def initUI(self):
        newAction = QtGui.QAction('New', self)
        newAction.setShortcut('Ctrl+N')
        newAction.setStatusTip('Create new file')
        newAction.triggered.connect(self.newFile)
        
        saveAction = QtGui.QAction('Save', self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.setStatusTip('Save current file')
        saveAction.triggered.connect(self.saveFile)
        
        openAction = QtGui.QAction('Open', self)
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open a file')
        openAction.triggered.connect(self.openFile)
        
        closeAction = QtGui.QAction('Close', self)
        closeAction.setShortcut('Ctrl+Q')
        closeAction.setStatusTip('Close Notepad')
        closeAction.triggered.connect(self.close)
        
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(newAction)
        fileMenu.addAction(saveAction)
        fileMenu.addAction(openAction)
        fileMenu.addAction(closeAction)
        
        self.text = QtGui.QTextEdit(self)
        
        self.setCentralWidget(self.text)
        self.setGeometry(300,300,300,300)
        self.setWindowTitle('Notepad')
        self.show()
        
    def newFile(self):
        self.text.clear()
        
    def saveFile(self):
        filename = QtGui.QFileDialog.getSaveFileName(self, 'Save File', os.getenv('HOME'))
        f = open(filename, 'w')
        filedata = self.text.toPlainText()
        f.write(filedata)
        f.close()
        
        
    def openFile(self):
        filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
        f = open(filename, 'r')
        filedata = f.read()
        self.text.setText(filedata)
        f.close()
        
def main():
    app = QtGui.QApplication(sys.argv)
    notepad = Notepad()
    sys.exit(app.exec_())
    
if __name__ == '__main__':
    main()


As you can see, with a little bit of effort, we can quite easily come out with a simple text editor in PyQt! You've learned the very basics for setting up our main window, getting a menubar up and running, how to connect Signals and Slots, and how to set a central widget! I hope this tutorial has helped you in learning PyQt! Please leave any constructive criticisms or comments below.

Is This A Good Question/Topic? 3
  • +

Replies To: A Basic PyQt Tutorial (Notepad)

#2 hogjonny  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 10-July 13

Posted 10 July 2013 - 12:29 PM

When I run this example, the pyqt app/window are created properly.
but if I try to 'save' or 'open' I trigger an exception on the following code

 filename = QtGui.QFileDialog.getSaveFileName(self, 'Save File', os.getenv('HOME')) 


 filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME')) 


... I am thinking it 's because I don't have a 'HOME' env variable in windows (although, I thought this was the user directory? Or is that unix specific env var?)

but if I change the
os.getenv('HOME')
to 'c:\\myTemp\\' (a directory I know exists) then the dialog opens fine.

Pretty great example of a basic QT app in python, thanks for posting it!
Was This Post Helpful? 0
  • +
  • -

#3 SegFaulty  Icon User is offline

  • New D.I.C Head
  • member icon

Reputation: 14
  • View blog
  • Posts: 35
  • Joined: 11-October 10

Posted 31 July 2013 - 07:47 AM

Huh, that's interesting. When I wrote the tutorial I had read that HOME should work just fine on Windows as well, but I didn't have a Windows environment to test it in, to be perfectly honest. I was running in Linux.... I'll have to do some research and update the tutorial if possible, thanks for pointing that out!
Was This Post Helpful? 0
  • +
  • -

#4 BlueMelon  Icon User is offline

  • D.I.C Head

Reputation: 39
  • View blog
  • Posts: 187
  • Joined: 27-April 10

Posted 31 July 2013 - 08:36 AM

How about the following:
os.path.expanduser("~")

os.path.expanduser

This post has been edited by BlueMelon: 31 July 2013 - 08:40 AM

Was This Post Helpful? 1
  • +
  • -

#5 SegFaulty  Icon User is offline

  • New D.I.C Head
  • member icon

Reputation: 14
  • View blog
  • Posts: 35
  • Joined: 11-October 10

Posted 31 July 2013 - 11:03 AM

You know, that seems to be a better solution.
Was This Post Helpful? 0
  • +
  • -

#6 Akitta  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 02-September 13

Posted 02 September 2013 - 10:53 PM

Thanks for the tutorial ( I've completed my first program )..!!
Was This Post Helpful? 0
  • +
  • -

#7 cristiano_araujo  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 3
  • Joined: 07-January 14

Posted 07 January 2014 - 06:52 PM

works well in my machine. i am using ubuntu. all the buttons and shortcuts is running perfect! thanks for this tuto!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1