Example #1
0
class RandomTimer(QObject):
    timeout = Signal()
    intervalChanged = Signal()
    activeChanged = Signal()

    def __init__(self):
        super(RandomTimer, self).__init__()

        self.timer = QTimer()
        self.timer.timeout.connect(self.timeout)

    def interval(self):
        return self.timer.interval()

    def setInterval(self, msec):
        if self.timer.interval() != msec:
            self.timer.setInterval(msec)
            self.intervalChanged.emit()
            print("interval = {}".format(self.timer.interval()))

    @Slot()
    def isActive(self):
        return self.timer.isActive()

    @Slot()
    def start(self):
        if not self.timer.isActive():
            self.timer.start()
            self.activeChanged.emit()

    @Slot()
    def stop(self):
        if self.timer.isActive():
            self.timer.stop()
            self.activeChanged.emit()

    @Slot(int, int, result=int)
    def randomInterval(self, min, max):
        range = max - min
        msec = min + random.randint(0, range)
        return msec

    interval = Property(int, interval, setInterval, notify=intervalChanged)
    active = Property(bool, isActive, notify=activeChanged)
Example #2
0
class LcdNumber(QLCDNumber):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowFlag(Qt.FramelessWindowHint, True)
        self.setAttribute(Qt.WA_StyledBackground)
        self.resize(130, 40)
        self.move(680, 150)
        self.setDigitCount(8)
        self.setMode(QLCDNumber.Dec)
        self.setSegmentStyle(QLCDNumber.Flat)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.time = datetime.strptime('00:00:00', '%H:%M:%S')
        self.one_seconds = timedelta(seconds=1)
        self.timer.start(1000)
        self.display(self.time.strftime('%H:%M:%S'))
        self.setStyleSheet("""
                QLCDNumber{
                    border:none;
                }
        """)
        self.timer.interval()

    def re_init(self):
        self.timer.start()
        self.time = datetime.strptime('00:00:00', '%H:%M:%S')
        self.display(self.time.strftime('%H:%M:%S'))
        self.timer.interval()
        pass

    def update_time(self):
        self.time += self.one_seconds
        self.display(self.time.strftime('%H:%M:%S'))

    def stop(self):
        if self.timer.isActive():
            self.timer.stop()
        else:
            self.timer.start()
Example #3
0
class Robotic_Viewer_NG(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle('A Wonderfull Name')
        self.resize(1080,720)
        self.setWindowIcon(QIcon(QPixmap('smallLogo.png')))
        self.setStyleSheet("QMainWindow {background: '#f1fcb3';}")
        self.setMinimumWidth(960)
        self.setMinimumHeight(720)

        ## Création des widgets
        self.settingsWidget = QWidget(self)
        ## Configuration des signals/slots
        self.timeSimu = 90
        self.time = 0
        self.rythmSim = QTimer()
        self.rythmSim.timeout.connect(self.simulationStep)
        self.buildGameElements()
        ## Configuration des widgets
        self.mapPx = QImage('TableCDFR2020.png').scaledToWidth(self.width()*2/3,aspectmode='KeepAspectRatio')
        self.scaleFactor = 300/self.mapPx.width()
        self.buildSettingsGroupBox(self.settingsWidget)
        self.settingsWidget.setStyleSheet("background: '#94e4f7';")
        self.fitToWindow()

    def buildGameElements(self):
        # Création d'une stratégie de jeux        
        self.robotM = VirtualRobot('MarioBot')
        self.robotG = VirtualRobot('GuiguiBot')
        self.robots = [ self.robotM , self.robotG ]

        self.brain = Strategy()
        self.brain.addRobot(self.robotM)
        self.brain.addRobot(self.robotG)

        self.robotM_traget = Circle(0,0,5)
        self.robotG_traget = Circle(0,0,5)

        # Création d'action supplémentaires
        self.act1 = Action('ManuelM',90,25,np.deg2rad(0))
        self.act2 = Action('ManuelG',70,25,np.deg2rad(180))

        self.brain.addAction(self.act1.getID(),self.act1)
        self.brain.addAction(self.act2.getID(),self.act2)

        # Création d'element de jeux externes
        l_cercA = Circle(80,-30,30)
        l_cercB = Circle(30,330,30)
        l_cercC = Circle(80,330,30)
        l_cercD = Circle(30,-30,40)

        self.staticElts = [l_cercA,l_cercB,l_cercC,l_cercD]

        i = 0
        for elt in self.staticElts :
            elt.setID(elt.getID()+' '+repr(i))
            print(elt)
            i = i +1

        self.rstElmtPos()

    def rstElmtPos(self):
        self.robotM.setStaticState(90,25,np.deg2rad(0))
        self.robotG.setStaticState(70,25,np.deg2rad(180))

        self.repaint()

    def rstElmtStat(self):
        #self.staticElts = [self.cercA,self.cercB,self.cercC,self.cercD]
        self.repaint()

    def paintEvent(self, event):
        mapP = QPainter()
        mapP.begin(self)
        mapP.drawImage(self.centralGP.x(),self.centralGP.y(),self.mapPx)
        mapP.end()

        self.drawRobot(self.robotM,'blue')
        self.drawRobot(self.robotG,'purple')
        self.drawElts(self.staticElts,'black','pink')
        self.drawElts([self.robotM_traget,self.robotG_traget],'red','white')

    def drawElts(self,elt_list,colorElt,colotTxt):
        for elt in elt_list :
            l_pp = self.getRealFromUiCoord(elt.getX(),elt.getY())
            eltP = QPainter()
            eltP.begin(self)
            eltP.setBrush(QBrush(QColor(colorElt)))

            myPen = QPen()
            myPen.setBrush(QBrush(QColor(colotTxt)))

            if 'rect' in elt.getID() :
                l_pw = elt.getWidth()/self.scaleFactor
                l_pl = elt.getLength()/self.scaleFactor
                eltP.drawRect(l_pp.getX()-(l_pw/2),l_pp.getY()-(l_pl/2),l_pw,l_pl)

                eltP.setPen(myPen)
                eltP.drawText(l_pp.getX()-(l_pw/4),l_pp.getY(),l_pw,l_pl,1,elt.getID())
            elif 'circle' in elt.getID() :
                l_pr = elt.getRayon()/self.scaleFactor
                eltP.drawEllipse(l_pp.getX()-(l_pr/2),l_pp.getY()-(l_pr/2),l_pr,l_pr)

                eltP.setPen(myPen)
                eltP.drawText(l_pp.getX()-l_pr/3,l_pp.getY(),l_pr,l_pr,1,elt.getID())
            else :
                pass
            eltP.end()

    def drawRobot(self, robot, color):
        # Position computing
        relativePoint = self.getRealFromUiCoord(robot.getPosition().x,robot.getPosition().y)
        w_tr = robot.getWidth()/(2*self.scaleFactor)
        l_tr = robot.getLength()/(4*self.scaleFactor)
        x_center = relativePoint.x # w_tr
        y_center = relativePoint.y #- l_tr

        pts_list = QPoint(-w_tr,-l_tr), QPoint(-w_tr,l_tr), QPoint(0,l_tr*1.2), QPoint(w_tr,l_tr),QPoint(w_tr,-l_tr),QPoint(-w_tr,-l_tr)

        polyst = self.rotatePolygon(pts_list,robot.getAngle())
        poly = QPolygon(polyst)
        poly.translate(x_center,y_center)

        # Draw Item
        myPen = QPen()
        myPen.setBrush(QBrush(QColor(color)))
        myPen.setWidth(5)

        robotP = QPainter()
        robotP.begin(self)
        robotP.setPen(myPen)
        robotP.drawPolyline(poly)
        robotP.end()

    def rotatePolygon(self,polygon,teta):
        rotatedPolygon = []
        for corner in polygon :
            Vi = np.array([corner.x(),corner.y(),1])
            Mrot = np.array([ [np.cos(teta), np.sin(teta),0],
                              [-np.sin(teta), np.cos(teta),0],
                              [ 0,0,0] ])
            Vo = np.dot(Mrot,np.transpose(Vi))

            rotatedPolygon.append(QPoint(Vo[0],Vo[1]))
        return rotatedPolygon

    def buildSettingsGroupBox(self, a_widget) :
        self.settingsGroupBox = QGroupBox('Settings')
        l_gridLayout = QGridLayout()

        self.labelRegTitle = QLabel('Position')           # 0
        self.lineEditReg = QLineEdit()                      # 1
        self.lineEditReg.setText("X : 0 - Y : 0 - T : 0")   # 1
        self.pushBtnRegRst = QPushButton('Restart')         # 2
        self.pushBtnRegRst.clicked.connect(self.rstElmtPos) # 2
        self.labelRegTarget = QLabel('Target : not defined')  # 3
        self.selectedRobot = 0

        self.labelSimuTitle = QLabel('Simulation')      # 0
        self.pushBtnStartSimu = QPushButton('Start')    # 1
        self.pushBtnStartSimu.clicked.connect(self.startSimu)
        self.pushBtnStopSimu = QPushButton('Stop')      # 2
        self.pushBtnStopSimu.clicked.connect(self.stopSimu)
        self.labelSimuInfos = QLabel('Durée de simulation : '+repr(self.timeSimu)+' sec')   # 4
        self.hSlider= QSlider()                         # 3
        self.hSlider.setOrientation(Qt.Horizontal)      # 3
        self.hSlider.valueChanged.connect(self.sliderEvolution)
        self.hSlider.setValue(self.timeSimu)
        self.hSlider.setRange(0,120)

        self.labelPfTitle = QLabel('Path Finding')      # 0
        self.comboBoxAlgoPF = QComboBox()               # 1
        self.comboBoxAlgoPF.addItem('A*')               # 1
        self.comboBoxAlgoPF.addItem('Dijtra')           # 1
        self.comboBoxLenghtPF = QComboBox()             # 2
        self.comboBoxLenghtPF.addItem('Manathan')       # 2
        self.comboBoxLenghtPF.addItem('Euclidian')      # 2
        self.pushBtnPF = QPushButton('NOT USED')         # 3
        self.pushBtnPF.clicked.connect(self.rstElmtStat)
        self.labelPfElt = QLabel('')                    # 4

        self.labelStratTitle = QLabel('Stratégie')      # 0
        self.labelStratRbtG = QLabel('GuiGuiBot')       # 0
        self.comboBoxStratRbtG = QComboBox()            # 1
        for l_act in self.brain.getActionKeys() :
            self.comboBoxStratRbtG.addItem(l_act)
        self.comboBoxStratRbtG.currentIndexChanged.connect(self.updateStrategies)
        self.labelStratRbtM = QLabel('MarioBot')        # 2
        self.comboBoxStratRbtM = QComboBox()            # 3
        for l_act in self.brain.getActionKeys() :
            self.comboBoxStratRbtM.addItem(l_act)
        self.comboBoxStratRbtM.currentIndexChanged.connect(self.updateStrategies)

        l_gridLayout.addWidget(self.labelRegTitle,0,0)
        l_gridLayout.addWidget(self.lineEditReg,1,0)
        l_gridLayout.addWidget(self.pushBtnRegRst,2,0)
        l_gridLayout.addWidget(self.labelRegTarget,3,0)

        l_gridLayout.addWidget(self.labelSimuTitle,0,1)
        l_gridLayout.addWidget(self.pushBtnStartSimu,1,1)
        l_gridLayout.addWidget(self.pushBtnStopSimu,2,1)
        l_gridLayout.addWidget(self.hSlider,3,1)
        l_gridLayout.addWidget(self.labelSimuInfos,4,1)

        l_gridLayout.addWidget(self.labelStratTitle,0,2)
        l_gridLayout.addWidget(self.labelStratRbtG,1,2)
        l_gridLayout.addWidget(self.comboBoxStratRbtG,2,2)
        l_gridLayout.addWidget(self.labelStratRbtM,3,2)
        l_gridLayout.addWidget(self.comboBoxStratRbtM,4,2)

        l_gridLayout.addWidget(self.labelPfTitle,0,3)
        l_gridLayout.addWidget(self.comboBoxAlgoPF,1,3)
        l_gridLayout.addWidget(self.comboBoxLenghtPF,2,3)
        l_gridLayout.addWidget(self.pushBtnPF,3,3)
        l_gridLayout.addWidget(self.labelPfElt,4,3)

        l_gridLayout.setColumnStretch(0, a_widget.width()/4)
        l_gridLayout.setColumnStretch(1, a_widget.width()/4)
        l_gridLayout.setColumnStretch(2, a_widget.width()/4)
        l_gridLayout.setColumnStretch(3, a_widget.width()/4)

        self.settingsGroupBox.setLayout(l_gridLayout)
        vbox = QVBoxLayout()
        vbox.addWidget(self.settingsGroupBox)

        a_widget.setLayout(vbox)

    def startSimu(self):
        self.time = 0
        self.rythmSim.start(100)

    def stopSimu(self):
        self.rythmSim.stop()

    def updateStrategies(self):
        self.brain.assignAction(self.comboBoxStratRbtG.currentText(),self.robotG.getID())
        self.brain.assignAction(self.comboBoxStratRbtM.currentText(),self.robotM.getID())

    def simulationStep(self):
        if self.time < self.timeSimu :
            # Appel de la stratégie
            self.brain.beat()

            # Simulation des robots
            for rbt in self.robots :
                rbt.updateState(self.rythmSim.interval())               # Refresh position
                rbt.updateSensors(self.brain.getElements().values())    # Refresh sensors data

            # Mise à jour de l'interface graphique
            self.time = self.time + (self.rythmSim.interval()/1000)
            self.labelSimuInfos.setText('Simulation : '+repr(round(self.time,2))+'\t/ '+repr(self.timeSimu)+' sec')
            self.robotM_traget.setXY(self.robotM.getTarget().getPosition().getX(),self.robotM.getTarget().getPosition().getY())
            self.robotG_traget.setXY(self.robotG.getTarget().getPosition().getX(),self.robotG.getTarget().getPosition().getY())
            self.repaint()
        else :
            pass

    def fitToWindow(self):
        self.settingsWidget.setGeometry(0,0,self.width(),self.height()/4)
        self.centralGP = QPoint((self.width()-self.mapPx.width())/2, self.settingsWidget.height()+20)
        self.repaint()

    def getUiFromRealCoord(self,x,y):
        ty = -self.centralGP.x()
        tx = -self.centralGP.y()
        s = self.scaleFactor

        Vre = np.array([x,y,s])
        Mtr = np.array([ [ 0,s,tx],
                         [ s,0,ty],
                         [ 0,0,0] ])
        Vui = np.dot(Mtr,np.transpose(Vre))

        l_x = Vui[0]
        l_y = Vui[1]
        return Point(round(l_x),round(l_y))

    def getRealFromUiCoord(self,x,y):
        tx = (y/self.scaleFactor)
        ty = (x/self.scaleFactor)
        Vui = np.array([self.centralGP.x(),self.centralGP.y(),1])
        Mtr = np.array([ [ 1,0,tx],
                         [ 0,1,ty],
                         [ 0,0,1] ])
        Vre = np.dot(Mtr,np.transpose(Vui))

        l_x = Vre[0]
        l_y = Vre[1]
        return Point(round(l_x),round(l_y))

    def mousePressEvent(self, it):
        rel_pt = self.getUiFromRealCoord(it.x(),it.y())
        if it.button()== Qt.MouseButton.RightButton and self.selectedRobot != 0:
            self.labelRegTarget.setText('Manual target of '+self.selectedRobot.getID()+' X: '+repr(rel_pt.x)+' Y:'+repr(rel_pt.y))
            if 'M' in self.selectedRobot.getID():
                self.brain.modifyAction('ManuelM',State(rel_pt.x,rel_pt.y,np.pi))
            if 'G' in self.selectedRobot.getID():
                self.brain.modifyAction('ManuelG',State(rel_pt.x,rel_pt.y,np.pi))

        if it.button()== Qt.MouseButton.LeftButton :
            for rbt in self.robots :
                if rbt.getPosition().distanceFrom(rel_pt)<10 :
                    self.selectedRobot = rbt
                    self.labelRegTarget.setText(self.selectedRobot.getID()+ ' selected...')

    def refreshEltLabel(self):
        l_str = ''
        for i in self.brain.getElementsKeys():
            l_str = i+' '+l_str
        self.labelPfElt.setText(l_str)

    def sliderEvolution(self,it):
        self.timeSimu = self.hSlider.value()
        self.labelSimuInfos.setText('Durée de simulation :'+repr(self.timeSimu)+' sec')

    def wheelEvent(self,event):
        angle_delta = 10.0*(event.delta()/120)
        rel_pt = self.getUiFromRealCoord(event.x(),event.y())
        
        for rbt in self.robots :
            if rbt.getPosition().distanceFrom(rel_pt)<10 :
                rbt.setAngle(rbt.getAngle() + np.deg2rad(angle_delta))
                self.lineEditReg.setText(rbt.__str__()+' '+rbt.getPosition().__str__())
                self.repaint()

    def mouseMoveEvent(self,it):
        rel_pt = self.getUiFromRealCoord(it.x(),it.y())

        table = Rect(100,150,200,300)

        for rbt in self.robots:
            if rbt.getPosition().distanceFrom(rel_pt)<10 :
                rbt.setPosition(rel_pt.x,rel_pt.y)
                self.lineEditReg.setText(rbt.__str__()+' '+rbt.getPosition().__str__())
                self.repaint()

        for elt in self.staticElts:
            if elt.getPosition().distanceFrom(rel_pt)<10 :
                elt.setPosition(rel_pt.x,rel_pt.y)

                if table.contains(elt.getPosition()):
                    if not self.brain.containsElement(elt.getID()):
                        self.brain.addElement(elt.getID(),elt)
                        self.refreshEltLabel()
                    else :
                        pass
                else :
                    if self.brain.containsElement(elt.getID()):
                        self.brain.removeElement(elt.getID())
                        self.refreshEltLabel()
                    else:
                        pass
                self.lineEditReg.setText(elt.__str__())
            self.repaint()

    def resizeEvent(self, it):
        self.fitToWindow()
Example #4
0
class Notification(QWidget):
    """Custom pop-up notification widget with fade-in and fade-out effect."""
    def __init__(self, parent, txt, anim_duration=500, life_span=2000):
        """

        Args:
            parent (QWidget): Parent widget
            txt (str): Text to display in notification
            anim_duration (int): Duration of the animation in msecs
            life_span (int): How long does the notification stays in place in msecs
        """
        super().__init__()
        self.setWindowFlags(Qt.Popup)
        self.setParent(parent)
        self._parent = parent
        self.label = QLabel(txt)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setWordWrap(True)
        self.label.setMargin(8)
        font = QFont()
        font.setBold(True)
        self.label.setFont(font)
        layout = QHBoxLayout()
        layout.addWidget(self.label)
        layout.setSizeConstraint(QLayout.SetFixedSize)
        layout.setContentsMargins(1, 0, 1, 0)
        self.setLayout(layout)
        self.adjustSize()
        # Move to the top right corner of the parent
        x = self._parent.size().width() - self.width() - 2
        self.move(x, 0)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setAttribute(Qt.WA_TranslucentBackground)
        ss = ("QWidget{background-color: rgba(255, 194, 179, 0.8);"
              "border-width: 2px;"
              "border-color: #ffebe6;"
              "border-style: groove; border-radius: 8px;}")
        self.setStyleSheet(ss)
        self.effect = QGraphicsOpacityEffect()
        self.setGraphicsEffect(self.effect)
        self.effect.setOpacity(0.0)
        self._opacity = 0.0
        self.timer = QTimer(self)
        self.timer.setInterval(life_span)
        self.timer.timeout.connect(self.start_self_destruction)
        # Fade in animation
        self.fade_in_anim = QPropertyAnimation(self, b"opacity")
        self.fade_in_anim.setDuration(anim_duration)
        self.fade_in_anim.setStartValue(0.0)
        self.fade_in_anim.setEndValue(1.0)
        self.fade_in_anim.valueChanged.connect(self.update_opacity)
        self.fade_in_anim.finished.connect(self.timer.start)
        # Fade out animation
        self.fade_out_anim = QPropertyAnimation(self, b"opacity")
        self.fade_out_anim.setDuration(anim_duration)
        self.fade_out_anim.setStartValue(1.0)
        self.fade_out_anim.setEndValue(0)
        self.fade_out_anim.valueChanged.connect(self.update_opacity)
        self.fade_out_anim.finished.connect(self.close)
        # Start fade in animation
        self.fade_in_anim.start(QPropertyAnimation.DeleteWhenStopped)

    def get_opacity(self):
        """opacity getter."""
        return self._opacity

    def set_opacity(self, op):
        """opacity setter."""
        self._opacity = op

    @Slot(float)
    def update_opacity(self, value):
        """Updates graphics effect opacity."""
        self.effect.setOpacity(value)

    def start_self_destruction(self):
        """Starts fade-out animation and closing of the notification."""
        self.fade_out_anim.start(QPropertyAnimation.DeleteWhenStopped)

    def enterEvent(self, e):
        super().enterEvent(e)
        self.start_self_destruction()
        self.setAttribute(Qt.WA_TransparentForMouseEvents)

    def remaining_time(self):
        if self.timer.isActive():
            return self.timer.remainingTime()
        if self.fade_out_anim.state() == QPropertyAnimation.Running:
            return 0
        return self.timer.interval()

    opacity = Property(float, get_opacity, set_opacity)
Example #5
0
class Notification(QFrame):
    """Custom pop-up notification widget with fade-in and fade-out effect."""
    def __init__(self,
                 parent,
                 txt,
                 anim_duration=500,
                 life_span=None,
                 word_wrap=True,
                 corner=Qt.TopRightCorner):
        """

        Args:
            parent (QWidget): Parent widget
            txt (str): Text to display in notification
            anim_duration (int): Duration of the animation in msecs
            life_span (int): How long does the notification stays in place in msecs
            word_wrap (bool)
            corner (Qt.Corner)
        """
        super().__init__()
        if life_span is None:
            word_count = len(txt.split(" "))
            mspw = 60000 / 140  # Assume people can read ~140 words per minute
            life_span = mspw * word_count
        self.setFocusPolicy(Qt.NoFocus)
        self.setWindowFlags(Qt.Popup)
        self.setParent(parent)
        self._parent = parent
        self._corner = corner
        self.label = QLabel(txt)
        self.label.setMaximumSize(parent.size())
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setWordWrap(word_wrap)
        self.label.setMargin(8)
        self.label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        font = QFont()
        font.setBold(True)
        self.label.setFont(font)
        layout = QHBoxLayout()
        layout.addWidget(self.label)
        layout.setSizeConstraint(QLayout.SetMinimumSize)
        layout.setContentsMargins(3, 3, 3, 3)
        self.setLayout(layout)
        self.adjustSize()
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setObjectName("Notification")
        self._background_color = "#e6ffc2b3"
        ss = ("QFrame#Notification{"
              f"background-color: {self._background_color};"
              "border-width: 2px;"
              "border-color: #ffebe6;"
              "border-style: groove; border-radius: 8px;}")
        self.setStyleSheet(ss)
        self.setAcceptDrops(True)
        self.effect = QGraphicsOpacityEffect()
        self.setGraphicsEffect(self.effect)
        self.effect.setOpacity(0.0)
        self._opacity = 0.0
        self.timer = QTimer(self)
        self.timer.setInterval(life_span)
        self.timer.timeout.connect(self.start_self_destruction)
        # Fade in animation
        self.fade_in_anim = QPropertyAnimation(self, b"opacity")
        self.fade_in_anim.setDuration(anim_duration)
        self.fade_in_anim.setStartValue(0.0)
        self.fade_in_anim.setEndValue(1.0)
        self.fade_in_anim.valueChanged.connect(self.update_opacity)
        self.fade_in_anim.finished.connect(self.timer.start)
        # Fade out animation
        self.fade_out_anim = QPropertyAnimation(self, b"opacity")
        self.fade_out_anim.setDuration(anim_duration)
        self.fade_out_anim.setStartValue(1.0)
        self.fade_out_anim.setEndValue(0)
        self.fade_out_anim.valueChanged.connect(self.update_opacity)
        self.fade_out_anim.finished.connect(self.close)
        # Start fade in animation
        self.fade_in_anim.start(QPropertyAnimation.DeleteWhenStopped)

    def show(self):
        # Move to the top right corner of the parent
        super().show()
        if self._corner in (Qt.TopRightCorner, Qt.BottomRightCorner):
            x = self._parent.size().width() - self.width() - 2
        else:
            x = self.pos().x()
        if self._corner in (Qt.BottomLeftCorner, Qt.BottomRightCorner):
            y = self._parent.size().height() - self.height() - 2
        else:
            y = self.pos().y()
        self.move(x, y)

    def get_opacity(self):
        """opacity getter."""
        return self._opacity

    def set_opacity(self, op):
        """opacity setter."""
        self._opacity = op

    @Slot(float)
    def update_opacity(self, value):
        """Updates graphics effect opacity."""
        self.effect.setOpacity(value)

    def start_self_destruction(self):
        """Starts fade-out animation and closing of the notification."""
        self.fade_out_anim.start(QPropertyAnimation.DeleteWhenStopped)
        self.setAttribute(Qt.WA_TransparentForMouseEvents)

    def enterEvent(self, e):
        super().enterEvent(e)
        self.start_self_destruction()

    def dragEnterEvent(self, e):
        super().dragEnterEvent(e)
        self.start_self_destruction()

    def remaining_time(self):
        if self.timer.isActive():
            return self.timer.remainingTime()
        if self.fade_out_anim.state() == QPropertyAnimation.Running:
            return 0
        return self.timer.interval()

    opacity = Property(float, get_opacity, set_opacity)