0
votes

I am using Qt Designer for designing my user interfaces, and I want to build custom widgets which could be a combination of existing qt widgets such as a QLabel and QPushButton attached screenshot

enter image description here.

Now I would like this to be independent with it's own business logic, signals and slots in a separate python file, but would want to add this as a component to my main screen.

I tried above by creating seprate ui file of type widget, but when I promote that from my MainWindow, it won't show up, and the code generated by pyuic would add it to the layout, but it is not rendered in main window.

enter image description here

Is there a way of doing that in PyQt and QDesigner?

EDIT:

Here is the actual code:

timer.py:

from PyQt4 import QtGui, QtCore
from datetime import timedelta, datetime

from logbook import info


import timer_ui


class Timer(QtGui.QWidget, timer_ui.Ui_TimerWidget):

    start_time = None
    running = False
    total_time = 0
    time_spent = ''

    activity = None

    stopwatch = 0
    elapsed_time = None
    total_elapsed_time = timedelta()

    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)

        self.setupUi(parent) #for custom widget parent is main window here which is dashboard

        self.lcdNumber.setDigitCount(12)

        self.qt_timer = QtCore.QTimer(self)
        self.qt_timer.timeout.connect(self.timer_event)
        self.qt_timer.start(1000)

        self.goButton.clicked.connect(self.go)
        self.breakButton.clicked.connect(self.break_timer)

    def go(self):
        # date text format .strftime('%a, %d %b %Y %H:%M:%S')
        self.start_time = datetime.now().replace(microsecond=0)
        self.running = True




        self.goButton.setEnabled(False)
        self.breakButton.setEnabled(True)

    def break_timer(self):
        ''' break finishes the activity '''
        break_time = datetime.now().replace(microsecond=0)
        self.activity.log_break(break_time.isoformat())
        self.activity = None  # activity completed

        self.total_elapsed_time += self.elapsed_time
        info(self.total_elapsed_time)

        self.running = False


        # self.lcdNumber.display(str(self.timer.get_elapsed()))

        self.goButton.setEnabled(True)
        self.breakButton.setEnabled(False)


    def timer_event(self):
        '''Updates the widget every second'''

        if self.running == True:
            current_time = datetime.now().replace(microsecond=0)

            # if self.elapsed_time is None:

            self.elapsed_time = current_time - self.start_time
            # else:
            #self.elapsed_time += current_time - self.timer.start_time.replace(microsecond=0)
            if self.total_elapsed_time is not None:
                 self.lcdNumber.display(str(self.elapsed_time + self.total_elapsed_time))
            else:
                self.lcdNumber.display(str(self.elapsed_time))

mainwindow.py:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'dashboard.ui'
#
# Created: Sat Mar 19 11:40:35 2016
#      by: PyQt4 UI code generator 4.10.4
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(772, 421)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.verticalLayout = QtGui.QVBoxLayout()
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.widget = Timer(self.centralwidget)

        self.verticalLayout.addWidget(self.widget)

        self.horizontalLayout = QtGui.QHBoxLayout()

        self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
        self.userNameLabel = QtGui.QLabel(self.centralwidget)
        self.userNameLabel.setObjectName(_fromUtf8("userNameLabel"))
        self.horizontalLayout.addWidget(self.userNameLabel)
        self.logoutButton = QtGui.QPushButton(self.centralwidget)
        self.logoutButton.setEnabled(True)
        self.logoutButton.setObjectName(_fromUtf8("logoutButton"))
        self.horizontalLayout.addWidget(self.logoutButton)

        self.verticalLayout.addLayout(self.horizontalLayout)

        self.listView = QtGui.QListView(self.centralwidget)
        self.listView.setObjectName(_fromUtf8("listView"))
        self.verticalLayout.addWidget(self.listView)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)
        self.menuBar = QtGui.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 772, 23))
        self.menuBar.setObjectName(_fromUtf8("menuBar"))
        MainWindow.setMenuBar(self.menuBar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "Title", None))
        self.userNameLabel.setText(_translate("MainWindow", "You are now logged in as", None))
        self.logoutButton.setText(_translate("MainWindow", "Logout", None))

from timer import Timer

timer_ui.py:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'timer.ui'
#
# Created: Sat Mar 19 11:41:40 2016
#      by: PyQt4 UI code generator 4.10.4
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_TimerWidget(object):
    def setupUi(self, TimerWidget):
        TimerWidget.setObjectName(_fromUtf8("TimerWidget"))
        TimerWidget.resize(412, 52)
        self.horizontalLayout = QtGui.QHBoxLayout(TimerWidget)
        self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
        self.label = QtGui.QLabel(TimerWidget)
        self.label.setObjectName(_fromUtf8("label"))
        self.horizontalLayout.addWidget(self.label)
        self.lcdNumber = QtGui.QLCDNumber(TimerWidget)
        self.lcdNumber.setAutoFillBackground(False)
        self.lcdNumber.setNumDigits(12)
        self.lcdNumber.setSegmentStyle(QtGui.QLCDNumber.Flat)
        self.lcdNumber.setObjectName(_fromUtf8("lcdNumber"))
        self.horizontalLayout.addWidget(self.lcdNumber)
        self.goButton = QtGui.QPushButton(TimerWidget)
        self.goButton.setObjectName(_fromUtf8("goButton"))
        self.horizontalLayout.addWidget(self.goButton)
        self.breakButton = QtGui.QPushButton(TimerWidget)
        self.breakButton.setObjectName(_fromUtf8("breakButton"))
        self.horizontalLayout.addWidget(self.breakButton)

        self.retranslateUi(TimerWidget)
        #QtCore.QMetaObject.connectSlotsByName(TimerWidget)

    def retranslateUi(self, TimerWidget):
        #TimerWidget.setWindowTitle(_translate("TimerWidget", "Form", None))
        self.label.setText(_translate("TimerWidget", "Total hours spent", None))
        self.goButton.setText(_translate("TimerWidget", "Go!", None))
        self.breakButton.setText(_translate("TimerWidget", "Break", None))
2

2 Answers

1
votes

Promoted widgets are just placeholders for standard Qt widgets. You cannot create a custom widget for Qt Designer that way.

It can be done, but the process is much more complicated than simple widget promotion. See Writing Qt Designer Plugins in the PyQt docs, and for a detailed tutorial, see Using Python Custom Widgets In Qt Designer on the Python Wiki. The PyQt source code also has many more examples (look in examples/designer/plugins).

EDIT:

There are two problems with your code. Firstly, you are passing the wrong argument to setupUi in the Timer class. You should fix it like this:

class Timer(QtGui.QWidget, timer_ui.Ui_TimerWidget):
    ...
    def __init__(self, parent=None):
        super(Timer, self).__init__(parent)
        self.setupUi(self) # pass in self, not parent

Secondly, you edited the mainwindow.py file and broke one of the layouts. Never, ever edit the modules generated by pyuic! The line you broke is this one:

        # self.verticalLayout = QtGui.QVBoxLayout()
        self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)

But don't try to fix this by editing - instead, make sure you regenerate all the ui modules with pyuic so you get back to clean, unedited files again.

0
votes

I do this all the time, i.e build Custom widgets and use them inside my main window.

I don't know what is the top level widget for your custom widget, but it should preferably be QWidget or QFrame from the list of Containers in the Qt Designer and add the child widgets inside it, let's name this custom widget file as custom.ui.

Next, create a Python class as follows:

Form, Base = uic.loadUiType('/path/to/custom.ui') # this path should be a relative path. For testing you can use absolute path.
class CustomWidget(Form, Base):
    def __init__(self, parent=None):
        super(CustomWidget, self).__init__(parent)
        self.setupUi(self)

You can add any number of signals and slots for the custom logic to the above class.

Similarly create a class for your main window and then create an object of CustomWidget inside that main class and add that object to the layout.

Form2, Base2 = uic.loadUiType('/path/to/dashboard.ui') # this path should be a relative path. For testing you can use absolute path.
class Window(Form2, Base2):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.setupUi(self)
        self.cWidget = CustomWidget(self)
        self.layout.addWidget(self.cWidget)

Note: This code is compatible with Python 2.x, if you are using Python 3.x, make necessary changes. This code is not tested, so syntax errors must also be resolved if found.