Create GUI in Python using Qt 2

Introduction

This notebook continues QtGui1

In Example 2 a MainWindow with a menubar is created and in Exercise 2 a this example is extended to a GUI for a WEFileIO class

Example 2

In this example a MainWindow with menubar is created. Use this example if menubar, toolbar and/or statusbar are required

Create ui

  • Open Qt Designer and create new "MainWindow"
  • Add "PushButton" to "MainWindow"
  • Set the text to "Open" (double click on button or enter in property editor)

Add action

  • Create new action in Action Editor
    • Text: "Open"
    • ObjectName: "actionOpen" (default)
    • Shortcut: Ctrl+O
    • ToolTip: "Open a file"

Connect action to PushButton

  • Add Signal/Slot in Signal/Slot Editor
    • Sender: pushButton
    • Signal: clicked()
    • Receiver: actionOpen
    • Slot: trigger()

Connect action to menu

  • Create a File menu by:
    • Doubleclick on “Type Here” in the menubar of the window
    • Type "File" + Enter
  • Drag actionOpen from Action Editor to the menu

Integrate into Python

  • Save as “MyUI/MyMainWindowUI.ui” in the same folder as this notebook
  • Create an empty "MyUI/MyMainWindowUI.py" file in the same folder (This is required in order to import the widget. Its content will be autogenerated if the file is completely empty or older than "MyUI/MyMainWindowUI.ui"

When loading via QtMainWindowLoader, actions are automatically connected to methods of the same name.

All PyQt elements are found in the ui-object, e.g. self.ui.pushButton and documentation of the PyQt elements is found at http://pyqt.sourceforge.net/Docs/PyQt4/classes.html

In order to integrate MyMainWindowUI in Python, subclass QtMainWindowLoader from QtGuiLoader and instantiate with MyMainWindowUI as ui_module argument.

In this example the action actionOpen can be invoked by:

  • The menu
  • Its shortcut (Ctrl+O)
  • The pushButton

Check out the body of actionOpen. What will happen?

Try it


In [1]:
import MyUI.MyMainWindowUI
from QtGuiLoader import QtMainWindowLoader
from PyQt4 import QtGui

class MyMainWindowWithMenu(QtMainWindowLoader):
    def __init__(self):
        try: self.ui = MyUI.MyMainWindowUI.Ui_Form() # Enables autocompletion (maybe...)
        except: pass
        QtMainWindowLoader.__init__(self, MyUI.MyMainWindowUI)
    
    def actionOpen(self):
        filename = str(QtGui.QFileDialog.getOpenFileName(self, "Open...", ".", "*.*"))
        if filename == "": return #cancel
        print filename

MyMainWindowWithMenu().start()

Exercise 2

In this exercise we will make a GUI for our WEFileIO project by extending the mainwindow from example 2 with a few widgets and the MyPlotControlUI from exercise 1

  • Open MyMainWindowUI in QtDesigner
  • Save it as "MyPlotMainWindowUI"
  • Add a Horisontal Layout to the window
  • Add a Horisontal Spacer to the horisontal layout
  • Add a Grid Layout to the horisontal layout, on the left side of the spacer
  • Set layoutName of grid layout to "gridLayoutPlot"
  • Add a Vertical Layout to the horisontal layout, on the right side of the spacer
  • Delete the spacer
  • Drag the "Open"-PushButton and drop it at the vertical layout
  • Add a Grid Layout below the "Open"-PushButton
  • Set layoutName of grid layout to "gridLayoutControl"
  • Right-click the form: Layout - Layout in a grid
  • Select horisontal layout and set layoutStretch to "1,0"

  • Create an empty "MyPlotMainWindowUI.py" file in the same folder

In [7]:
from QtGuiLoader import QtWidgetLoader
import MyUI.MyPlotControlUI
class MyPlotControlWidget(QtWidgetLoader):
    def __init__(self, we_file_io):
        try:self.ui = MyUI.MyPlotControlUI.Ui_Form() # Enables autocompletion (if you are lucky...)
        except: pass
        QtWidgetLoader.__init__(self, ui_module=MyUI.MyPlotControlUI)
        self.we_file_io = we_file_io
                
        #Connect widget signals to actionUpdatePlot
        self.ui.xLineEdit.editingFinished.connect(self.actionUpdatePlot)
        self.ui.yLineEdit.editingFinished.connect(self.actionUpdatePlot)
        self.ui.colorComboBox.currentIndexChanged.connect(self.actionUpdatePlot)
        self.ui.horizontalSlider.valueChanged.connect(self.actionUpdatePlot)
        self.ui.spinBox.valueChanged.connect(self.actionUpdatePlot)
        self.ui.doubleSpinBox.valueChanged.connect(self.actionUpdatePlot)

        
    x_str = property(lambda self : str(self.ui.xLineEdit.text()))
    y_str = property(lambda self : str(self.ui.yLineEdit.text()))
    color = property(lambda self : str(self.ui.colorComboBox.currentText()))
    width = property(lambda self : self.ui.horizontalSlider.value())
    ylim = property(lambda self : (self.ui.spinBox.value(), self.ui.doubleSpinBox.value()))
    
    def actionUpdatePlot(self):
        self.we_file_io.plot()

In [8]:
import sys
sys.path.append("../py4we") # append py4we package to path to access WEFileIO

from we_file_io import WEFileIO
import numpy as np
class MyPlotFileIO(WEFileIO):
    title = "No title"
    def __init__(self, mpl_widget):
        WEFileIO.__init__(self, file_type_name = "Exercise2file", file_extension = ".title")
        self.figure = mpl_widget.figure
        self.ui_control = MyPlotControlWidget(self)
        self.plot()

    def _read(self):
        with open(self.filename, 'r') as f:
            self.title = f.read()
        self.plot()
    
    def _plot(self, fig):
        axes = fig.axes[0]
        x = eval(self.ui_control.x_str)
        y = eval(self.ui_control.y_str)
        axes.plot(x,y, self.ui_control.color, linewidth=self.ui_control.width)
        axes.set_ylim(self.ui_control.ylim)
        axes.set_title(self.title)
        fig.canvas.draw()

In [9]:
import MyUI.MyPlotMainWindowUI
from QtGuiLoader import QtMainWindowLoader
from matplotlibwidget import MatplotlibWidget
from PyQt4 import QtGui

class MyPlotMainWindow(QtMainWindowLoader):
    def __init__(self):
        module = MyUI.MyPlotMainWindowUI
        try:self.ui = module.Ui_Form() # Enables autocompletion (if you are lucky...)
        except: pass
        QtMainWindowLoader.__init__(self, module)
        mpl = MatplotlibWidget()
        self.ui.gridLayoutPlot.addWidget(mpl)
        self.fileio = MyPlotFileIO(mpl)
        self.ui.gridLayoutControl.addWidget(self.fileio.ui_control)
        
    
    def actionOpen(self):
        filename = str(QtGui.QFileDialog.getOpenFileName(self, "Open...", ".", "*%s" % self.fileio.file_extension))
        if filename == "": return #cancel
        self.fileio.read(filename)


MyPlotMainWindow().start()


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-9-ccda53ba8e5c> in <module>()
     22 
     23 
---> 24 MyPlotMainWindow().start()

<ipython-input-9-ccda53ba8e5c> in __init__(self)
     12         mpl = MatplotlibWidget()
     13         self.ui.gridLayoutPlot.addWidget(mpl)
---> 14         self.fileio = MyPlotFileIO(mpl)
     15         self.ui.gridLayoutControl.addWidget(self.fileio.ui_control)
     16 

<ipython-input-8-237b7fd186f8> in __init__(self, mpl_widget)
     10         self.figure = mpl_widget.figure
     11         self.ui_control = MyPlotControlWidget(self)
---> 12         self.plot()
     13 
     14     def _read(self):

C:\Users\cpav\Documents\GitHub\Python4WindEnergy-master\py4we\we_file_io.pyc in plot(self, *args, **kwargs)
     80         if self.figure is None:
     81             self.figure = plt.figure()
---> 82         self._plot(self.figure, *args, **kwargs)
     83         plt.show()
     84 

<ipython-input-8-237b7fd186f8> in _plot(self, fig)
     21         x = eval(self.ui_control.x_str)
     22         y = eval(self.ui_control.y_str)
---> 23         axes.plot(x,y, self.ui_control.color, linewidth=self.ui_control.width)
     24         axes.set_ylim(self.ui_control.ylim)
     25         axes.set_title(self.title)

C:\Python27\lib\site-packages\matplotlib\axes.pyc in plot(self, *args, **kwargs)
   4135         lines = []
   4136 
-> 4137         for line in self._get_lines(*args, **kwargs):
   4138             self.add_line(line)
   4139             lines.append(line)

C:\Python27\lib\site-packages\matplotlib\axes.pyc in _grab_next_args(self, *args, **kwargs)
    315                 return
    316             if len(remaining) <= 3:
--> 317                 for seg in self._plot_args(remaining, kwargs):
    318                     yield seg
    319                 return

C:\Python27\lib\site-packages\matplotlib\axes.pyc in _plot_args(self, tup, kwargs)
    274         ret = []
    275         if len(tup) > 1 and is_string_like(tup[-1]):
--> 276             linestyle, marker, color = _process_plot_format(tup[-1])
    277             tup = tup[:-1]
    278         elif len(tup) == 3:

C:\Python27\lib\site-packages\matplotlib\axes.pyc in _process_plot_format(fmt)
    127         else:
    128             raise ValueError(
--> 129                 'Unrecognized character %c in format string' % c)
    130 
    131     if linestyle is None and marker is None:

ValueError: Unrecognized character ' in format string
compile MyUI\MyPlotMainWindowUI.ui > MyUI\MyPlotMainWindowUI.py

In [ ]: