I want to have a central Widget with a grid layout containing multiple other widgets .
the problem is that the central widget is not showing on QMainWindow even after using setCentralWidget function .
here is the code that is not working, i can't find the error (edit: there was no exceptions raised, just the fact i couldn't see the widgets)
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QGridLayout
class Board(QWidget):
def __init__(self):
super().__init__()
Clock(QWidget):
def __init__(self):
super().__init__()
class MainGrid(QWidget):
def __init__(self):
super().__init__()
self.initGrid()
def initGrid(self):
grid= QGridLayout()
test = QLabel('test')
board = Board()
clock = Clock()
board.setStyleSheet('background-color: pink')
clock.setStyleSheet('background-color: blue')
grid.addWidget(board, 2, 1, 10, 10)
grid.addWidget(clock, 13, 4, 3, 3)
self.setLayout(grid)
class MainWin(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
centralGrid = MainGrid()
centralGrid.setStyleSheet('background-color: red')
centralGrid.sizeHint()
self.setCentralWidget(centralGrid)
self.setGeometry(200, 100, 1000, 600)
self.setWindowTitle('Simple Checkers')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MainWin()
sys.exit(app.exec_())
edit: thanks to scheff answer i think i found where i went wrong.
to visualize the widgets i changed their backgrounds using setStyleSheet function, on Qt Documentation :
Note: If you subclass a custom widget from QWidget, then in order to use the StyleSheets you need to provide a paintEvent to the custom widget :
as for the test label i used it for further testing but forgot to add it to the grid layout which added even more confusion .
Unfortunately, the OP claimed that
the problem is that the central widget is not showing on QMainWindow even after using setCentralWidget function .
without elaborating in detail.
I had a rough look onto the source and came to the conclusion that
widgets have been added to layout
layout is set to widget
the widget has been set to QMainWindow.
So far so fine.
Then I copied the complete source of OP to my local box.
To make it running I had to add/modify a variety of things:
All Qt imports were missing. I added
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
For sys.argv (in app = QApplication(sys.argv)), import sys is needed as well.
The widgets Board and Clock were missing.
#board = Board()
#clock = Clock()
clock = QLabel('Clock')
#board.setStyleSheet('background-color: pink')
The test = QLabel('test') wasn't added to the grid layout.
grid.addWidget(test, 2, 1, 10, 10)
After having fixed all of this, the (modified) source was this:
#!/usr/bin/python3
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainGrid(QWidget):
def __init__(self):
super().__init__()
self.initGrid()
def initGrid(self):
grid= QGridLayout()
test = QLabel('test')
#board = Board()
#clock = Clock()
clock = QLabel('Clock')
#board.setStyleSheet('background-color: pink')
test.setStyleSheet('background-color: pink')
clock.setStyleSheet('background-color: blue')
grid.addWidget(test, 2, 1, 10, 10)
grid.addWidget(clock, 13, 4, 3, 3)
self.setLayout(grid)
class MainWin(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
centralGrid = MainGrid()
centralGrid.setStyleSheet('background-color: red')
centralGrid.sizeHint()
self.setCentralWidget(centralGrid)
self.setGeometry(200, 100, 1000, 600)
self.setWindowTitle('Simple Checkers')
self.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
gui = MainWin()
sys.exit(app.exec_())
Note:
I added the "hut" in the first line
#!/usr/bin/python3
for my own convenience.
Then I ran it in cygwin64 (because I only had Windows 10 with cygwin at hand):
$ chmod a+x testQMainWindowCentralWidget.py
$ ./testQMainWindowCentralWidget.py
and got:
Now, the QMainWindow.setCentralWidget() works as expected.
I don't know which issues the OP actually ran in.
I'm not sure whether the exposed code of OP was the exact copy/paste and the missing details were the actual source of OP's problems.
When I tried to make it running I carefully considered the trace-backs I got in the first attempts and fixed the bugs step-by-step respectively until I got the above result.
Related
I am trying to overlay some graphics(QtGraphicsView) on top of video player(QVideoWidget). i have already tried setting QtGraphicsView subclass stylesheets to transparent and background brush and none is working.
#self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30, 3)))
#self.setStyleSheet("background-color:rgba(0,0,0,0)")
#self.setStyleSheet("background:rgba(0,0,0,0)")
self.setStyleSheet("background:transparent")
self.setStyleSheet("background-color:transparent")
self.setStyleSheet("background-color:rgba(30,30,30,3)")
self.setStyleSheet("background:rgba(30,30,30,3)")
Actual Intentions is to easily crop a video (visual way). All other work is done regarding capturing events , doing math etc. this image explains situation pretty well. . At this point it feels, surely i am doing it wrong way, there must be some easier way in QtMultiMedia Components to draw on top of them. Any Ideas really appreciated.
One possible solution is to use QGraphicsVideoItem instead of QVideoWidget and embed it in the QGraphicsView, then the other items can be made child of the QGraphicsVideoItem so that it is on top, besides the position of the new items will be related to the QGraphicsVideoItem.
import os
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self._scene = QtWidgets.QGraphicsScene(self)
self._gv = QtWidgets.QGraphicsView(self._scene)
self._videoitem = QtMultimediaWidgets.QGraphicsVideoItem()
self._scene.addItem(self._videoitem)
self._ellipse_item = QtWidgets.QGraphicsEllipseItem(QtCore.QRectF(50, 50, 40, 40), self._videoitem)
self._ellipse_item.setBrush(QtGui.QBrush(QtCore.Qt.green))
self._ellipse_item.setPen(QtGui.QPen(QtCore.Qt.red))
self._player = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
self._player.stateChanged.connect(self.on_stateChanged)
self._player.setVideoOutput(self._videoitem)
file = os.path.join(os.path.dirname(__file__), "small.mp4")
self._player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file)))
button = QtWidgets.QPushButton("Play")
button.clicked.connect(self._player.play)
self.resize(640, 480)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self._gv)
lay.addWidget(button)
#QtCore.pyqtSlot(QtMultimedia.QMediaPlayer.State)
def on_stateChanged(self, state):
if state == QtMultimedia.QMediaPlayer.PlayingState:
self._gv.fitInView(self._videoitem, QtCore.Qt.KeepAspectRatio)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I tried .setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) which works nicely but requires user to move mouse to the scroll bar and use it to experience the smooth scroll but the mouse wheel works the old way with jumpy scrolling, i wonder if there a way to make the scrolling behave the same when using the mouse wheel ?
You should use self.widget.verticalScrollBar().setSingleStep(step).
QTableWidget inherits QTableView, which inherits QAbstractItemView, which inherits QAbstractScrollArea, which has method verticalScrollBar(), which brings us to the QScrollBar Class that inherits QAbstractSlider, which finally has setSingleStep(step) method (maybe there is shorter path?).
Here's the complete code:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setWindowTitle("Scrolling QTableWidget smoothly BY MOUSE WHEEL")
label = QLabel("singleStep:")
self.spinbox = QSpinBox()
self.spinbox.setValue(1)
self.spinbox.setMinimum(1)
self.spinbox.setMaximum(200)
self.spinbox.valueChanged.connect(self.on_value_changed)
self.widget = QTableWidget(100, 5)
for i in range(100):
for j in range(5):
self.widget.setItem(i, j, QTableWidgetItem(str(i+j)))
self.widget.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
#self.widget.verticalScrollBar().setSingleStep(1)
self.set_single_step()
spinbox_layout = QHBoxLayout()
spinbox_layout.addStretch()
spinbox_layout.addWidget(label)
spinbox_layout.addWidget(self.spinbox)
layout = QVBoxLayout()
layout.addLayout(spinbox_layout)
layout.addWidget(self.widget)
self.setLayout(layout)
def on_value_changed(self, step):
self.set_single_step()
def set_single_step(self):
self.widget.verticalScrollBar().setSingleStep(self.spinbox.value())
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.resize(800, 600)
window.show()
sys.exit(app.exec())
You can increase/decrease step in spinbox to see how it behaves. I hope that is what you asked for.
I am looking for a solution to remove a terrible looking focus rectangle over a QSlider.
In addition that it looks terrible, it covers the ticks and is not entirely drawn.
This is an ancient issue; I remember stumbling into it many years ago. The workarounds
mentioned (e.g. Removing dotted border without setting NoFocus in Windows PyQt) do not work for a slider on my Linux system.
Setting outline to none or using the Qt.WA_MacShowFocusRect does not work on Linux.
slider.setStyleSheet('QSlider { outline:none; }')
slider.setAttribute(Qt.WA_MacShowFocusRect, 0)
The only thing that 'works' is setting the focus policy to Qt.NoFocus, which is not a real
solution, since one might want to work with keyboard.
slider.setFocusPolicy(Qt.NoFocus)
I am on Linux and the issue is on all available themes. Source code:
#!/usr/bin/python
from PyQt5.QtWidgets import (QWidget, QSlider, QHBoxLayout,
QLabel, QApplication)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout()
sld = QSlider(Qt.Horizontal, self)
# sld.setFocusPolicy(Qt.NoFocus)
# sld.setStyleSheet('QSlider { outline:none; padding: 0 2 0 2;}')
# sld.setAttribute(Qt.WA_MacShowFocusRect, 0)
sld.setTickPosition(QSlider.TicksAbove)
sld.setRange(0, 100)
sld.setPageStep(5)
sld.valueChanged.connect(self.changeValue)
self.label = QLabel("0", self)
self.label.setMinimumWidth(80)
hbox.addWidget(sld)
hbox.addSpacing(15)
hbox.addWidget(self.label)
self.setLayout(hbox)
self.setGeometry(300, 300, 350, 250)
self.setWindowTitle('QSlider')
self.show()
def changeValue(self, value):
self.label.setText(str(value))
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Screenshot:
I want to hide a QFrame from the start of the app to later on showing it as a warning message with a function.
But I can't find a solution. The frame keeps showing up without changing its start size.
I would also be happy with a solution in Qt Designer but I can't figure out how to do it either way.
self.frame_top_warning.setFixedHeight(0) seems to work but makes problems later on when animating that frame.
class SampleApp(ui_main.Ui_MainWindow, QtWidgets.QMainWindow):
def __init__(self):
super(SampleApp, self).__init__()
self.setupUi(self)
# Here I want to set the start size to 0 to, later on, animate it in.
self.frame_top_warning.resize(self.frame_top_warning.width(), 0)
One possibility could be to set the maximum height of the widget to 0 with self.frame_top_warning.setMaximumHeight(0) at the beginning to hide the QFrame. Then you can use QtCore.QParallelAnimationGroup to animate two properties at the same time minimumHeight and maximumHeight. In this way you are constraining the height of the widget to be what you want it to be.
I have put together a small example to show you what I mean
import sys
from PyQt5 import QtWidgets, QtCore
class Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
show_warning_button = QtWidgets.QPushButton('Show Warning')
layout.addWidget(show_warning_button)
show_warning_button.clicked.connect(self.showWarningLabel)
layout.addWidget(QtWidgets.QPushButton('Button'))
layout.addWidget(QtWidgets.QLabel('This is some text'))
self.frame_top_warning = QtWidgets.QFrame()
self.frame_top_warning.setStyleSheet('QFrame {background: red;}')
self.frame_top_warning.setMaximumHeight(0)
layout.addWidget(self.frame_top_warning)
min_height_animation = QtCore.QPropertyAnimation(self.frame_top_warning, b"minimumHeight")
min_height_animation.setDuration(600)
min_height_animation.setStartValue(0)
min_height_animation.setEndValue(400)
max_height_animation = QtCore.QPropertyAnimation(self.frame_top_warning, b"maximumHeight")
max_height_animation.setDuration(600)
max_height_animation.setStartValue(0)
max_height_animation.setEndValue(400)
self.animation = QtCore.QParallelAnimationGroup()
self.animation.addAnimation(min_height_animation)
self.animation.addAnimation(max_height_animation)
self.setLayout(layout)
self.resize(800, 600)
self.show()
def showWarningLabel(self):
self.animation.start()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Widget()
sys.exit(app.exec_())
Hope this helps =)
I am trying to develop a GUI to show my robot and obstacles around it. I created a class GUI and it has a member function showObstacle to show obstacle. Since I want to keep all the obstacles in place, I create new obstacle object in a vector to store all the objects.
I called this function in class's init function to test and it was successful.
But when I call this function in different class, it doesn't show the obstacles on the GUI window. In class TCP_Connection (where I get information about the robot), I created myGui class and I called showObstacle function.
After debugging, it turns out it creates the Obstacle Objects whenever it is called, but it doesn't display it on the window
#this is file 1, GUI Class
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
from PyQt5.QtCore import Qt
from pynput import keyboard
class App(QWidget):
def __init__(self, OtherWindow):
super().__init__()
self.setGeometry(100,100,440,800)
self.setWindowTitle("New GUI Interface Window")
self.currentState = 0
self.obstCounter = 0
self.d = []
#testing showObstacle
self.showObstacle(205, 305) #this works
self.showObstacle(210,315) #this also works
self.show()
def showObstacle(self, Obs_x, Obs_y):
self.obstImage = QtGui.QImage('obsta_edited.png')
self.pixmap_obst = QtGui.QPixmap(self.obstImage)
self.d.append("O{0}".format(self.obstCounter))
self.d[self.obstCounter] = QtWidgets.QLabel(self)
self.d[self.obstCounter].setPixmap(self.pixmap_obst)
self.d[self.obstCounter].move(Obs_x, Obs_y)
self.d[self.obstCounter].adjustSize()
self.obstCounter += 1
print(self.d)
print("Obstacle Run")
this is the calling class
from myGUI import App
from PyQt5.QtWidgets import QWidget,QLabel,QLineEdit, QHBoxLayout,QVBoxLayout,QMainWindow,QPushButton, QFrame, QDesktopWidget, QApplication
from PyQt5.Qt import QStackedLayout
import sys, random
class TCP_Communication:
def __init__(self):
super().__init__()
self.openWindow()
def openWindow(self):
self.window = QMainWindow()
self.myGui = App(self.window)
self.myGui.showObstacle(215, 320)
self.stacked_layout = QStackedLayout()
self.stacked_layout.addWidget(self.myGui)
def tcp_showObstacle(self,x,y):
self.myGui.showObstacle(x,y)
if __name__ == '__main__':
app = QApplication([])
tcp_com = TCP_Communication()
sys.exit(app.exec_())
Edited : attached the result ; Suppose to show 4 obstacles but only shows 2
Edited2 : Code is executable now
The problem is that the QLabels are not showing, the main widget when displayed at the beginning makes your children show up, but after that it does not make them visible, as it is in your case, so your task is to call their method show() directly:
def showObstacle(self, Obs_x, Obs_y):
pixmap_obst = QtGui.QPixmap('obsta_edited.png')
self.d.append("O{0}".format(self.obstCounter))
label = QtWidgets.QLabel(self)
label.setPixmap(pixmap_obst)
label.move(Obs_x, Obs_y)
label.adjustSize()
label.show() # <---show QLabel
self.d[self.obstCounter] = label
self.obstCounter += 1
print(self.d)
print("Obstacle Run")