Spacing issue with QMainwindow with centralwidget that has gridlayout - python

I've coded a little Snake example.
I've created a QMainWindow with a centralWidget that has a gridlayout.
I can't seem to get the spacing right. I thought I can do it with setContentMargins(), so I placed it everywhere I can but the space still remains. I'm probably overseeing something. I think it has to do with my grid. But I don't see the problem. Thoughts?
Here is a picture:
https://imgur.com/tHbf6tV
Here's my code:
import sys
from PyQt5 import QtWidgets as qw
from PyQt5 import QtGui as qg
from PyQt5 import QtCore as qc
class Snake(qw.QMainWindow):
def __init__(self, parent=None):
super(Snake, self).__init__(parent)
self.setContentsMargins(0,0,0,0)
self.test()
self.show()
def test(self):
self.centralwidget = qw.QWidget()
self.setCentralWidget(self.centralwidget)
self.centralwidget.setStyleSheet("background-color: green")
self.gridLayout = qw.QGridLayout()
self.gridLayout.setSpacing(0)
# self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.vLayout = qw.QVBoxLayout(self.centralwidget)
self.hLayout = qw.QHBoxLayout()
self.hLayout.setContentsMargins(0, 0, 0, 0)
self.vLayout.setContentsMargins(0, 0, 0, 0)
# center the grid with stretch on both sides
self.hLayout.addLayout(self.gridLayout)
self.hLayout.addStretch(0)
self.vLayout.addLayout(self.hLayout)
# push grid to the top of the window
self.vLayout.addStretch(0)
self.buttons = []
for i in range(24):
l = []
for j in range(24):
b = qw.QPushButton()
b.setFixedSize(20, 20)
b.setStyleSheet("background-color: black")
l.append(b)
self.gridLayout.addWidget(b, i, j)
self.gridLayout.setColumnMinimumWidth(j, 20)
self.buttons.append(l)
self.gridLayout.setRowMinimumHeight(i, 16)
self.buttons[5][5].setStyleSheet("background-color: green")
self.buttons[4][5].setStyleSheet("background-color: green")
self.buttons[3][5].setStyleSheet("background-color: green")
if __name__ == '__main__':
app = qw.QApplication(sys.argv)
Menu = Snake()
sys.exit(app.exec_())

i think i've solved it.The clue was to recreate my grid without QHBoxLayout and QVBoxLayout.
Here's my code:
import sys
from PyQt5 import QtWidgets as qw
from PyQt5 import QtGui as qg
from PyQt5 import QtCore as qc
class Snake(qw.QMainWindow):
def __init__(self, parent=None):
super(Snake, self).__init__(parent)
self.setContentsMargins(0, 0, 0, 0)
self.setFixedSize(20*24,20*24)
self.test()
self.show()
def test(self):
self.centralwidget = qw.QWidget()
self.setCentralWidget(self.centralwidget)
self.centralwidget.setStyleSheet("background-color: green")
self.centralwidget.setContentsMargins(0, 0, 0, 0)
self.gridLayout = qw.QGridLayout()
self.gridLayout.setSpacing(0)
self.gridLayout.setContentsMargins(0,0,0,0)
self.buttons = []
for i in range(24):
for j in range(24):
b = qw.QLabel("Knopf" + str(i) + "/" + str(j))
b.setFixedSize(20, 20)
b.setStyleSheet("background-color: black")
self.gridLayout.addWidget(b, i, j)
self.gridLayout.setColumnMinimumWidth(j, 20)
self.gridLayout.setRowMinimumHeight(i, 16)
self.centralwidget.setLayout(self.gridLayout)
if __name__ == '__main__':
app = qw.QApplication(sys.argv)
Menu = Snake()
sys.exit(app.exec_())

Related

PyQT5 QScrollBar within QDockWidget

I have a lot of buttons, lineedits and other widgets in a window. Because they are too much for one window, I like to wrap them in a QScrollArea. All that should be in a QDockWindow. My problem: the docker window is visible and works, but none of the buttons and no scroll bar is visible.
I'm using python 3.6 / PyQT5
My code is:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from pprint import pprint
class DockWindow(QMainWindow):
def __init__(self,parent=None):
super(DockWindow, self).__init__(parent)
layout=QHBoxLayout()
self.docker = QDockWidget()
self.dockerWidget = QWidget()
self.scroll = QScrollArea()
self.widget = QWidget()
self.grid = QGridLayout()
for i in range(1,50):
for j in range(1,5):
object = QPushButton("btn" + str(i) + ";" + str(j))
self.grid.addWidget(object,i,j)
self.widget.setLayout(self.grid)
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
self.scrollLayout = QHBoxLayout(self.scroll)
self.dockerWidget.setLayout(self.scrollLayout)
self.scroll.setGeometry(10, 10, 100, 100)
self.docker.setWidget(self.dockerWidget)
self.setCentralWidget(QTextEdit())
self.addDockWidget(Qt.RightDockWidgetArea,self.docker)
self.setGeometry(600, 100, 1000, 900)
self.setWindowTitle('Scroll Area Demonstration')
self.show()
if __name__ == '__main__':
app=QApplication(sys.argv)
demo=DockWindow()
demo.show()
sys.exit(app.exec_())
I saw
PyQt QScrollArea within QScrollArea
but this is in PyQt4 and when I tried to convert it to PyQt5, nothing was visible again.
I also saw
Qscrollbar in PyQt5 nothing is shown
but I do have a size in my code for the QScrollArea.
So how can I make the buttons visible?
Yep, that works.
Code is now:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from pprint import pprint
class DockWindow(QMainWindow):
def __init__(self,parent=None):
super(DockWindow, self).__init__(parent)
layout=QHBoxLayout()
self.docker = QDockWidget()
self.scroll = QScrollArea()
self.widget = QWidget()
self.grid = QGridLayout()
for i in range(1,50):
for j in range(1,5):
object = QPushButton("btn" + str(i) + ";" + str(j))
self.grid.addWidget(object,i,j)
self.widget.setLayout(self.grid)
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
self.docker.setWidget(self.scroll)
self.scroll.setGeometry(10, 10, 100, 100)
self.setCentralWidget(QTextEdit())
self.addDockWidget(Qt.RightDockWidgetArea,self.docker)
self.setGeometry(600, 100, 1000, 900)
self.setWindowTitle('Scroll Area Demonstration')
self.show()
if __name__ == '__main__':
app=QApplication(sys.argv)
demo=DockWindow()
demo.show()
sys.exit(app.exec_())

Rotate background PYQT5

I'm trying to rotate a background image with a button and trim the image over the window, but it doesn't work and I don't know why it is not working.
But when I hit the button my image just fades away...
And here is my code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QGridLayout, QPushButton
from PyQt5.QtGui import QPixmap
class myApplication(QtWidgets.QWidget):
def __init__(self, parent=None):
super(myApplication, self).__init__(parent)
self.img = QtGui.QImage()
pixmap = QtGui.QPixmap("ola.png")
self.label = QLabel(self)
self.label.setMinimumSize(600, 600)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setPixmap(pixmap)
grid = QGridLayout()
button = QPushButton('Rotate 15 degrees')
button.clicked.connect(self.rotate_pixmap)
grid.addWidget(self.label, 0, 0)
grid.addWidget(button, 1, 0)
self.setLayout(grid)
self.rotation = 0
def rotate_pixmap(self):
pixmap = QtGui.QPixmap(self.img)
self.rotation += 15
transform = QtGui.QTransform().rotate(self.rotation)
pixmap = pixmap.transformed(transform, QtCore.Qt.SmoothTransformation)
self.label.setPixmap(pixmap)
if __name__ == '__main__':
app = QApplication([])
w = myApplication()
w.show()
sys.exit(app.exec_())
"self.img" is an empty QImage and you are rotating that element. The idea is to rotate the QPixmap:
class myApplication(QtWidgets.QWidget):
def __init__(self, parent=None):
super(myApplication, self).__init__(parent)
self.rotation = 0
self.pixmap = QtGui.QPixmap("ola.png")
self.label = QLabel()
self.label.setMinimumSize(600, 600)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setPixmap(self.pixmap)
button = QPushButton("Rotate 15 degrees")
button.clicked.connect(self.rotate_pixmap)
grid = QGridLayout(self)
grid.addWidget(self.label, 0, 0)
grid.addWidget(button, 1, 0)
def rotate_pixmap(self):
pixmap = self.pixmap.copy()
self.rotation += 15
transform = QtGui.QTransform().rotate(self.rotation)
pixmap = pixmap.transformed(transform, QtCore.Qt.SmoothTransformation)
self.label.setPixmap(pixmap)

Zoom in and out in widget

How can I make a zoom effect with key input on a widget? The widget is inside a scroll area and there are some drawings made with QPainter who change with user input. The zoom would affect the length of the scrolling bar, the closer you are, the smaller the bar becomes. The zoom at minimum level should make the scroll bar as big as the widget area, so all the content in the widget could be visualized.
MRE:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QPen, QColor
import sys
class Diedrico(QWidget):
def __init__(self, parent):
super().__init__(parent)
def paintEvent(self, event):
qp = QPainter(self)
qp.setPen(QPen(QColor(Qt.black), 5))
qp.drawRect(500, 500, 1000, 1000)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.resize(520, 520)
self.widget_central = QtWidgets.QWidget(self)
scrol = QtWidgets.QScrollArea(self.widget_central)
scrol.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scrol.setGeometry(QtCore.QRect(10, 10, 500, 500))
scrol.setWidgetResizable(False)
contenido = QtWidgets.QWidget()
contenido.setGeometry(QtCore.QRect(0, 0, 2000, 2000))
scrol.setWidget(contenido)
self.Diedrico = Diedrico(contenido)
self.Diedrico.setGeometry(QtCore.QRect(0, 0, 2000, 2000))
self.setCentralWidget(self.widget_central)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
Why do you want to reinvent the wheel? Instead of wanting to implement the logic of the scaling feature, use the classes that already do it. In this case, a good option is to use QGraphicsView with QGraphicsScene:
Note: The shortcut standard Zoom In and Zoom Out are associated with Ctrl + + and Ctrl + -, respectively.
from PyQt5 import QtCore, QtGui, QtWidgets
class Diedrico(QtWidgets.QWidget):
def paintEvent(self, event):
qp = QtGui.QPainter(self)
pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black), 5)
qp.setPen(pen)
qp.drawRect(500, 500, 1000, 1000)
class UiVentana(QtWidgets.QMainWindow):
factor = 1.5
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self._scene = QtWidgets.QGraphicsScene(self)
self._view = QtWidgets.QGraphicsView(self._scene)
self._diedrico = Diedrico()
self._diedrico.setFixedSize(2000, 2000)
self._scene.addWidget(self._diedrico)
self.setCentralWidget(self._view)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomIn),
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_in,
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomOut),
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_out,
)
#QtCore.pyqtSlot()
def zoom_in(self):
scale_tr = QtGui.QTransform()
scale_tr.scale(UiVentana.factor, UiVentana.factor)
tr = self._view.transform() * scale_tr
self._view.setTransform(tr)
#QtCore.pyqtSlot()
def zoom_out(self):
scale_tr = QtGui.QTransform()
scale_tr.scale(UiVentana.factor, UiVentana.factor)
scale_inverted, invertible = scale_tr.inverted()
if invertible:
tr = self._view.transform() * scale_inverted
self._view.setTransform(tr)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
Update:
If you want to use + and - for ZoomIn and ZoomOut, respectively, then just change the shortcuts to:
QtWidgets.QShortcut(
QtGui.QKeySequence(QtCore.Qt.Key_Plus), # <---
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_in,
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtCore.Qt.Key_Minus), # <---
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_out,
)

Graphics in PyQtGraph not updating after zoom

I have the following PyQtGraph program, which makes a red square "move" when moving a slider:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QSlider
from pyqtgraph import (
mkBrush,
mkPen,
GraphicsObject,
QtGui,
PlotWidget,
)
class SquareItem(GraphicsObject):
def __init__(self):
super().__init__()
self.position_picture = QtGui.QPicture()
def paint(self, p, *args):
p.drawPicture(0, 0, self.position_picture)
def boundingRect(self):
return QtCore.QRectF(-5, -5, 20, 10)
def update_position(self, x):
self.position_picture = QtGui.QPicture()
painter = QtGui.QPainter(self.position_picture)
painter.scale(1, -1)
painter.setBrush(mkBrush('r'))
painter.setPen(mkPen(None))
painter.drawRect(QtCore.QRectF(x, 0, 1, 1))
painter.end()
self.informViewBoundsChanged()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Micromouse maze simulator')
self.resize(600, 600)
frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout(frame)
self.graphics = PlotWidget()
self.graphics.setAspectLocked()
self.item = SquareItem()
self.graphics.addItem(self.item)
self.slider = QSlider(QtCore.Qt.Horizontal)
self.slider.setSingleStep(1)
self.slider.setPageStep(10)
self.slider.setRange(0, 10)
self.slider.setTickPosition(QSlider.TicksAbove)
self.slider.valueChanged.connect(self.slider_value_changed)
self.slider.setValue(1)
layout.addWidget(self.graphics)
layout.addWidget(self.slider)
self.setCentralWidget(frame)
def slider_value_changed(self, value):
self.item.update_position(value)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
Everything seems to work fine, but if I zoom in/out and then move the slider again the square position is no longer updated (i.e.: the square is not re-drawn).
How can I fix that?
Updates
I am using a square to simplify the problem. In reality, I do not only change position, but I can also draw different shapes, so using setPos() is not really an option.
You should not update the painting if you want to change position, you should only use setPos(). the paint() function takes boundingRect() as a reference so when moving the graph you are moving it in that coordinate system instead of the coordinate system of PlotWidget.
class SquareItem(GraphicsObject):
def paint(self, p, *args):
p.setBrush(mkBrush('r'))
p.setPen(mkPen(None))
p.drawRect(self.boundingRect())
def boundingRect(self):
return QtCore.QRectF(0, 0, 1, 1)
def update_position(self, x):
self.setPos(x, 0)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Micromouse maze simulator')
self.resize(600, 600)
frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout(frame)
self.graphics = PlotWidget()
self.graphics.setAspectLocked()
self.item = SquareItem()
self.graphics.addItem(self.item)
self.graphics.setRange(rect=QtCore.QRectF(-10, -10, 20, 20))
self.slider = QSlider(QtCore.Qt.Horizontal)
self.slider.setSingleStep(1)
self.slider.setPageStep(10)
self.slider.setRange(0, 10)
self.slider.setTickPosition(QSlider.TicksAbove)
self.slider.valueChanged.connect(self.slider_value_changed)
self.slider.setValue(1)
layout.addWidget(self.graphics)
layout.addWidget(self.slider)
self.setCentralWidget(frame)
def slider_value_changed(self, value):
self.item.update_position(value)
If you are not going to use signals it is advisable to use objects that inherit from QGraphicsItem instead of QGraphicsObject, for example you could use QGraphicsRectItem:
import sys
from PyQt5 import QtCore, QtWidgets
from pyqtgraph import (
mkBrush,
mkPen,
GraphicsObject,
QtGui,
PlotWidget,
)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Micromouse maze simulator')
self.resize(600, 600)
frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout(frame)
self.graphics = PlotWidget()
self.graphics.setAspectLocked()
self.item = QtWidgets.QGraphicsRectItem(0, 0, 1, 1)
self.item.setBrush(mkBrush('r'))
self.item.setPen(mkPen(None))
self.graphics.addItem(self.item)
self.graphics.setRange(rect=QtCore.QRectF(-10, -10, 20, 20))
self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.slider.setSingleStep(1)
self.slider.setPageStep(10)
self.slider.setRange(0, 10)
self.slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
self.slider.valueChanged.connect(self.slider_value_changed)
self.slider.setValue(1)
layout.addWidget(self.graphics)
layout.addWidget(self.slider)
self.setCentralWidget(frame)
def slider_value_changed(self, value):
self.item.setPos(value, 0)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
update:
If you want to redraw you should call update():
import sys
from PyQt5 import QtCore, QtWidgets
from pyqtgraph import (
mkBrush,
mkPen,
GraphicsObject,
QtGui,
PlotWidget,
)
class SquareItem(GraphicsObject):
colors = ['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w', 'FF0', 'AA0', '0AA']
def __init__(self):
super().__init__()
self.mColor = SquareItem.colors[0]
def paint(self, p, *args):
p.setBrush(mkBrush(self.mColor))
p.setPen(mkPen(None))
p.drawRect(self.boundingRect())
def boundingRect(self):
return QtCore.QRectF(0, 0, 1, 1)
def update_draw(self, x):
self.mColor = SquareItem.colors[x]
self.update()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Micromouse maze simulator')
self.resize(600, 600)
frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout(frame)
self.graphics = PlotWidget()
self.graphics.setAspectLocked()
self.item = SquareItem()
self.graphics.addItem(self.item)
self.graphics.setRange(rect=QtCore.QRectF(-10, -10, 20, 20))
self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.slider.setSingleStep(1)
self.slider.setPageStep(10)
self.slider.setRange(0, 10)
self.slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
self.slider.valueChanged.connect(self.item.update_draw)
self.slider.setValue(1)
layout.addWidget(self.graphics)
layout.addWidget(self.slider)
self.setCentralWidget(frame)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())

PyQT dynamically, automatically changing color properties of table widget

Since I am new in PyQt I am trying to solve a problem with dynamically change color of table cell - help is more than welcome.
Function testFunction should change tableWidget color if for loop find 1 in array or 0. Is it possible to set this properties? Box should automatically change color on every 2 seconds without any additional action. Check the code below...
import sys, os
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setWindowTitle("Hello!")
self.tableWidget = QtGui.QTableWidget()
self.tableItem = QtGui.QTableWidgetItem()
self.tableWidget.resize(400,250)
self.tableWidget.setRowCount(1)
self.tableWidget.setColumnCount(1)
self.tableWidget.setItem(0,0, QtGui.QTableWidgetItem("START TOOL"))
self.tableWidget.item(0,0).setBackground(QtGui.QColor(100,100,150))
realLayout = QtGui.QVBoxLayout()
realLayout.addWidget(tableWidget)
self.setLayout(realLayout)
self.testFunction()
def testFunction(self) :
a = [1,0,1,1,1,1,1,0,0,0,0,0,1]
for i in range(0,len(a)) :
if a[i] == 1 :
self.tableWidget.item(0,0).setBackground(QtGui.QColor(100,100,100))
else :
self.tableWidget.item(0,0).setBackground(QtGui.QColor(0,255,0))
time.sleep(2)
def main():
app = QtGui.QApplication(sys.argv)
GUI = MainWindow()
GUI.show()
sys.exit(app.exec_())
if __name__ == '__main__' :
main()
You must change realLayout.addWidget(tableWidget) to realLayout.addWidget(self.tableWidget) and and you should not use "sleep", must create a timer(QTimer)
import sys, os
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QTimer
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setWindowTitle("Hello!")
self.tableWidget = QtGui.QTableWidget()
self.tableItem = QtGui.QTableWidgetItem()
self.tableWidget.resize(400, 250)
self.tableWidget.setRowCount(1)
self.tableWidget.setColumnCount(1)
self.tableWidget.setItem(0, 0, QtGui.QTableWidgetItem("START TOOL"))
self.tableWidget.item(0, 0).setBackground(QtGui.QColor(100, 100, 150))
realLayout = QtGui.QVBoxLayout()
realLayout.addWidget(self.tableWidget)
self.setLayout(realLayout)
self.counter = 0
self.a = [1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1]
self.testFunction()
timer = QTimer(self)
timer.timeout.connect(self.testFunction)
timer.start(2*1000)
def testFunction(self):
self.counter += 1
self.counter %= len(self.a)
if self.a[self.counter]:
self.tableWidget.item(0, 0).setBackground(QtGui.QColor(100, 100, 100))
else:
self.tableWidget.item(0, 0).setBackground(QtGui.QColor(0, 255, 0))
def main():
app = QtGui.QApplication(sys.argv)
GUI = MainWindow()
GUI.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories