Why am I getting a QWidget error?

373 views Asked by At

I have recently found this person's code that I could possibly be able to use for my project but I'm still pretty new to Python so I'm having a really rough time understanding the code and the error that keeps occurring even though the code is exactly the same as the person I borrowed from. I'm trying to re-purpose it so that I can paint on a window class that I have already created. Am I just missing something very minor or something major? Here is the code:

from PyQt4 import QtGui,QtCore, uic
import sys

#Path to UI File
uifile = 'C:\Users\owner\Downloads\Paint_on_a_panel_PyQt\PaintOnAPanel.ui'
form, base = uic.loadUiType(uifile)

## My Own Colour Class, simple and light weight
class Colour3:
    R = 0
    G = 0
    B = 0
    #CONSTRUCTOR
    def __init__(self): 
        self.R = 0
        self.G = 0
        self.B = 0
    #CONSTRUCTOR - with the values to give it
    def __init__(self, nR, nG, nB):
        self.R = nR
        self.G = nG
        self.B = nB

## My Own Point Class, simple and light weight
class Point:
    #X Coordinate Value
    X = 0
    #Y Coordinate Value
    Y = 0
    #CONSTRUCTOR
    def __init__(self): 
        self.X = 0
        self.Y = 0
    #CONSTRUCTOR - with the values to give it
    def __init__(self, nX, nY):
        self.X = nX
        self.Y = nY
    #So we can set both values at the same time
    def Set(self,nX, nY):
        self.X = nX
        self.Y = nY 


## Shape class; holds data on the drawing point
class Shape:
    Location = Point(0,0)
    Width = 0.0
    Colour = Colour3(0,0,0)
    ShapeNumber = 0
    #CONSTRUCTOR - with the values to give it
    def __init__(self, L, W, C, S):
        self.Location = L
        self.Width = W
        self.Colour = C
        self.ShapeNumber = S


class Shapes:
    #Stores all the shapes
    __Shapes = []
    def __init__(self):
        self.__Shapes = []
    #Returns the number of shapes being stored.
    def NumberOfShapes(self):
        return len(self.__Shapes)
    #Add a shape to the database, recording its position,
    #width, colour and shape relation information
    def NewShape(self,L,W,C,S):
        Sh = Shape(L,W,C,S)
        self.__Shapes.append(Sh)
    #returns a shape of the requested data.
    def GetShape(self, Index):
        return self.__Shapes[Index]
    #Removes any point data within a certain threshold of a point.
    def RemoveShape(self, L, threshold):
        #do while so we can change the size of the list and it wont come back to bite me in the ass!!
        i = 0
        while True:
            if(i==len(self.__Shapes)):
                break 
            #Finds if a point is within a certain distance of the point to remove.
            if((abs(L.X - self.__Shapes[i].Location.X) < threshold) and (abs(L.Y - self.__Shapes[i].Location.Y) < threshold)):
                #removes all data for that number
                del self.__Shapes[i]
                #goes through the rest of the data and adds an extra
                #1 to defined them as a seprate shape and shuffles on the effect.
                for n in range(len(self.__Shapes)-i):
                    self.__Shapes[n+i].ShapeNumber += 1
                #Go back a step so we dont miss a point.
                i -= 1
            i += 1



class CreateUI():
    Brush = True
    DrawingShapes = Shapes()
    IsPainting = False
    IsEraseing = False

    CurrentColour = Colour3(0,0,0)
    CurrentWidth = 10
    ShapeNum = 0
    IsMouseing = False
    PaintPanel = 0
    #Constructor
    def __init__(self):
        super(base,self).__init__()
        self.setupUi(self)
        self.setObjectName('Rig Helper')
        self.PaintPanel = Painter(self)
        self.PaintPanel.close()
        self.DrawingFrame.insertWidget(0,self.PaintPanel)
        self.DrawingFrame.setCurrentWidget(self.PaintPanel)
        self.Establish_Connections()

    def SwitchBrush(self):
        if(self.Brush == True):
            self.Brush = False
        else:
            self.Brush = True

    def ChangeColour(self):
        col = QtGui.QColorDialog.getColor()
        if col.isValid():
            self.CurrentColour = Colour3(col.red(),col.green(),col.blue())

    def ChangeThickness(self,num):
        self.CurrentWidth = num

    def ClearSlate(self):
        self.DrawingShapes = Shapes()
        self.PaintPanel.repaint()


    def Establish_Connections(self):
        QtCore.QObject.connect(self.BrushErase_Button, QtCore.SIGNAL("clicked()"),self.SwitchBrush)
        QtCore.QObject.connect(self.ChangeColour_Button, QtCore.SIGNAL("clicked()"),self.ChangeColour)
        QtCore.QObject.connect(self.Clear_Button, QtCore.SIGNAL("clicked()"),self.ClearSlate)
        QtCore.QObject.connect(self.Thickness_Spinner, QtCore.SIGNAL("valueChanged(int)"),self.ChangeThickness)

class Painter(QtGui.QWidget):
    ParentLink = 0
    MouseLoc = Point(0,0)  
    LastPos = Point(0,0)  
    def __init__(self,parent):
        super(Painter, self).__init__()
        self.ParentLink = parent
        self.MouseLoc = Point(0,0)
        self.LastPos = Point(0,0) 
    #Mouse down event
    def mousePressEvent(self, event): 
        if(self.ParentLink.Brush == True):
            self.ParentLink.IsPainting = True
            self.ParentLink.ShapeNum += 1
            self.LastPos = Point(0,0)
        else:
            self.ParentLink.IsEraseing = True      
    #Mouse Move event        
    def mouseMoveEvent(self, event):
        if(self.ParentLink.IsPainting == True):
            self.MouseLoc = Point(event.x(),event.y())
            if((self.LastPos.X != self.MouseLoc.X) and (self.LastPos.Y != self.MouseLoc.Y)):
                self.LastPos =  Point(event.x(),event.y())
                self.ParentLink.DrawingShapes.NewShape(self.LastPos,self.ParentLink.CurrentWidth,self.ParentLink.CurrentColour,self.ParentLink.ShapeNum)
            self.repaint()
        if(self.ParentLink.IsEraseing == True):
            self.MouseLoc = Point(event.x(),event.y())
            self.ParentLink.DrawingShapes.RemoveShape(self.MouseLoc,10)     
            self.repaint()        

    #Mose Up Event         
    def mouseReleaseEvent(self, event):
        if(self.ParentLink.IsPainting == True):
            self.ParentLink.IsPainting = False
        if(self.ParentLink.IsEraseing == True):
            self.ParentLink.IsEraseing = False  

    def paintEvent(self,event):
        painter = QtGui.QPainter()
        painter.begin(self)
        self.drawLines(event, painter)
        painter.end()

    def drawLines(self, event, painter):
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        for i in range(self.ParentLink.DrawingShapes.NumberOfShapes()-1):

            T = self.ParentLink.DrawingShapes.GetShape(i)
            T1 = self.ParentLink.DrawingShapes.GetShape(i+1)

            if(T.ShapeNumber == T1.ShapeNumber):
                pen = QtGui.QPen(QtGui.QColor(T.Colour.R,T.Colour.G,T.Colour.B), T.Width/2, QtCore.Qt.SolidLine)
                painter.setPen(pen)
                painter.drawLine(T.Location.X,T.Location.Y,T1.Location.X,T1.Location.Y)

#Main UI Class
class CreateUI(QtGui.QMainWindow):
    Brush = True
    DrawingShapes = Shapes()
    IsPainting = False
    IsEraseing = False

    CurrentColour = Colour3(0,0,0)
    CurrentWidth = 10
    ShapeNum = 0
    IsMouseing = False
    PaintPanel = 0
    #Constructor
    def __init__(self):
        super(CreateUI,self).__init__()
        self.setupUi(self)
        self.setObjectName('Rig Helper')
        self.PaintPanel = Painter(self)
        self.PaintPanel.close()
        self.DrawingFrame.insertWidget(0,self.PaintPanel)
        self.DrawingFrame.setCurrentWidget(self.PaintPanel)
        self.Establish_Connections()

    def SwitchBrush(self):
        if(self.Brush == True):
            self.Brush = False
        else:
            self.Brush = True

    def ChangeColour(self):
        col = QtGui.QColorDialog.getColor()
        if col.isValid():
            self.CurrentColour = Colour3(col.red(),col.green(),col.blue())

    def ChangeThickness(self,num):
        self.CurrentWidth = num

    def ClearSlate(self):
        self.DrawingShapes = Shapes()
        self.PaintPanel.repaint()  


    def Establish_Connections(self):
        QtCore.QObject.connect(self.BrushErase_Button, QtCore.SIGNAL("clicked()"),self.SwitchBrush)
        QtCore.QObject.connect(self.ChangeColour_Button, QtCore.SIGNAL("clicked()"),self.ChangeColour)
        QtCore.QObject.connect(self.Clear_Button, QtCore.SIGNAL("clicked()"),self.ClearSlate)
        QtCore.QObject.connect(self.Thickness_Spinner, QtCore.SIGNAL("valueChanged(int)"),self.ChangeThickness)



#New instance of the UI Form class, creating and showing it
def main():
    global PyForm
    PyForm = CreateUI()
    PyForm.show()

#check if another instance exsists.
if __name__=="__main__":
    main()

The error that occurs is: QWidget: Must construct a QApplication before a QPaintDevice. Much thanks!

2

There are 2 answers

2
Alex Huszagh On BEST ANSWER

The error is exactly what it says it is: You need to construct a QApplication before you try to make any QWidgets, or the Qt system does not know where to start.

Replace main() with this:

def main():
    app = QApplication(sys.argv)
    global PyForm
    PyForm = CreateUI()
    PyForm.show()
    sys.exit(app.exec_())

And add an "import sys" somewhere at the start of the file.

The reasons for using the sys constructor are the same as the answer above me elucidates: it allows you to pass parameters if need be to the QApplication, and call a clean exit upon the app closing.

1
mdurant On

If you look at any PyQt-based program, you will see the same general pattern for starting things up. In your case it would go:

def main():
    global PyForm
    app = QtGui.QApplication([])
    PyForm = CreateUI()
    PyForm.show()
    app.exec_()

where you could replace the [] with sys.argv (to pass display options to qt) and might call sys.exit() at the end.