24

I've only recently started programming and Python (PyQt) in particular. I have my main QMainWindow class. But I wanted to separate it from UI widgets, so that all windows stuff (menus, toolbars, common buttons) are in QMainWindow, but all program/UI specific widgets (pusgbuttons, comboboxes, images, checkboxes etc.) are in a separate QWidget class. But I'm not sure if I'm doing this right.

  1. I have a problem with layouts - something invisible is covering the menus so that they're not clickable by mouse, I think I'm not adding my UI widget to the main window correctly

Here's how I do it:

class MyMainWindow(QMainWindow):
    def __init__(self, parent = None):
        super(MyMainWindow, self).__init__(parent)

        self.main_widget = QWidget(self)
        ...
        self.form_widget = FormWidget(self) 
        #This is my UI widget

        self.main_layout = QVBoxLayout(self.main_widget)
        self.main_layout.sizeConstraint = QLayout.SetDefaultConstraint
        self.main_layout.addWidget(self.form_widget.main_widget) 
        #form_widget has its own main_widget where I put all other widgets onto

        self.main_widget.setLayout(self.main_layout)
        self.setCentralWidget(self.main_widget)
  1. I've seen other Python programs where applications are broken into a lot of small files of code (as I understand it, having everything in on main class is unreadable or unmanageable).

What's your suggestion about breaking code into small pieces? How's it better done? Or for UI it can all be in one big place? Should I break UI code/classes into separate file at all?

Thank you.

[SOLVED]

I found my mistake - I deleted the main_widget from the UI widget class (now all UI widgets are placed directly on the UI class widget itself) and only do this:

self.main_layout.addWidget(self.form_widget)

no more problems with menus

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
linuxoid
  • 1,415
  • 3
  • 14
  • 32
  • I've found my mistake. I've deleted the main_widget from the UI widget and use itself as the holding widget for all other small widgets (buttons, editlines etc.), and the main class simply instantiates the UI widget (see above) - the menu problem solved. – linuxoid Jan 12 '12 at 01:53

3 Answers3

29

Are you looking for something like this? I'm not really sure what your main_widget is

from PyQt4.QtCore import *
from PyQt4.QtGui  import *

import sys

class MyMainWindow(QMainWindow):

    def __init__(self, parent=None):

        super(MyMainWindow, self).__init__(parent)
        self.form_widget = FormWidget(self) 
        self.setCentralWidget(self.form_widget) 


class FormWidget(QWidget):

    def __init__(self, parent):        
        super(FormWidget, self).__init__(parent)
        self.layout = QVBoxLayout(self)

        self.button1 = QPushButton("Button 1")
        self.layout.addWidget(self.button1)

        self.button2 = QPushButton("Button 2")
        self.layout.addWidget(self.button2)

        self.setLayout(self.layout)

app = QApplication([])
foo = MyMainWindow()
foo.show()
sys.exit(app.exec_())
Community
  • 1
  • 1
Jeff
  • 6,932
  • 7
  • 42
  • 72
11

I would recommend using Qt Designer to create as much of the UI as possible.

You will find it much easier to experiment with layouts and so forth that way, and it will automatically keep most of the UI related stuff separate from the rest of your application logic. Do this for the main window, and also for any dialog boxes, however simple.

Then use pyuic4 to compile python modules from all the ui files, and put them all together in their own sub-package.

I would recommend using the -w flag when compiling ui files. This will generate a simple wrapper UI class that can be subclassed directly.

So your main window would end up looking something like this:

from ui.mainwindow import MainWindowUI

class MainWindow(MainWindowUI):
    def __init__(self):
        super(MainWindow, self).__init__()
        # connect signals...
        # do other setup stuff...

Note that all the widgets added in Qt Designer are now accessible directly as attributes of the MainWindow instance.

I would not worry about breaking your application up into smaller modules until later on in development. It may not turn out to be necessary - but if it does, it will become more obvious how to do this once the application starts to become more complex.

There are no hard and fast rules - every project is different.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • 1
    I used to use the Qt Designer, but I don't like its generated code, it looks awful and it's hard to change later on, it also adds a lot of rubbish I don't really need. That's why I started writing UIs myself from scratch. This way I control every line of code. Although I may be missing something but I will learn. Thank you for your reply. – linuxoid Jan 12 '12 at 01:51
  • 8
    @user665327. You've completely misunderstood the purpose of the generated modules. They are not _meant_ for editing. They are only meant for _importing_. I won't say that there's anything wrong with writing UI code by hand, but you are missing out hugely by not using Qt Designer. At the very least, you should use it as a tool for experimenation. And even if you don't use the code generated by `pyuic`, you can still learn _a lot_ by seeing how it does things (this is especially true when it comes to layout management). – ekhumoro Jan 12 '12 at 03:20
10
import sys
from PyQt4 import QtCore, QtGui


class MainWindow(QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.form_widget = FormWidget(self)
        _widget = QtGui.QWidget()
        _layout = QtGui.QVBoxLayout(_widget)
        _layout.addWidget(self.form_widget)
        self.setCentralWidget(_widget)

class FormWidget(QtGui.QWidget):

    def __init__(self, parent):
        super(FormWidget, self).__init__(parent)
        self.__controls()
        self.__layout()

    def __controls(self):
        self.label = QtGui.QLabel("Name for backdrop")
        self.txted = QtGui.QLineEdit()
        self.lbled = QtGui.QLabel("Select a readNode")
        self.cmbox = QtGui.QComboBox()

    def __layout(self):
        self.vbox = QtGui.QVBoxLayout()
        self.hbox = QtGui.QHBoxLayout()
        self.h2Box = QtGui.QHBoxLayout()

        self.hbox.addWidget(self.label)
        self.hbox.addWidget(self.txted)

        self.h2Box.addWidget(self.lbled)
        self.h2Box.addWidget(self.cmbox)

        self.vbox.addLayout(self.hbox)
        self.vbox.addLayout(self.h2Box)
        self.setLayout(self.vbox)

def main():
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    win.show()
    app.exec_()

if __name__ == '__main__':
    sys.exit(main()) 

correct way!!!