Search code examples
pythonpyqtpyqt5qt-designerqlabel

QLabel act strange on QMainWindow


I want to create a gui, with a Qlabel where to display an image, after this I want to make a selection (rectangle) on image and save the selection as another image in my computer. But I have a problem:

First, when I create the gui (withou QtDesigner) everything works fine, but when I create the gui on QtDesigner, and I want to display my new image and KpeWindow class on my box label but it is displayed in top left corner, why?

gui.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>GUI</class>
 <widget class="QMainWindow" name="GUI">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>770</width>
    <height>527</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>GUI</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QPushButton" name="clear_btn">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>220</y>
      <width>71</width>
      <height>32</height>
     </rect>
    </property>
    <property name="text">
     <string>clear</string>
    </property>
   </widget>
   <widget class="QPushButton" name="save_btn">
    <property name="geometry">
     <rect>
      <x>70</x>
      <y>220</y>
      <width>69</width>
      <height>32</height>
     </rect>
    </property>
    <property name="text">
     <string>save</string>
    </property>
   </widget>
   <widget class="QPushButton" name="load_btn">
    <property name="geometry">
     <rect>
      <x>130</x>
      <y>220</y>
      <width>67</width>
      <height>32</height>
     </rect>
    </property>
    <property name="text">
     <string>load</string>
    </property>
   </widget>
   <widget class="QLabel" name="grafic">
    <property name="geometry">
     <rect>
      <x>218</x>
      <y>12</y>
      <width>531</width>
      <height>411</height>
     </rect>
    </property>
    <property name="frameShape">
     <enum>QFrame::Box</enum>
    </property>
    <property name="text">
     <string/>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>770</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

First part of the code:

import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5.uic import loadUi

class KpeWindow(QtWidgets.QLabel):
    def __init__(self, parent=None):
        QtWidgets.QLabel.__init__(self,parent)
        self.selection = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)


    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            position = QtCore.QPoint(event.pos())
            if self.selection.isVisible():
                # visible selection
                print(self.upper_left.manhattanLength())
                if (self.upper_left - position).manhattanLength() < 20:
                    # close to upper left corner, drag it
                    self.mode = "drag_upper_left"
                elif (self.lower_right - position).manhattanLength() < 20:
                    # close to lower right corner, drag it
                    self.mode = "drag_lower_right"
                else:
                    # clicked somewhere else, hide selection
                    self.selection.hide()
            else:
                # no visible selection, start new selection
                self.upper_left = position
                self.lower_right = position
                self.mode = "drag_lower_right"
                self.selection.show()

    def mouseMoveEvent(self, event):
        '''
            Mouse moved. If selection is visible, drag it according to drag mode.
        '''

        if self.selection.isVisible():
            # visible selection
            if self.mode is "drag_lower_right":
                self.lower_right = QtCore.QPoint(event.pos())
                # print(str(self.lower_right))
            elif self.mode is "drag_upper_left":
                self.upper_left = QtCore.QPoint(event.pos())
                # print(str(self.upper_left))
            # update geometry
            self.selection.setGeometry(QtCore.QRect(self.upper_left, self.lower_right).normalized())

MainWindow without QtDesigner

enter image description here

class MainWindow(QtWidgets.QWidget):
        def __init__(self):
            super(MainWindow, self).__init__()
            QtWidgets.QMainWindow.__init__(self)
            layout = QtWidgets.QVBoxLayout(self)
            label = KpeWindow(self)
            pixmap = QPixmap("image.JPG")
            label.setPixmap(pixmap)
            layout.addWidget(label)
            label.setFocusPolicy(Qt.StrongFocus)
            self.setFocusProxy(label)
            label.setFocus(True)
            self.setLayout(layout)
            self.setFixedSize(640, 480)
            self.show()

    if __name__ == '__main__':
        app = QApplication(sys.argv)
        mainWin = MainWindow()
        mainWin.show()
        sys.exit(app.exec_())

MainWindow with QtDesigner:

enter image description here

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        loadUi('GUI/gui.ui', self)
        #self.grafic <-- this is the box label
        self.grafic = KpeWindow(self)
        pixmap = QPixmap("image.JPG")
        self.grafic.setPixmap(pixmap)
        self.grafic.setFocusPolicy(Qt.StrongFocus)
        self.setFocusProxy(self.grafic)
        self.grafic.setFocus(True)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())

Solution

  • The cause of the error is that you are assuming that if you create a widget with the same name of the QLabel it will replace it, but that is false, you are creating another QLabel whose parent is the window so it will be located in the topLeft.

    So there are 2 solutions:

    1. Promote the KpeWindow to be the grafic, there is an example in this answer.

    2. Set the KpeWindow in the QLabel through a layout.

    from PyQt5 import QtCore, QtGui, QtWidgets, uic
    
    class KpeWindow(QtWidgets.QLabel):
        # ...
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            uic.loadUi('GUI/gui.ui', self)
            kpe_window = KpeWindow()
            pixmap = QtGui.QPixmap("image.JPG")
            kpe_window.setPixmap(pixmap)
            kpe_window.setFocusPolicy(QtCore.Qt.StrongFocus)
            self.setFocusProxy(kpe_window)
            kpe_window.setFocus(True)
            lay = QtWidgets.QVBoxLayout(self.grafic)
            lay.addWidget(kpe_window)
    
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        mainWin = MainWindow()
        mainWin.show()
        sys.exit(app.exec_())
    

    The second solution is the simplest.