0

I am trying to make an image viewer in PyQt5 with Python. So far I have the functionality I want at the moment, this is a matter of styling the GUI how I would like it.

enter image description here

It looks almost how I want it. I struggled to get that transparentish background, but managed to by setting windowflags to FramelesswindowHint. Unfortunately, now I don't have a menubar to close the application if I want to and drag the window around. I figure I will have to craft my own bar if I want to style it in a way that suits my background, which is fine, adding an X button to close the window shouldn't be too much of an issue.

My question is, how do I make it so I can drag the window around? I could make a rectangle widget and position it at the top with my buttons, but how do I get the whole window to move if the user clicks and drags it? I know similar questions are around, but I'm not sure how to make click and drag functions yet on objects in PyQt5 and it seems they use different layout strategies so I'm finding them confusing.

Here is my code:

from PyQt5 import QtCore, QtGui, QtWidgets
import os


class Ui_MainWindow(object):
    def __init__(self):
        cwd = r"C:\Users\chees\PycharmProjects\PYQT5GUI\images"
        self.imagelist = []
        self.imagepaths = []
        self.imagecount = 0

        for a, b, c in os.walk(cwd):
            self.imagelist = c
            for i in c:
                self.imagepaths.append(a + "\\" + i)
            print(self.imagepaths)


    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setWindowModality(QtCore.Qt.NonModal)
        MainWindow.resize(661, 580)

        MainWindow.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        MainWindow.setWindowFlags(QtCore.Qt.FramelessWindowHint)


        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.b = QtWidgets.QWidget(self.centralwidget)
        self.b.setGeometry(QtCore.QRect(0, 0, MainWindow.width(), MainWindow.height()))
        self.b.setStyleSheet("background-color: rgba(0, 0, 0, 70%); border:1px; border-radius:25px;")




        self.imageviewer = QtWidgets.QLabel(self.centralwidget)
        self.imageviewer.setGeometry(QtCore.QRect(170, 60, 351, 321))



        self.imageviewer.setText("")
        self.imageviewer.setPixmap(QtGui.QPixmap("images/IMG1.jpg"))
        self.imageviewer.setScaledContents(True)
        self.imageviewer.setObjectName("imageviewer")

        self.backward_button = QtWidgets.QPushButton(self.centralwidget)
        self.backward_button.setGeometry(QtCore.QRect(50, 471, 120, 50))
        self.backward_button.setObjectName("backward_button")
        self.backward_button.clicked.connect(self.backward)

        self.forward_button = QtWidgets.QPushButton(self.centralwidget)
        self.forward_button.setGeometry(QtCore.QRect(510, 470, 120, 50))
        self.forward_button.setObjectName("forward_button")
        self.forward_button.clicked.connect(self.forward)

        MainWindow.setCentralWidget(self.centralwidget)


        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        self.statusbar.setStyleSheet("background-color: rgba(255,255,255, 40%)")

        MainWindow.setStatusBar(self.statusbar)


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

        self.changesize()



    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.backward_button.setText(_translate("MainWindow", "<<"))
        self.forward_button.setText(_translate("MainWindow", ">>"))

    def changesize(self):
        self.imageviewer.adjustSize()
        if self.imageviewer.width() < 500:
            MainWindow.resize(500, self.imageviewer.height() + 200)
            self.b.resize(500, self.imageviewer.height() + 200)

            self.imageviewer.move((500-self.imageviewer.width())//2, 55)
        else:
            MainWindow.resize((self.imageviewer.width() + self.imageviewer.width() // 8), (self.imageviewer.height() + 200))
            self.b.resize((self.imageviewer.width() + self.imageviewer.width() // 8),
                              (self.imageviewer.height() + 200))
            self.imageviewer.move(self.imageviewer.width() // 16, 55)
        self.forward_button.move(MainWindow.width()-150,MainWindow.height()-80)
        self.backward_button.move(30, MainWindow.height() - 80)
        self.statusbar.showMessage(self.imagepaths[self.imagecount])


    def flipmode(self):
        self.imageviewer.setPixmap(QtGui.QPixmap("images\\" + self.imagelist[self.imagecount]))
        self.changesize()
        print(self.imageviewer.height(), self.imageviewer.width())

    def forward(self):
        self.imagecount += 1
        if self.imagecount == len(self.imagelist):
            self.imagecount = 0
        self.flipmode()


    def backward(self):
        self.imagecount -= 1
        if self.imagecount < 0:
            self.imagecount = len(self.imagelist)-1
        self.flipmode()



if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()

    ui.setupUi(MainWindow)
    MainWindow.show()

    print(ui.imagelist)
    sys.exit(app.exec_())
Pokebab
  • 183
  • 8

1 Answers1

1

Do not modify the code generated by Qt Designer but create another class that inherits from the appropriate widget and use the initial class to fill it.

One of the ways to drag a window might look like this:

    self._old_pos = None

def mousePressEvent(self, event):
    if event.button() == QtCore.Qt.LeftButton:
        self._old_pos = event.pos()

def mouseReleaseEvent(self, event):
    if event.button() == QtCore.Qt.LeftButton:
        self._old_pos = None

def mouseMoveEvent(self, event):
    if not self._old_pos:
        return
    delta = event.pos() - self._old_pos
    self.move(self.pos() + delta)

from PyQt5 import QtCore, QtGui, QtWidgets
import os


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setWindowModality(QtCore.Qt.NonModal)
        MainWindow.resize(661, 580)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.b = QtWidgets.QWidget(self.centralwidget)
        self.b.setGeometry(QtCore.QRect(0, 0, MainWindow.width(), MainWindow.height()))
        self.b.setStyleSheet("background-color: rgba(0, 0, 0, 70%); border:1px; border-radius:25px;")

        self.imageviewer = QtWidgets.QLabel(self.centralwidget)
        self.imageviewer.setGeometry(QtCore.QRect(170, 60, 351, 321))
        self.imageviewer.setText("")
        self.imageviewer.setPixmap(QtGui.QPixmap("images/IMG1.jpg"))
        self.imageviewer.setScaledContents(True)
        self.imageviewer.setObjectName("imageviewer")
        self.backward_button = QtWidgets.QPushButton(self.centralwidget)
        self.backward_button.setGeometry(QtCore.QRect(50, 471, 120, 50))
        self.backward_button.setObjectName("backward_button")
        self.backward_button.clicked.connect(self.backward)
        self.forward_button = QtWidgets.QPushButton(self.centralwidget)
        self.forward_button.setGeometry(QtCore.QRect(510, 470, 120, 50))
        self.forward_button.setObjectName("forward_button")
        self.forward_button.clicked.connect(self.forward)

        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        self.statusbar.setStyleSheet("background-color: rgba(255,255,255, 40%)")
        MainWindow.setStatusBar(self.statusbar)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.backward_button.setText(_translate("MainWindow", "<<"))
        self.forward_button.setText(_translate("MainWindow", ">>"))


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()

        self.setupUi(self)  
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)

#        cwd = r"C:\Users\chees\PycharmProjects\PYQT5GUI\images"
        cwd = "D:/_Qt/__Qt/images"

        self.imagelist = []
        self.imagepaths = []
        self.imagecount = 0

        for a, b, c in os.walk(cwd):
            self.imagelist = c
            for i in c:
                self.imagepaths.append(a + "\\" + i)
            #print(self.imagepaths)

        self.changesize()

#++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

        self._old_pos = None

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self._old_pos = event.pos()

    def mouseReleaseEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self._old_pos = None

    def mouseMoveEvent(self, event):
        if not self._old_pos:
            return
        delta = event.pos() - self._old_pos
        self.move(self.pos() + delta)
# ++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        


    def changesize(self):
        self.imageviewer.adjustSize()
        if self.imageviewer.width() < 500:
            self.resize(500, self.imageviewer.height() + 200)
            self.b.resize(500, self.imageviewer.height() + 200)

            self.imageviewer.move((500-self.imageviewer.width())//2, 55)
        else:
            self.resize((self.imageviewer.width() + self.imageviewer.width() // 8), (self.imageviewer.height() + 200))
            self.b.resize((self.imageviewer.width() + self.imageviewer.width() // 8),
                              (self.imageviewer.height() + 200))
            self.imageviewer.move(self.imageviewer.width() // 16, 55)
        self.forward_button.move(self.width()-150, self.height()-80)
        self.backward_button.move(30, self.height() - 80)
        self.statusbar.showMessage(self.imagepaths[self.imagecount])


    def flipmode(self):
        self.imageviewer.setPixmap(QtGui.QPixmap("images\\" + self.imagelist[self.imagecount]))
        self.changesize()
#        print(self.imageviewer.height(), self.imageviewer.width())

    def forward(self):
        self.imagecount += 1
        if self.imagecount == len(self.imagelist):
            self.imagecount = 0
        self.flipmode()


    def backward(self):
        self.imagecount -= 1
        if self.imagecount < 0:
            self.imagecount = len(self.imagelist)-1
        self.flipmode()        


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
#    MainWindow = QtWidgets.QMainWindow()
#    ui = Ui_MainWindow()
#    ui.setupUi(MainWindow)
#    MainWindow.show()
    w = MainWindow()
    w.show()
#    print(ui.imagelist)
    sys.exit(app.exec_())

enter image description here

S. Nick
  • 12,879
  • 8
  • 25
  • 33
  • Thanks for the reply. The code you provided works well. Just a few questions. 1. Why is it necessary that we create another class and not just use the ui_mainwindow class? 2. I understand what the mouse event functions are doing in their definitions, but where are these functions being called and arguments being passed? – Pokebab May 21 '20 at 23:12
  • 1
    @Pokebab I recommend you read: https://www.riverbankcomputing.com/static/Docs/PyQt5/designer.html and https://stackoverflow.com/questions/46544780/qtdesigner-changes-will-be-lost-after-redesign-user-interface – S. Nick May 21 '20 at 23:36
  • 1
    @Pokebab and https://doc.qt.io/qt-5/qwidget.html#mousePressEvent ... – S. Nick May 21 '20 at 23:41
  • Much appreciated, thank you. – Pokebab May 22 '20 at 00:09