Below is part of my code. I would like to set the label (marked by an arrow) to the value returned by the function executed in the main class. my code returns AttributeError: type object 'MainTab' has no attribute 'label'
class App(QMainWindow):
def __init__(self):
super().__init__()
self.setMinimumSize(800, 500)
self.tab_widget = TabWidget(self)
self.setCentralWidget(self.tab_widget)
self.show()
class TabWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
self.tabs = QTabWidget()
self.tabs.addTab(MainTab(), "Main")
self.tabs.resize(500, 200)
# Add tabs to widget
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
class MainTab(QDialog):
def __init__(self, parent=None):
super().__init__()
self.originalPalette = QApplication.palette()
self.mainLayout = QHBoxLayout()
self.buttonLayout = QVBoxLayout()
buttonMsg = QPushButton("test button")
self.buttonLayout.addWidget(buttonMsg)
self.mainLayout.addLayout(self.buttonLayout)
self.label = QLabel(self) # <--------------------------------------------
# pixmap = QPixmap('test.jpg')
# self.label.setPixmap(pixmap)
self.label.setStyleSheet("border: 1px solid black;")
self.label.setAlignment(Qt.AlignCenter)
self.mainLayout.addWidget(self.label)
self.setLayout(self.mainLayout)
def main():
check_requirements(exclude=('tensorboard', 'thop'))
im0 = yoloRun.run()
height, width, channel = im0.shape
bytesPerLine = 3 * width
qImg = QImage(im0.data, width, height, bytesPerLine, QImage.Format_RGB888)
pixmap = QPixmap(qImg)
MainTab.label.setPixmap(pixmap) # <--------- FROM HERE I WOULD LIKE TO PUT PIXMAP AS BACKGROUND
if __name__ == '__main__':
t1 = threading.Thread(target=main)
t1.start()
app = QApplication([])
app.setStyle('Fusion')
app.setApplicationName('KiTest')
ex = App()
app.exec()
How am I supposed to do this?
i'm loading an image on a label supposed after that
a mouse click event that draws dots on the label but dots are drawn on the main window ( behind the label )
GUI Image
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
uic.loadUi('MainWindow.ui', self)
self.setFixedSize(self.size())
self.show()
self.points = QtGui.QPolygon()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def mousePressEvent(self, e):
self.points << e.pos()
self.update()
def paintEvent(self, ev):
qp = QtGui.QPainter(self)
qp.setRenderHint(QtGui.QPainter.Antialiasing)
pen = QtGui.QPen(QtCore.Qt.red, 5)
brush = QtGui.QBrush(QtCore.Qt.red)
qp.setPen(pen)
qp.setBrush(brush)
for i in range(self.points.count()):
# qp.drawEllipse(self.points.point(i), 5, 5)
# or
qp.drawPoints(self.points)
def main():
app = QtWidgets.QApplication(sys.argv)
application = ApplicationWindow()
application.show()
sys.exit(app.exec_())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = ApplicationWindow()
sys.exit(app.exec_())
main()
You are calling QPainter(self) so the paint device is the MainWindow. Instead call QPainter on the QPixmap. Here is an example.
class Template(QWidget):
def __init__(self):
super().__init__()
self.lbl = QLabel()
self.pix = QPixmap('photo.jpeg')
grid = QGridLayout(self)
grid.addWidget(self.lbl, 0, 0)
self.points = QPolygon()
def mousePressEvent(self, event):
self.points << QPoint(event.x() - self.lbl.x(), event.y() - self.lbl.y())
def paintEvent(self, event):
qp = QPainter(self.pix)
qp.setRenderHint(QPainter.Antialiasing)
pen = QPen(Qt.red, 5)
brush = QBrush(Qt.red)
qp.setPen(pen)
qp.setBrush(brush)
qp.drawPoints(self.points)
self.lbl.setPixmap(self.pix)
However a better way is to use QImage in a custom widget that will act as a canvas.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Canvas(QWidget):
def __init__(self, photo, *args, **kwargs):
super().__init__(*args, **kwargs)
self.image = QImage(photo)
self.setFixedSize(self.image.width(), self.image.height())
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
qp = QPainter(self.image)
qp.setRenderHint(QPainter.Antialiasing)
qp.setPen(QPen(Qt.red, 5))
qp.setBrush(Qt.red)
qp.drawPoint(event.pos())
self.update()
def paintEvent(self, event):
qp = QPainter(self)
rect = event.rect()
qp.drawImage(rect, self.image, rect)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
w = QWidget()
self.setCentralWidget(w)
grid = QGridLayout(w)
grid.addWidget(Canvas('photo.jpeg'))
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MainWindow()
gui.show()
sys.exit(app.exec_())
Output:
In the program below, load the background image and paint it on it.
But, I got a problem.
In this program, when i use 'eraser' tool, the background image is erased too!
Actually, I just want to erase what i painted, except background image.
And then, I'd like to save only the painted ones(layer) as an image.
In this case, What should i do?
import sys
from PyQt5.QtCore import *
from PyQt5.QtCore import Qt
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox,
QPushButton, QVBoxLayout, QWidget, QSlider)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
class CWidget(QWidget):
def __init__(self):
super().__init__()
# 전체 폼 박스
formbox = QHBoxLayout()
self.setLayout(formbox)
# 좌, 우 레이아웃박스
left = QVBoxLayout()
right = QVBoxLayout()
# 그룹박스2
gb = QGroupBox('펜 설정')
left.addWidget(gb)
grid = QGridLayout()
gb.setLayout(grid)
label = QLabel('펜 색상')
grid.addWidget(label, 1, 0)
self.pencolor = QColor(0, 0, 0)
self.penbtn = QPushButton()
self.penbtn.setStyleSheet('background-color: rgb(0,0,0)')
self.penbtn.clicked.connect(self.showColorDlg)
grid.addWidget(self.penbtn, 1, 1)
label = QLabel('펜 굵기')
grid.addWidget(label, 2, 0)
self.slider = QSlider(Qt.Horizontal)
self.slider.setMinimum(3)
self.slider.setMaximum(21)
self.slider.setValue(5)
self.slider.setFocusPolicy(Qt.StrongFocus)
self.slider.setTickPosition(QSlider.TicksBothSides)
self.slider.setTickInterval(1)
self.slider.setSingleStep(1)
grid.addWidget(self.slider)
# 그룹박스4
gb = QGroupBox('Eraser')
left.addWidget(gb)
hbox = QHBoxLayout()
gb.setLayout(hbox)
self.checkbox = QCheckBox('Eraser')
self.checkbox.stateChanged.connect(self.checkClicked)
hbox.addWidget(self.checkbox)
left.addStretch(1)
self.view = CView(self)
right.addWidget(self.view)
formbox.addLayout(left)
formbox.addLayout(right)
formbox.setStretchFactor(left, 0)
formbox.setStretchFactor(right, 1)
self.setGeometry(100, 100, 800, 500)
def checkClicked(self, state):
pass
def createExampleGroup(self):
groupBox = QGroupBox("Slider Example")
slider = QSlider(Qt.Horizontal)
slider.setFocusPolicy(Qt.StrongFocus)
slider.setTickPosition(QSlider.TicksBothSides)
slider.setTickInterval(10)
slider.setSingleStep(1)
vbox = QVBoxLayout()
vbox.addWidget(slider)
vbox.addStretch(1)
groupBox.setLayout(vbox)
return groupBox
def showColorDlg(self):
color = QColorDialog.getColor()
sender = self.sender()
self.pencolor = color
self.penbtn.setStyleSheet('background-color: {}'.format(color.name()))
# QGraphicsView display QGraphicsScene
class CView(QGraphicsView):
def __init__(self, parent):
super().__init__(parent)
self.scene = QGraphicsScene()
self.setScene(self.scene)
self.items = []
self.start = QPointF()
self.end = QPointF()
self.backgroundImage = None
self.graphicsPixmapItem = None
self.setRenderHint(QPainter.HighQualityAntialiasing)
self.open()
def moveEvent(self, e):
rect = QRectF(self.rect())
rect.adjust(0, 0, -2, -2)
self.scene.setSceneRect(rect)
def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
# 시작점 저장
self.start = e.pos()
self.end = e.pos()
def mouseMoveEvent(self, e):
# e.buttons()는 정수형 값을 리턴, e.button()은 move시 Qt.Nobutton 리턴
if e.buttons() & Qt.LeftButton:
self.end = e.pos()
if self.parent().checkbox.isChecked():
pen = QPen(QColor(255, 255, 255), 10)
path = QPainterPath()
path.moveTo(self.start)
path.lineTo(self.end)
self.scene.addPath(path, pen)
self.start = e.pos()
return None
pen = QPen(self.parent().pencolor, self.parent().slider.value())
# Path 이용
path = QPainterPath()
path.moveTo(self.start)
path.lineTo(self.end)
self.scene.addPath(path, pen)
# 시작점을 다시 기존 끝점으로
self.start = e.pos()
def stretch(self, state):
self._set_image(state == 2)
def open(self):
fileName, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath(), filter='Images (*.png *.xpm *.jpg *jpeg)')
if fileName:
image = QImage(fileName)
if image.isNull():
QMessageBox.information(self, "Image Viewer",
"Cannot load %s." % fileName)
return
self.backgroundImage = fileName
self._set_image(False)
def _set_image(self, stretch: bool):
tempImg = QPixmap(self.backgroundImage)
if stretch:
tempImg = tempImg.scaled(self.scene.width(), self.scene.height())
if self.graphicsPixmapItem is not None:
self.scene.removeItem(self.graphicsPixmapItem)
self.graphicsPixmapItem = QGraphicsPixmapItem(tempImg)
self.scene.addItem(self.graphicsPixmapItem)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = CWidget()
w.show()
sys.exit(app.exec_())
You can create another transparent item where you draw and that is on the QGraphicsPixmapItem. For painting it is only necessary to draw on a transparent QPixmap that is in the transparent item, and for the deletion we use the composition mode QPainter::CompositionMode_Clear as I indicate in this answer.
Considering the above the solution is:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class LayerItem(QtWidgets.QGraphicsRectItem):
DrawState, EraseState = range(2)
def __init__(self, parent=None):
super().__init__(parent)
self.current_state = LayerItem.DrawState
self.setPen(QtGui.QPen(QtCore.Qt.NoPen))
self.m_line_eraser = QtCore.QLineF()
self.m_line_draw = QtCore.QLineF()
self.m_pixmap = QtGui.QPixmap()
def reset(self):
r = self.parentItem().pixmap().rect()
self.setRect(QtCore.QRectF(r))
self.m_pixmap = QtGui.QPixmap(r.size())
self.m_pixmap.fill(QtCore.Qt.transparent)
def paint(self, painter, option, widget=None):
super().paint(painter, option, widget)
painter.save()
painter.drawPixmap(QtCore.QPoint(), self.m_pixmap)
painter.restore()
def mousePressEvent(self, event):
if self.current_state == LayerItem.EraseState:
self._clear(event.pos().toPoint())
elif self.current_state == LayerItem.DrawState:
self.m_line_draw.setP1(event.pos())
self.m_line_draw.setP2(event.pos())
super().mousePressEvent(event)
event.accept()
def mouseMoveEvent(self, event):
if self.current_state == LayerItem.EraseState:
self._clear(event.pos().toPoint())
elif self.current_state == LayerItem.DrawState:
self.m_line_draw.setP2(event.pos())
self._draw_line(
self.m_line_draw, QtGui.QPen(self.pen_color, self.pen_thickness)
)
self.m_line_draw.setP1(event.pos())
super().mouseMoveEvent(event)
def _draw_line(self, line, pen):
painter = QtGui.QPainter(self.m_pixmap)
painter.setPen(pen)
painter.drawLine(line)
painter.end()
self.update()
def _clear(self, pos):
painter = QtGui.QPainter(self.m_pixmap)
r = QtCore.QRect(QtCore.QPoint(), 10 * QtCore.QSize())
r.moveCenter(pos)
painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
painter.eraseRect(r)
painter.end()
self.update()
#property
def pen_thickness(self):
return self._pen_thickness
#pen_thickness.setter
def pen_thickness(self, thickness):
self._pen_thickness = thickness
#property
def pen_color(self):
return self._pen_color
#pen_color.setter
def pen_color(self, color):
self._pen_color = color
#property
def current_state(self):
return self._current_state
#current_state.setter
def current_state(self, state):
self._current_state = state
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setScene(QtWidgets.QGraphicsScene(self))
self.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
self.setAlignment(QtCore.Qt.AlignCenter)
self.background_item = QtWidgets.QGraphicsPixmapItem()
self.foreground_item = LayerItem(self.background_item)
self.scene().addItem(self.background_item)
def set_image(self, image):
self.scene().setSceneRect(
QtCore.QRectF(QtCore.QPointF(), QtCore.QSizeF(image.size()))
)
self.background_item.setPixmap(image)
self.foreground_item.reset()
self.fitInView(self.background_item, QtCore.Qt.KeepAspectRatio)
self.centerOn(self.background_item)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
menu = self.menuBar().addMenu(self.tr("File"))
open_action = menu.addAction(self.tr("Open image..."))
open_action.triggered.connect(self.open_image)
pen_group = QtWidgets.QGroupBox(self.tr("Pen settings"))
eraser_group = QtWidgets.QGroupBox(self.tr("Eraser"))
self.pen_button = QtWidgets.QPushButton(clicked=self.showColorDlg)
color = QtGui.QColor(0, 0, 0)
self.pen_button.setStyleSheet(
"background-color: {}".format(color.name())
)
self.pen_slider = QtWidgets.QSlider(
QtCore.Qt.Horizontal,
minimum=3,
maximum=21,
value=5,
focusPolicy=QtCore.Qt.StrongFocus,
tickPosition=QtWidgets.QSlider.TicksBothSides,
tickInterval=1,
singleStep=1,
valueChanged=self.onThicknessChanged,
)
self.eraser_checkbox = QtWidgets.QCheckBox(
self.tr("Eraser"), stateChanged=self.onStateChanged
)
self.view = GraphicsView()
self.view.foreground_item.pen_thickness = self.pen_slider.value()
self.view.foreground_item.pen_color = color
# layouts
pen_lay = QtWidgets.QFormLayout(pen_group)
pen_lay.addRow(self.tr("Pen color"), self.pen_button)
pen_lay.addRow(self.tr("Pen thickness"), self.pen_slider)
eraser_lay = QtWidgets.QVBoxLayout(eraser_group)
eraser_lay.addWidget(self.eraser_checkbox)
vlay = QtWidgets.QVBoxLayout()
vlay.addWidget(pen_group)
vlay.addWidget(eraser_group)
vlay.addStretch()
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QHBoxLayout(central_widget)
lay.addLayout(vlay, stretch=0)
lay.addWidget(self.view, stretch=1)
self.resize(640, 480)
#QtCore.pyqtSlot(int)
def onStateChanged(self, state):
self.view.foreground_item.current_state = (
LayerItem.EraseState
if state == QtCore.Qt.Checked
else LayerItem.DrawState
)
#QtCore.pyqtSlot(int)
def onThicknessChanged(self, value):
self.view.foreground_item.pen_thickness = value
#QtCore.pyqtSlot()
def showColorDlg(self):
color = QtWidgets.QColorDialog.getColor(
self.view.foreground_item.pen_color, self
)
self.view.foreground_item.pen_color = color
self.pen_button.setStyleSheet(
"background-color: {}".format(color.name())
)
def open_image(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
"Open File",
QtCore.QDir.currentPath(),
filter="Images (*.png *.xpm *.jpg *jpeg)",
)
if filename:
pixmap = QtGui.QPixmap(filename)
if pixmap.isNull():
QtWidgets.QMessageBox.information(
self, "Image Viewer", "Cannot load %s." % filename
)
return
self.view.set_image(pixmap)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I can't figure it. I want to be able to swap out QWidgets according to events like button clicks but I am missing something and I haven't been able to search out any example along the lines of the code below. What I want to do is click one of the top buttons and get the widget below to switch between either the QCalendar or QtextEdit. Where am I going wrong?
Thanks!
#!/usr/bin/python
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout(self)
widget = QtGui.QCalendarWidget()
button1 = QtGui.QPushButton('Calendar', self)
button1.setCheckable(True)
button1.clicked[bool].connect(self.setWidget)
button2 = QtGui.QPushButton('TextEdit', self)
button2.setCheckable(True)
button2.clicked[bool].connect(self.setWidget)
splitter1 = QtGui.QSplitter(QtCore.Qt.Vertical)
splitter1.addWidget(button1)
splitter1.addWidget(button2)
splitter1.addWidget(widget)
hbox.addWidget(splitter1)
self.setLayout(hbox)
self.setGeometry(0, 0, 600, 600)
self.setWindowTitle('Switching QWidgets')
self.show()
def setWidget(self, pressed):
source = self.sender()
val1 = QtGui.QCalendarWidget()
val2 = QtGui.QTextEdit()
if source.text() == "Calendar":
widget = val1
QtGui.QWidget.update(Example.hbox)
elif source.text() == "TextEdit":
widget = val2
QtGui.QWidget.update(Example.hbox)
else:
widget = val1
QtGui.QWidget.update(Example.hbox)
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You have to use QStackedWidget, where you update the indexes.
class Example(QtGui.QWidget):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout(self)
self.stacked = QtGui.QStackedWidget(self)
self.stacked.addWidget(QtGui.QCalendarWidget())
self.stacked.addWidget(QtGui.QTextEdit())
splitter1 = QtGui.QSplitter(QtCore.Qt.Vertical)
for text in ["Calendar", "TextEdit"]:
btn = QtGui.QPushButton(text, self)
btn.clicked.connect(self.setWidget)
splitter1.addWidget(btn)
splitter1.addWidget(self.stacked)
hbox.addWidget(splitter1)
self.setLayout(hbox)
self.setGeometry(0, 0, 600, 600)
self.setWindowTitle('Switching QWidgets')
self.show()
def setWidget(self):
source = self.sender()
if source.text() == "Calendar":
self.stacked.setCurrentIndex(0)
elif source.text() == "TextEdit":
self.stacked.setCurrentIndex(1)
If you change your code to keep a reference to the calendar and textedit widgets, then add them both to the splitter in init... You can just use the show and hide functions in your setWidget().
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.hbox = QtGui.QHBoxLayout(self)
self.widget = QtGui.QCalendarWidget()
self.widget2 = QtGui.QTextEdit()
button1 = QtGui.QPushButton('Calendar', self)
button1.setCheckable(True)
button1.clicked[bool].connect(self.setWidget)
button2 = QtGui.QPushButton('TextEdit', self)
button2.setCheckable(True)
button2.clicked[bool].connect(self.setWidget)
splitter1 = QtGui.QSplitter(QtCore.Qt.Vertical)
splitter1.addWidget(button1)
splitter1.addWidget(button2)
splitter1.addWidget(self.widget)
splitter1.addWidget(self.widget2)
self.widget2.hide()
self.hbox.addWidget(splitter1)
self.setLayout(self.hbox)
self.setGeometry(59, 59, 600, 600)
self.setWindowTitle('Switching QWidgets')
self.show()
def setWidget(self, pressed):
source = self.sender()
if source.text() == "Calendar":
self.widget.show()
self.widget2.hide()
elif source.text() == "TextEdit":
self.widget.hide()
self.widget2.show()
I have a QT widget that has two list boxes that are wrapped in QFrames along with a label and placed side-by-side on QWidget. I can move between them using the tab key but I would like to move between them using the left and right arrow keys. What is the best way to do this?
My QWidget and ListWidgets look like the following
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
hbox = QHBoxLayout()
hbox.addWidget(Left())
hbox.addWidget(Right())
self.setLayout(hbox)
self.show()
class Left(QFrame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
header = QLabel()
header.setText('Left')
l = QListWidget()
items = ['Item %s' % (i + 1) for i in range(10)]
l.addItems(items)
vbox = QVBoxLayout()
vbox.addWidget(header)
vbox.addWidget(l)
self.setLayout(vbox)
class Right(QFrame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
header = QLabel()
header.setText('Right')
l = QListWidget()
items = ['Item %s' % (i + 1) for i in range(10)]
l.addItems(items)
vbox = QVBoxLayout()
vbox.addWidget(header)
vbox.addWidget(l)
self.setLayout(vbox)
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You can install an event-filter on the list-widgets and then use the focusNextPrevChild method of their parent widget to move the focus:
class MainWindow(QWidget):
...
def init_ui(self):
hbox = QHBoxLayout()
self.left = Left()
self.left.installEventFilter(self)
hbox.addWidget(self.left)
self.right = Right()
self.right.installEventFilter(self)
hbox.addWidget(self.right)
self.setLayout(hbox)
self.show()
def eventFilter(self, source, event):
if (event.type() == QEvent.KeyPress and
(event.key() == Qt.Key_Left or event.key() == Qt.Key_Right) and
event.modifiers() == Qt.NoModifier and
(source is self.left or source is self.right)):
self.focusNextPrevChild(event.key() == Qt.Key_Right)
return True
return super(MainWindow, self).eventFilter(source, event)