Example #1
0
    def __init__(self):
        super().__init__()

        self.datasets = DataSets()
        self._max_recent = 6  # maximum number of recent files
        self.history = []  # command history

        settings = self._read_settings()
        self.recent = settings["recent"] if settings["recent"] else []

        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle("MNELAB")

        menubar = self.menuBar()

        file_menu = menubar.addMenu("&File")
        file_menu.addAction("&Open...", self.open_file, QKeySequence.Open)
        self.recent_menu = file_menu.addMenu("Open Recent")
        self.recent_menu.aboutToShow.connect(self._update_recent_menu)
        self.recent_menu.triggered.connect(self._load_recent)
        if not self.recent:
            self.recent_menu.setEnabled(False)
        self.close_file_action = file_menu.addAction("&Close", self.close_file,
                                                     QKeySequence.Close)
        file_menu.addSeparator()
        file_menu.addAction("&Quit", self.close, QKeySequence.Quit)

        plot_menu = menubar.addMenu("&Plot")
        self.plot_raw_action = plot_menu.addAction("&Raw data", self.plot_raw)

        tools_menu = menubar.addMenu("&Tools")
        self.filter_action = tools_menu.addAction("&Filter data...",
                                                  self.filter_data)
        self.run_ica_action = tools_menu.addAction("&Run ICA...")
        self.import_ica_action = tools_menu.addAction("&Load ICA...",
                                                      self.load_ica)

        view_menu = menubar.addMenu("&View")
        view_menu.addAction("Show/hide statusbar", self._toggle_statusbar)

        help_menu = menubar.addMenu("&Help")
        help_menu.addAction("&About", self.show_about)
        help_menu.addAction("About &Qt", self.show_about_qt)

        self.names = QStringListModel()
        splitter = QSplitter()
        self.sidebar = QListView()
        self.sidebar.setFocusPolicy(0)
        self.sidebar.setFrameStyle(0)
        self.sidebar.setModel(self.names)
        self.sidebar.clicked.connect(self._update_data)
        splitter.addWidget(self.sidebar)
        self.infowidget = InfoWidget()
        splitter.addWidget(self.infowidget)
        width = splitter.size().width()
        splitter.setSizes((width * 0.25, width * 0.75))
        self.setCentralWidget(splitter)

        self.status_label = QLabel()
        self.statusBar().addPermanentWidget(self.status_label)
        if settings["statusbar"]:
            self.statusBar().show()
        else:
            self.statusBar().hide()

        self._toggle_actions(False)
        self.show()
Example #2
0
    def init_ui(self):
        '''
        Helper function to initialize UI widgets
        '''
        # set up the frame object
        cw = QtGui.QWidget()
        self.setCentralWidget(cw)
        self.frame_widget = FrameWidget(self.frame.radius)
        self.available_shapes_widget = AvailableShapesWidget()
        self.info_widget = InfoWidget()
        
        self.frame.set_frame_widget(self.frame_widget)
        self.frame.set_available_shapes_widget(self.available_shapes_widget)
        self.frame.set_info_widget(self.info_widget)  

        # Exit action
        exit_action = QtGui.QAction(QtGui.QIcon.fromTheme("exit"),
                                    '&Exit', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Exit application')
        exit_action.triggered.connect(self.close)
        
        # Create a menubar
        #menubar = self.menuBar()
        #file_menu = menubar.addMenu('&File')
        #file_menu.addAction(exit_action)
        
        # Create a toolbar
        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(exit_action)
        
        # Create a statusbar
        self.statusBar()
        self.statuslabel = QtGui.QLabel("Algorithm is <b>stopped</b>")
        self.statusBar().addPermanentWidget(self.statuslabel)
        self.frame.set_statuslabel(self.statuslabel)
        
        # Button to move shapes back to the right
        reset_button = QtGui.QPushButton('Reset', self)
        reset_button.clicked.connect(self.frame.reset_shapes)
        reset_button.resize(reset_button.sizeHint())
        reset_button.setStatusTip("Move all fitted shapes to the available shapes")
        
        # Button to run algorithm
        run_button = QtGui.QPushButton("Run")
        run_button.clicked.connect(self.run_frame_widget_algorithm)
        run_button.setStatusTip("Run the currently selected algorithm")
        
        # Button to stop algorithm
        stop_button = QtGui.QPushButton("Stop")
        stop_button.clicked.connect(self.frame.request_stop)
        stop_button.setStatusTip("Stop the currently running algorithm")
        
        # Button to generate shapes
        generate_button = QtGui.QPushButton('Generate\nshapes', self)
        generate_button.clicked.connect(self.generate_shapes)
        generate_button.setStatusTip('Generate new shapes')
        
        # Button to shuffle shapes
        shuffle_button = QtGui.QPushButton("Shuffle")
        shuffle_button.clicked.connect(self.frame.shuffle_available_shapes)
        shuffle_button.setStatusTip('Shuffle available shapes')
        
        # Button to clear shapes
        clear_button = QtGui.QPushButton("Clear shapes")
        clear_button.clicked.connect(self.frame.clear_available_shapes)
        clear_button.setStatusTip("Clear all shapes if algorithm is not running")
        
        # Button to sort shapes
        sort_button = QtGui.QPushButton("Sort by size")
        sort_button.clicked.connect(self.sort_available_shapes)
        sort_button.setStatusTip("Sort the available shapes by size")
        
        # Combobox to choose algorithm
        algolabel = QtGui.QLabel()
        algolabel.setText("Algorithm:")

        self.algocombo = QtGui.QComboBox()
        self.algocombo.addItems(self.frame.get_algorithms())
        self.algocombo.setStatusTip("Choose the algorithm for packing")
        
        # Checkboxes, spinboxes and labels for shape generation
        self.rectcheckbox = QtGui.QCheckBox()
        self.rectcheckbox.setText('Generate')
        self.rectcheckbox.setChecked(True)

        self.circcheckbox = QtGui.QCheckBox()
        self.circcheckbox.setText('Generate')
        self.circcheckbox.setChecked(True)

        self.rectspinbox = QtGui.QSpinBox()
        self.rectspinbox.setMinimum(0)
        self.rectspinbox.setMaximum(50)
        self.rectspinbox.setValue(5)

        self.circspinbox = QtGui.QSpinBox()
        self.circspinbox.setMinimum(0)
        self.circspinbox.setMaximum(50)
        self.circspinbox.setValue(5)
        self.circspinbox.accelerated = True

        self.circmaxspinbox = QtGui.QSpinBox()
        self.circmaxspinbox.setMinimum(10)
        self.circmaxspinbox.setMaximum(self.frame.radius * 2)
        self.circmaxspinbox.setValue(self.frame.radius)

        self.circminspinbox = QtGui.QSpinBox()
        self.circminspinbox.setMinimum(10)
        self.circminspinbox.setMaximum(self.frame.radius * 2)
        self.circminspinbox.setValue(10)

        self.circmaxspinbox.valueChanged.connect(self.cminChange)
        self.circminspinbox.valueChanged.connect(self.cmaxChange)
        
        self.rectmaxspinbox = QtGui.QSpinBox()
        self.rectmaxspinbox.setMinimum(10)
        self.rectmaxspinbox.setMaximum(self.frame.radius * 2)
        self.rectmaxspinbox.setValue(self.frame.radius)

        self.rectminspinbox = QtGui.QSpinBox()
        self.rectminspinbox.setMinimum(10)
        self.rectminspinbox.setMaximum(self.frame.radius * 2)
        self.rectminspinbox.setValue(10)

        self.rectmaxspinbox.valueChanged.connect(self.rminChange)
        self.rectminspinbox.valueChanged.connect(self.rmaxChange)

        rectlabel = QtGui.QLabel()
        rectlabel.setText("<b>rectangles</b> with sides from")
        circlabel = QtGui.QLabel()
        circlabel.setText("<b>circles</b> with diameters from")
        tolabel1 = QtGui.QLabel()
        tolabel1.setText("to")
        tolabel2 = QtGui.QLabel()
        tolabel2.setText("to")
        emptylabel = QtGui.QLabel()
        emptylabel.setText("")

        # Grid layout for shape generation
        gengrid = QtGui.QGridLayout()
        gengrid.addWidget(self.rectcheckbox, 0, 0)
        gengrid.addWidget(self.circcheckbox, 1, 0)
        gengrid.addWidget(self.rectspinbox, 0, 1)
        gengrid.addWidget(self.circspinbox, 1, 1)
        gengrid.addWidget(rectlabel, 0, 2)
        gengrid.addWidget(circlabel, 1, 2)
        gengrid.addWidget(self.rectminspinbox, 0, 3)
        gengrid.addWidget(self.circminspinbox, 1, 3)
        gengrid.addWidget(tolabel1, 0, 4)
        gengrid.addWidget(tolabel2, 1, 4)
        gengrid.addWidget(self.rectmaxspinbox, 0, 5)
        gengrid.addWidget(self.circmaxspinbox, 1, 5)
        gengrid.addWidget(generate_button, 0, 6, 2, 1)
        gengrid.addWidget(emptylabel, 0, 7)
        gengrid.setColumnStretch(7, 1)
        
        # HBox for run, stop and reset buttons
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(run_button)
        hbox.addWidget(stop_button)
        hbox.addWidget(reset_button)
        hbox.addStretch(1)
        
        # HBox for algorithm combobox and label
        hboxalgo = QtGui.QHBoxLayout()
        hboxalgo.addWidget(algolabel)
        hboxalgo.addWidget(self.algocombo)
        hboxalgo.addStretch(1)
        
        # VBox for left pane
        vboxmain = QtGui.QVBoxLayout()
        vboxmain.addWidget(self.frame_widget)
        vboxmain.addWidget(self.info_widget)
        vboxmain.addLayout(hboxalgo)
        vboxmain.addLayout(hbox)
        
        # Checkbox for reverse sorting and HBox for buttons
        self.reversecheck = QtGui.QCheckBox()
        self.reversecheck.setText('Reverse')
        self.reversecheck.setChecked(False)
        self.reversecheck.setStatusTip("Sort in reverse")
        hboxshape = QtGui.QHBoxLayout()
        hboxshape.addWidget(self.reversecheck)
        hboxshape.addWidget(sort_button)
        hboxshape.addWidget(shuffle_button)
        hboxshape.addWidget(clear_button)
        hboxshape.addStretch(1)
        
        # ScrollArea for available_shapes_widget and VBox for right pane
        aswscroll = QtGui.QScrollArea()
        aswscroll.setWidget(self.available_shapes_widget)
        aswscroll.setMinimumWidth(430)
        aswscroll.setWidgetResizable(True)
        vboxright = QtGui.QVBoxLayout()
        vboxright.addWidget(aswscroll)
        vboxright.addLayout(hboxshape)
        vboxright.addLayout(gengrid)
        
        # HBox to split UI in two panes
        hboxmain = QtGui.QHBoxLayout()
        hboxmain.addLayout(vboxmain)
        hboxmain.addLayout(vboxright)
        hboxmain.setStretch(1,1)
        
        # Central widget
        self.centralWidget().setLayout(hboxmain)
        
        # Set window title and show main window first and widgets after that
        # to avoid extra windows popping up
        self.setWindowTitle('Packman')
        self.show()
        self.center()
        self.frame_widget.show()
        self.available_shapes_widget.show()
        self.info_widget.show()
Example #3
0
class MainWindow(QMainWindow):
    """MNELAB main window.
    """
    def __init__(self):
        super().__init__()

        self.datasets = DataSets()
        self._max_recent = 6  # maximum number of recent files
        self.history = []  # command history

        settings = self._read_settings()
        self.recent = settings["recent"] if settings["recent"] else []

        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle("MNELAB")

        menubar = self.menuBar()

        file_menu = menubar.addMenu("&File")
        file_menu.addAction("&Open...", self.open_file, QKeySequence.Open)
        self.recent_menu = file_menu.addMenu("Open Recent")
        self.recent_menu.aboutToShow.connect(self._update_recent_menu)
        self.recent_menu.triggered.connect(self._load_recent)
        if not self.recent:
            self.recent_menu.setEnabled(False)
        self.close_file_action = file_menu.addAction("&Close", self.close_file,
                                                     QKeySequence.Close)
        file_menu.addSeparator()
        file_menu.addAction("&Quit", self.close, QKeySequence.Quit)

        plot_menu = menubar.addMenu("&Plot")
        self.plot_raw_action = plot_menu.addAction("&Raw data", self.plot_raw)

        tools_menu = menubar.addMenu("&Tools")
        self.filter_action = tools_menu.addAction("&Filter data...",
                                                  self.filter_data)
        self.run_ica_action = tools_menu.addAction("&Run ICA...")
        self.import_ica_action = tools_menu.addAction("&Load ICA...",
                                                      self.load_ica)

        view_menu = menubar.addMenu("&View")
        view_menu.addAction("Show/hide statusbar", self._toggle_statusbar)

        help_menu = menubar.addMenu("&Help")
        help_menu.addAction("&About", self.show_about)
        help_menu.addAction("About &Qt", self.show_about_qt)

        self.names = QStringListModel()
        splitter = QSplitter()
        self.sidebar = QListView()
        self.sidebar.setFocusPolicy(0)
        self.sidebar.setFrameStyle(0)
        self.sidebar.setModel(self.names)
        self.sidebar.clicked.connect(self._update_data)
        splitter.addWidget(self.sidebar)
        self.infowidget = InfoWidget()
        splitter.addWidget(self.infowidget)
        width = splitter.size().width()
        splitter.setSizes((width * 0.25, width * 0.75))
        self.setCentralWidget(splitter)

        self.status_label = QLabel()
        self.statusBar().addPermanentWidget(self.status_label)
        if settings["statusbar"]:
            self.statusBar().show()
        else:
            self.statusBar().hide()

        self._toggle_actions(False)
        self.show()

    def open_file(self):
        """Open file.
        """
        fname = QFileDialog.getOpenFileName(self, "Open file",
                                            filter="*.bdf *.edf")[0]
        if fname:
            self.load_file(fname)

    def load_file(self, fname):
        raw = mne.io.read_raw_edf(fname, stim_channel=None, preload=True)
        name, _ = splitext(split(fname)[-1])
        self.history.append("raw = mne.io.read_raw_edf('{}', "
                            "stim_channel=None, preload=True)".format(fname))
        self.datasets.insert_data(DataSet(name=name, fname=fname, raw=raw))
        self._update_sidebar()
        self._update_main()
        self._add_recent(fname)
        self._update_statusbar()
        self._toggle_actions()

    def close_file(self):
        """Close current file.
        """
        self.datasets.remove_data()
        self._update_sidebar()
        self._update_main()
        self._update_statusbar()

        if not self.datasets:
            self.infowidget.clear()
            self._toggle_actions(False)
            self.status_label.clear()

    def get_info(self):
        """Get basic information on current file.
        """
        raw = self.datasets.current.raw
        fname = self.datasets.current.fname

        nchan = raw.info["nchan"]
        chans = Counter([channel_type(raw.info, i) for i in range(nchan)])

        return {"File name": fname if fname else "-",
                "Number of channels": raw.info["nchan"],
                "Channels": ", ".join(
                    [" ".join([str(v), k.upper()]) for k, v in chans.items()]),
                "Samples": raw.n_times,
                "Sampling frequency": str(raw.info["sfreq"]) + " Hz",
                "Length": str(raw.n_times / raw.info["sfreq"]) + " s",
                "Size in memory": "{:.2f} MB".format(
                    raw._data.nbytes / 1024 ** 2),
                "Size on disk": "-" if not fname else "{:.2f} MB".format(
                    getsize(fname) / 1024 ** 2)}

    def plot_raw(self):
        """Plot raw data.
        """
        events = self.datasets.current.events
        self.datasets.current.raw.plot(events=events)

    def load_ica(self):
        """Load ICA solution from a file.
        """
        fname = QFileDialog.getOpenFileName(self, "Load ICA",
                                            filter="*.fif *.fif.gz")
        if fname[0]:
            self.state.ica = mne.preprocessing.read_ica(fname[0])

    def filter_data(self):
        dialog = FilterDialog()

        if dialog.exec_():
            low, high = dialog.low, dialog.high
            self.datasets.current.raw.filter(low, high)
            self.history.append("raw.filter({}, {})".format(low, high))
            if QMessageBox.question(self, "Add new data set",
                                    "Store the current signals in a new data "
                                    "set?") == QMessageBox.Yes:
                new = DataSet(name="NEW", fname="",
                              raw=self.datasets.current.raw)
                self.datasets.insert_data(new)
                self._update_sidebar()
                self._update_main()
                self._update_statusbar()

    def show_about(self):
        """Show About dialog.
        """
        QMessageBox.about(self, "About MNELAB",
                          "Licensed under the BSD 3-clause license.\n"
                          "Copyright 2017 by Clemens Brunner.")

    def show_about_qt(self):
        """Show About Qt dialog.
        """
        QMessageBox.aboutQt(self, "About Qt")

    def _update_sidebar(self):
        self.names.setStringList(self.datasets.names)
        self.sidebar.setCurrentIndex(self.names.index(self.datasets.index))

    def _update_main(self):
        if self.datasets:
            self.infowidget.set_values(self.get_info())
        else:
            self.infowidget.clear()

    def _update_statusbar(self):
        if self.datasets:
            mb = self.datasets.nbytes / 1024 ** 2
            self.status_label.setText("Total Memory: {:.2f} MB".format(mb))
        else:
            self.status_label.clear()

    def _toggle_actions(self, enabled=True):
        """Toggle actions.
        """
        self.close_file_action.setEnabled(enabled)
        self.plot_raw_action.setEnabled(enabled)
        self.filter_action.setEnabled(enabled)
        self.run_ica_action.setEnabled(enabled)
        self.import_ica_action.setEnabled(enabled)

    def _add_recent(self, fname):
        if fname in self.recent:  # avoid duplicates
            self.recent.remove(fname)
        self.recent.insert(0, fname)
        while len(self.recent) > self._max_recent:  # prune list
            self.recent.pop()
        self._write_settings()
        if not self.recent_menu.isEnabled():
            self.recent_menu.setEnabled(True)

    def _write_settings(self):
        settings = QSettings()
        if self.recent:
            settings.setValue("recent", self.recent)
        settings.setValue("statusbar", not self.statusBar().isHidden())

    def _read_settings(self):
        settings = QSettings()
        recent = settings.value("recent")
        statusbar = settings.value("statusbar")
        if (statusbar is None) or (statusbar == "true"):
            statusbar = True
        else:
            statusbar = False
        return {"recent": recent, "statusbar": statusbar}

    @pyqtSlot(QModelIndex)
    def _update_data(self, selected):
        """Update index and information based on the state of the sidebar.
        """
        if selected.row() != self.datasets.index:
            self.datasets.index = selected.row()
            self.datasets.update_current()
            self._update_main()

    @pyqtSlot()
    def _update_recent_menu(self):
        self.recent_menu.clear()
        for recent in self.recent:
            self.recent_menu.addAction(recent)

    @pyqtSlot(QAction)
    def _load_recent(self, action):
        self.load_file(action.text())

    @pyqtSlot()
    def _toggle_statusbar(self):
        if self.statusBar().isHidden():
            self.statusBar().show()
        else:
            self.statusBar().hide()
        self._write_settings()

    def closeEvent(self, event):
        print("\nCommand History")
        print("===============")
        print("\n".join(self.history))
        event.accept()
Example #4
0
class MainWindow(QtGui.QMainWindow):
    '''
    Creates a main window with the required widgets to handle showing shapes
    and running algorithms for fitting shapes. Inherits QtGui.QMainWindow.
    '''
        

    def __init__(self, frame):
        '''
        Initialize an instance and set the frame which handles algorithms and
        fitting to frame, and the radius to radius.

        @param frame: the frame which will handle fitting
        @type  frame: Frame
        '''
        super(MainWindow, self).__init__()
        self.frame = frame
        self.init_ui()
        
    def init_ui(self):
        '''
        Helper function to initialize UI widgets
        '''
        # set up the frame object
        cw = QtGui.QWidget()
        self.setCentralWidget(cw)
        self.frame_widget = FrameWidget(self.frame.radius)
        self.available_shapes_widget = AvailableShapesWidget()
        self.info_widget = InfoWidget()
        
        self.frame.set_frame_widget(self.frame_widget)
        self.frame.set_available_shapes_widget(self.available_shapes_widget)
        self.frame.set_info_widget(self.info_widget)  

        # Exit action
        exit_action = QtGui.QAction(QtGui.QIcon.fromTheme("exit"),
                                    '&Exit', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Exit application')
        exit_action.triggered.connect(self.close)
        
        # Create a menubar
        #menubar = self.menuBar()
        #file_menu = menubar.addMenu('&File')
        #file_menu.addAction(exit_action)
        
        # Create a toolbar
        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(exit_action)
        
        # Create a statusbar
        self.statusBar()
        self.statuslabel = QtGui.QLabel("Algorithm is <b>stopped</b>")
        self.statusBar().addPermanentWidget(self.statuslabel)
        self.frame.set_statuslabel(self.statuslabel)
        
        # Button to move shapes back to the right
        reset_button = QtGui.QPushButton('Reset', self)
        reset_button.clicked.connect(self.frame.reset_shapes)
        reset_button.resize(reset_button.sizeHint())
        reset_button.setStatusTip("Move all fitted shapes to the available shapes")
        
        # Button to run algorithm
        run_button = QtGui.QPushButton("Run")
        run_button.clicked.connect(self.run_frame_widget_algorithm)
        run_button.setStatusTip("Run the currently selected algorithm")
        
        # Button to stop algorithm
        stop_button = QtGui.QPushButton("Stop")
        stop_button.clicked.connect(self.frame.request_stop)
        stop_button.setStatusTip("Stop the currently running algorithm")
        
        # Button to generate shapes
        generate_button = QtGui.QPushButton('Generate\nshapes', self)
        generate_button.clicked.connect(self.generate_shapes)
        generate_button.setStatusTip('Generate new shapes')
        
        # Button to shuffle shapes
        shuffle_button = QtGui.QPushButton("Shuffle")
        shuffle_button.clicked.connect(self.frame.shuffle_available_shapes)
        shuffle_button.setStatusTip('Shuffle available shapes')
        
        # Button to clear shapes
        clear_button = QtGui.QPushButton("Clear shapes")
        clear_button.clicked.connect(self.frame.clear_available_shapes)
        clear_button.setStatusTip("Clear all shapes if algorithm is not running")
        
        # Button to sort shapes
        sort_button = QtGui.QPushButton("Sort by size")
        sort_button.clicked.connect(self.sort_available_shapes)
        sort_button.setStatusTip("Sort the available shapes by size")
        
        # Combobox to choose algorithm
        algolabel = QtGui.QLabel()
        algolabel.setText("Algorithm:")

        self.algocombo = QtGui.QComboBox()
        self.algocombo.addItems(self.frame.get_algorithms())
        self.algocombo.setStatusTip("Choose the algorithm for packing")
        
        # Checkboxes, spinboxes and labels for shape generation
        self.rectcheckbox = QtGui.QCheckBox()
        self.rectcheckbox.setText('Generate')
        self.rectcheckbox.setChecked(True)

        self.circcheckbox = QtGui.QCheckBox()
        self.circcheckbox.setText('Generate')
        self.circcheckbox.setChecked(True)

        self.rectspinbox = QtGui.QSpinBox()
        self.rectspinbox.setMinimum(0)
        self.rectspinbox.setMaximum(50)
        self.rectspinbox.setValue(5)

        self.circspinbox = QtGui.QSpinBox()
        self.circspinbox.setMinimum(0)
        self.circspinbox.setMaximum(50)
        self.circspinbox.setValue(5)
        self.circspinbox.accelerated = True

        self.circmaxspinbox = QtGui.QSpinBox()
        self.circmaxspinbox.setMinimum(10)
        self.circmaxspinbox.setMaximum(self.frame.radius * 2)
        self.circmaxspinbox.setValue(self.frame.radius)

        self.circminspinbox = QtGui.QSpinBox()
        self.circminspinbox.setMinimum(10)
        self.circminspinbox.setMaximum(self.frame.radius * 2)
        self.circminspinbox.setValue(10)

        self.circmaxspinbox.valueChanged.connect(self.cminChange)
        self.circminspinbox.valueChanged.connect(self.cmaxChange)
        
        self.rectmaxspinbox = QtGui.QSpinBox()
        self.rectmaxspinbox.setMinimum(10)
        self.rectmaxspinbox.setMaximum(self.frame.radius * 2)
        self.rectmaxspinbox.setValue(self.frame.radius)

        self.rectminspinbox = QtGui.QSpinBox()
        self.rectminspinbox.setMinimum(10)
        self.rectminspinbox.setMaximum(self.frame.radius * 2)
        self.rectminspinbox.setValue(10)

        self.rectmaxspinbox.valueChanged.connect(self.rminChange)
        self.rectminspinbox.valueChanged.connect(self.rmaxChange)

        rectlabel = QtGui.QLabel()
        rectlabel.setText("<b>rectangles</b> with sides from")
        circlabel = QtGui.QLabel()
        circlabel.setText("<b>circles</b> with diameters from")
        tolabel1 = QtGui.QLabel()
        tolabel1.setText("to")
        tolabel2 = QtGui.QLabel()
        tolabel2.setText("to")
        emptylabel = QtGui.QLabel()
        emptylabel.setText("")

        # Grid layout for shape generation
        gengrid = QtGui.QGridLayout()
        gengrid.addWidget(self.rectcheckbox, 0, 0)
        gengrid.addWidget(self.circcheckbox, 1, 0)
        gengrid.addWidget(self.rectspinbox, 0, 1)
        gengrid.addWidget(self.circspinbox, 1, 1)
        gengrid.addWidget(rectlabel, 0, 2)
        gengrid.addWidget(circlabel, 1, 2)
        gengrid.addWidget(self.rectminspinbox, 0, 3)
        gengrid.addWidget(self.circminspinbox, 1, 3)
        gengrid.addWidget(tolabel1, 0, 4)
        gengrid.addWidget(tolabel2, 1, 4)
        gengrid.addWidget(self.rectmaxspinbox, 0, 5)
        gengrid.addWidget(self.circmaxspinbox, 1, 5)
        gengrid.addWidget(generate_button, 0, 6, 2, 1)
        gengrid.addWidget(emptylabel, 0, 7)
        gengrid.setColumnStretch(7, 1)
        
        # HBox for run, stop and reset buttons
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(run_button)
        hbox.addWidget(stop_button)
        hbox.addWidget(reset_button)
        hbox.addStretch(1)
        
        # HBox for algorithm combobox and label
        hboxalgo = QtGui.QHBoxLayout()
        hboxalgo.addWidget(algolabel)
        hboxalgo.addWidget(self.algocombo)
        hboxalgo.addStretch(1)
        
        # VBox for left pane
        vboxmain = QtGui.QVBoxLayout()
        vboxmain.addWidget(self.frame_widget)
        vboxmain.addWidget(self.info_widget)
        vboxmain.addLayout(hboxalgo)
        vboxmain.addLayout(hbox)
        
        # Checkbox for reverse sorting and HBox for buttons
        self.reversecheck = QtGui.QCheckBox()
        self.reversecheck.setText('Reverse')
        self.reversecheck.setChecked(False)
        self.reversecheck.setStatusTip("Sort in reverse")
        hboxshape = QtGui.QHBoxLayout()
        hboxshape.addWidget(self.reversecheck)
        hboxshape.addWidget(sort_button)
        hboxshape.addWidget(shuffle_button)
        hboxshape.addWidget(clear_button)
        hboxshape.addStretch(1)
        
        # ScrollArea for available_shapes_widget and VBox for right pane
        aswscroll = QtGui.QScrollArea()
        aswscroll.setWidget(self.available_shapes_widget)
        aswscroll.setMinimumWidth(430)
        aswscroll.setWidgetResizable(True)
        vboxright = QtGui.QVBoxLayout()
        vboxright.addWidget(aswscroll)
        vboxright.addLayout(hboxshape)
        vboxright.addLayout(gengrid)
        
        # HBox to split UI in two panes
        hboxmain = QtGui.QHBoxLayout()
        hboxmain.addLayout(vboxmain)
        hboxmain.addLayout(vboxright)
        hboxmain.setStretch(1,1)
        
        # Central widget
        self.centralWidget().setLayout(hboxmain)
        
        # Set window title and show main window first and widgets after that
        # to avoid extra windows popping up
        self.setWindowTitle('Packman')
        self.show()
        self.center()
        self.frame_widget.show()
        self.available_shapes_widget.show()
        self.info_widget.show()

    def run_frame_widget_algorithm(self):
        '''
        Call frame.run_algorithm with the appropriate algorithm index and
        change the permanent label in the statusbar to show that an algorithm
        is running'''
        self.frame.run_algorithm(self.algocombo.currentIndex())
        self.statuslabel.setText("Algorithm is <b>running</b>")

    def generate_shapes(self):
        '''
        Call frame.generate_shapes with the appropriate parameters as set by
        the user in the interface.
        '''
        if not self.rectcheckbox.isChecked():
            num_rects = 0
        else:
            num_rects = self.rectspinbox.value()
        if not self.circcheckbox.isChecked():
            num_circs = 0
        else:
            num_circs = self.circspinbox.value()
        self.frame.generate_shapes(num_rects, 
                                   num_circs, 
                                   self.rectminspinbox.value(),
                                   self.rectmaxspinbox.value(), 
                                   self.circminspinbox.value(),
                                   self.circmaxspinbox.value())
    
    def sort_available_shapes(self):
        '''
        Call frame.sort_available_shapes with the appropriate parameter to tell
        if shapes should be sorted descending by size or ascending by size
        '''
        self.frame.sort_available_shapes(not self.reversecheck.isChecked())

    def cminChange(self, cmax):
        '''
        Set value of min circle number spinbox to the value of the max circle
        number spinbox if the latter is smaller than the former.
        '''
        self.circminspinbox.setValue(min(self.circminspinbox.value(),
                                         cmax))

    def cmaxChange(self, cmin):
        '''
        Set value of max circle number spinbox to the value of the min circle
        number spinbox if the latter is greater than the former.
        '''
        self.circmaxspinbox.setValue(max(cmin,
                                         self.circmaxspinbox.value()))

    def rminChange(self, rmax):
        '''
        Set value of min rectangle number spinbox to the value of the max
        circle number spinbox if the latter is smaller than the former.
        '''
        self.rectminspinbox.setValue(min(self.rectminspinbox.value(),
                                         rmax))

    def rmaxChange(self, rmin):
        '''
        Set value of max rectangle number spinbox to the value of the min
        circle number spinbox if the latter is greater than the former.
        '''
        self.rectmaxspinbox.setValue(max(rmin,
                                         self.rectmaxspinbox.value()))
    
    def closeEvent(self, event):
        '''
        Called when trying to close programme. Shows a dialog box asking the
        user to confirm the decision.
        '''
        reply = QtGui.QMessageBox.question(self, 'Message',
            "Are you sure you want to quit?", QtGui.QMessageBox.Yes | 
            QtGui.QMessageBox.No, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            self.frame.request_end()
            event.accept()
        else:
            event.ignore()

    def keyPressEvent(self, e):
        '''
        Called when Escape is pressed to close programme.
        '''
        if e.key() == QtCore.Qt.Key_Escape:
            self.close()
            
    def center(self):
        '''
        Center self on screen.
        '''
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())