I'm having trouble setting up PyQt on my own. My idea its to creat a music-player with a song title and album cover. I have had success in creating my own window and adding the album cover. But I can't add the label in the right position. I want the song title to be at the top-center of the window, like the image below:
I have tried a lot of ways, but had no luck.
import sys
from PyQt5.QtGui import QIcon, QPixmap, QFontDatabase, QFont
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QWidget, QGridLayout, QDialog
from PyQt5.QtCore import Qt, QRect
# Subclass QMainWindow to customise your application's main window
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.title = 'PyQt5 simple window - pythonspot.com'
self.left = 10
self.top = 10
self.width = 480
self.height = 320
self.initUI()
self.setWindowTitle("My Awesome App")
def add_font(self):
# Load the font:
font_db = QFontDatabase()
font_id = font_db.addApplicationFont('American Captain.ttf')
families = font_db.applicationFontFamilies(font_id)
ttf_font = QFont(' '.join(families), 15)
return ttf_font
def initUI(self):
ttf_font = self.add_font()
w = QWidget()
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.show()
album_cover = QLabel(self)
album_pic = QPixmap('resized_image.jpg')
album_cover.setPixmap(album_pic)
album_cover.setAlignment(Qt.AlignCenter)
self.setCentralWidget(album_cover)
art_alb = QLabel(self)
art_alb.setFont(ttf_font)
art_alb.setText("michael buble - christmas")
art_alb.setGeometry(self.x, self.y, self.x, self.y)
art_alb.setAlignment(Qt.AlignTop | Qt.AlignCenter )
art_alb.show()
self.show()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
You should use a central widget with a layout to control how the child widgets are sized and positioned in the main window. Here is a re-write of your initUI method that should do what you want:
class MainWindow(QMainWindow):
...
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
widget = QWidget()
layout = QGridLayout(widget)
art_alb = QLabel(self)
ttf_font = self.add_font()
art_alb.setFont(ttf_font)
art_alb.setText("michael buble - christmas")
layout.addWidget(art_alb, 0, 0, Qt.AlignTop | Qt.AlignHCenter)
album_cover = QLabel(self)
album_pic = QPixmap('image.jpg')
album_cover.setPixmap(album_pic)
layout.addWidget(album_cover, 1, 0, Qt.AlignHCenter)
layout.setRowStretch(1, 1)
self.setCentralWidget(widget)
Note that there's no need to keep calling show(), since this is all handled automatically by the layout. For more information, see the Layout Management article in the Qt docs.
Related
I've a problem with pyqt5. I've create a windows with a scene that has a background image, re-implementing drawBackground. I've also a button that allow me to add a line in a position on the scene. The problem is that if i click the button to draw the line, then this line is drawn in a separate scene with it's own background, instead of into the scene i have. Seems like it create a new scene to draw the line on. Here is my code:
import sys
from PyQt5 import QtGui
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import (QMainWindow, QGraphicsView, QPushButton,
QHBoxLayout, QVBoxLayout, QWidget, QApplication, QGraphicsScene)
class GraphicsScene(QGraphicsScene):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._image = QImage()
#property
def image(self):
return self._image
#image.setter
def image(self, img):
self._image = img
self.update()
def drawBackground(self, painter, rect):
if self.image.isNull():
super().drawBackground(painter, rect)
else:
painter.drawImage(rect, self._image)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.title = "parcelDeliveryIta";
self.top = 100
self.left = 100
self.width = 1500
self.height = 900
self.initUI()
def initUI(self):
self.scene = GraphicsScene(self)
self.scene._image = QImage('Italy.png')
view = QGraphicsView(self.scene, self)
self.scene.setSceneRect(0, 0, view.width(), view.height())
addLine = QPushButton('AddLine')
addLine.clicked.connect(self.addLine)
hbox = QHBoxLayout(self)
hbox.addWidget(view)
vbox = QVBoxLayout(self)
vbox.addWidget(addLine)
hbox.addLayout(vbox)
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.setFixedSize(self.width, self.height)
self.setLayout(hbox)
self.show()
def addLine(self):
self.scene.addLine(0, 0, 100, 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
and this is the result when clicking the button:
As it is possible to see, the line is drawn with its own backgroung, that is image I've setted as background to the scene (the image above is cropped to show better the line)
Thanks for helping.
Explanation:
It seems that my previous solution is not suitable for your case since as the docs points out:
void QGraphicsScene::drawBackground(QPainter *painter, const
QRectF &rect) Draws the background of the scene using painter,
before any items and the foreground are drawn. Reimplement this
function to provide a custom background for the scene.
All painting is done in scene coordinates. The rect parameter is the
exposed rectangle.
If all you want is to define a color, texture, or gradient for the
background, you can call setBackgroundBrush() instead.
See also drawForeground() and drawItems().
(emphasis mine)
That paint will also be used to paint the base of the items and therefore caused that behavior.
Solution:
So you will have to resort to another solution, for example to use a QGraphicsPixmapItem as a base and readjust the size of the window with that information:
import sys
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import (
QGraphicsView,
QPushButton,
QHBoxLayout,
QVBoxLayout,
QWidget,
QApplication,
QGraphicsScene,
)
class GraphicsView(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
scene = QGraphicsScene(self)
self.setScene(scene)
self._pixmap_item = self.scene().addPixmap(QPixmap())
self._pixmap_item.setZValue(-1)
#property
def pixmap(self):
return self._pixmap_item.pixmap()
#pixmap.setter
def pixmap(self, pixmap):
self._pixmap_item.setPixmap(pixmap)
self.scene().setSceneRect(self._pixmap_item.boundingRect())
def resizeEvent(self, event):
if not self._pixmap_item.pixmap().isNull():
self.fitInView(self._pixmap_item)
super().resizeEvent(event)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.title = "parcelDeliveryIta"
self.top = 100
self.left = 100
self.width = 1500
self.height = 900
self.initUI()
def initUI(self):
self.view = GraphicsView(self)
self.view.pixmap = QPixmap("Italy.png")
addLine = QPushButton("AddLine")
addLine.clicked.connect(self.addLine)
hbox = QHBoxLayout(self)
hbox.addWidget(self.view)
vbox = QVBoxLayout()
vbox.addWidget(addLine)
hbox.addLayout(vbox)
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.setFixedSize(self.width, self.height)
self.show()
def addLine(self):
self.view.scene().addLine(0, 0, 100, 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
I want the first window that opens up to contain 4 push buttons responding to the numbers: 2,3,4,5. Once I have pressed one of these buttons I want the window to close and a new window to open with a label (just so I know it works) and also to use that number as a separate variable as it will select a sheet that I am reading from an excel file.
At the moment I can create the first window with the boxes, and when I press one the new window opens - but I cannot get the selection I made to come across.
Here is as far as I have got so far...
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QHBoxLayout, QGroupBox, QDialog, QVBoxLayout, QMainWindow
# select floor window
class select_floor_window(QDialog):
def __init__(self, parent=None):
super().__init__()
self.title = 'Select floor'
self.left = 10
self.top = 10
self.width = 320
self.height = 100
self.selection_ui()
def selection_ui(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.createHorizontalLayout()
windowLayout = QVBoxLayout()
windowLayout.addWidget(self.horizontalGroupBox)
self.setLayout(windowLayout)
self.show()
def createHorizontalLayout(self):
# box layout
self.horizontalGroupBox = QGroupBox("Which floor are you on?")
layout = QHBoxLayout()
# floor buttons
floor_2_button = QPushButton("2", self)
floor_2_button.clicked.connect(self.on_click2)
layout.addWidget(floor_2_button)
floor_3_button = QPushButton("3", self)
floor_3_button.clicked.connect(self.on_click3)
layout.addWidget(floor_3_button)
floor_4_button = QPushButton("4", self)
floor_4_button.clicked.connect(self.on_click4)
layout.addWidget(floor_4_button)
floor_5_button = QPushButton("5", self)
floor_5_button.clicked.connect(self.on_click5)
layout.addWidget(floor_5_button)
self.horizontalGroupBox.setLayout(layout)
# close this window and load main window
def on_click2(self):
self.floorchoice = 2
self.show_main = main_Window()
self.show_main.show()
self.hide()
print("2")
return floorchoice
def on_click3(self):
self.floorchoice = 3
self.show_main = main_Window()
self.show_main.show()
self.hide()
print("3")
return floorchoice
def on_click4(self):
self.floorchoice = 4
self.show_main = main_Window()
self.show_main.show()
self.hide()
print("4")
return floorchoice
def on_click5(self):
self.floorchoice = 5
self.show_main = main_Window()
self.show_main.show()
self.hide()
print("5")
return floorchoice
# create main window
class main_Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.title = "2ndwindow"
self.top = 100
self.left = 100
self.width = 680
self.height = 500
show_floor_button = QLabel(floorchoice, self)
show_floor_button.move(100,100)
# Close app button
close_app_button = QPushButton("Exit", self)
close_app_button.move(400,400)
close_app_button.setToolTip("Close application")
close_app_button.clicked.connect(self.CloseApp)
self.InitWindow()
# showing window
def InitWindow(self):
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = select_floor_window()
sys.exit(app.exec_())
Anyway that I try to set up the code, it always says that "name 'floorchoice' is not defined" in the second window, and cannot get it to display the result from the first class.
The functions that are invoked do not return anything, plus self.floorchoice is different floorchoice, the first is an attribute of the class and the other is a local variable.
What you have to do is that the class through the constructor or another method get that information.
In the next one I add one more argument to the invocation using functool.partial, then I pass that argument to the constructor of the other window so that I get that information.
I think your main error is caused by not taking into account that each variable, instance, object, etc. has a scope within the application.
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QHBoxLayout, QGroupBox, QDialog, QVBoxLayout, QMainWindow
from functools import partial
# select floor window
class select_floor_window(QDialog):
def __init__(self, parent=None):
super().__init__()
self.title = "Select floor"
self.left = 10
self.top = 10
self.width = 320
self.height = 100
self.selection_ui()
def selection_ui(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.createHorizontalLayout()
windowLayout = QVBoxLayout(self)
windowLayout.addWidget(self.horizontalGroupBox)
def createHorizontalLayout(self):
# box layout
self.horizontalGroupBox = QGroupBox("Which floor are you on?")
layout = QHBoxLayout()
self.horizontalGroupBox.setLayout(layout)
# floor buttons
for option in (2, 3, 4, 5):
button = QPushButton(str(option))
layout.addWidget(button)
wrapper = partial(self.on_click, option)
button.clicked.connect(wrapper)
# close this window and load main window
def on_click(self, option):
self.show_main = main_Window(option)
self.show_main.show()
self.hide()
# create main window
class main_Window(QMainWindow):
def __init__(self, option, parent=None):
super().__init__(parent)
self.title = "2ndwindow"
self.top = 100
self.left = 100
self.width = 680
self.height = 500
show_floor_button = QLabel(str(option), self)
show_floor_button.move(100, 100)
# Close app button
close_app_button = QPushButton("Exit", self)
close_app_button.move(400, 400)
close_app_button.setToolTip("Close application")
close_app_button.clicked.connect(self.close)
self.InitWindow()
# showing window
def InitWindow(self):
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = select_floor_window()
ex.show()
sys.exit(app.exec_())
I am working on a PyQt5 project, which needs a folder viewer by PyQt5 QTreeView. In order to put more stuff, I try to change the size of the tree view but in vain. Here is the code from Pythonspot:
import sys
from PyQt5.QtWidgets import QApplication, QFileSystemModel, QTreeView, QWidget, QVBoxLayout
from PyQt5.QtGui import QIcon
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 file system view - pythonspot.com'
self.left = 10
self.top = 10
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.model = QFileSystemModel()
self.model.setRootPath('')
self.tree = QTreeView()
self.tree.setModel(self.model)
self.tree.setAnimated(False)
self.tree.setIndentation(20)
self.tree.setSortingEnabled(True)
self.tree.setWindowTitle("Dir View")
self.tree.resize(640, 200)
windowLayout = QVBoxLayout()
windowLayout.addWidget(self.tree)
self.setLayout(windowLayout)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
I change the tree view by
self.tree.resize(640, 200)
Why it does not function?
A layout is used to establish the position and size of the widget you are using, so in your case even if you use resize the size will not be changed, instead you should set a fixed size so the layout can not change the size of the QTreeView.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class App(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 file system view - pythonspot.com'
self.left, self.top, self.width, self.height = 10, 10, 640, 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.model = QtWidgets.QFileSystemModel()
self.model.setRootPath('')
self.tree = QtWidgets.QTreeView()
self.tree.setModel(self.model)
self.tree.setAnimated(False)
self.tree.setIndentation(20)
self.tree.setSortingEnabled(True)
self.tree.setWindowTitle("Dir View")
self.tree.setFixedSize(640, 200)
windowLayout = QtWidgets.QVBoxLayout(self)
windowLayout.addWidget(self.tree, alignment=QtCore.Qt.AlignTop)
self.show()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Can someone help me figure out how to combine layouts?
Taking offset from the guides from: [https://pythonspot.com/en/pyqt5/]
I would rather not use Designer as the layout is going to be a part of several tabs that is determined based on the amount of tests and data sets from a specified data folder.
For example, I would like to switch out the Blue button in:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QGroupBox, QDialog, QVBoxLayout
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class App(QDialog):
def __init__(self):
super().__init__()
self.title = 'PyQt5 layout - pythonspot.com'
self.left = 10
self.top = 10
self.width = 320
self.height = 100
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.createHorizontalLayout()
windowLayout = QVBoxLayout()
windowLayout.addWidget(self.horizontalGroupBox)
self.setLayout(windowLayout)
self.show()
def createHorizontalLayout(self):
self.horizontalGroupBox = QGroupBox("What is your favorite color?")
layout = QHBoxLayout()
buttonBlue = QPushButton('Blue', self)
buttonBlue.clicked.connect(self.on_click)
layout.addWidget(buttonBlue)
buttonRed = QPushButton('Red', self)
buttonRed.clicked.connect(self.on_click)
layout.addWidget(buttonRed)
buttonGreen = QPushButton('Green', self)
buttonGreen.clicked.connect(self.on_click)
layout.addWidget(buttonGreen)
self.horizontalGroupBox.setLayout(layout)
#pyqtSlot()
def on_click(self):
print('PyQt5 button click')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
with the grid layout from:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QGroupBox, QDialog, QVBoxLayout, QGridLayout
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class App(QDialog):
def __init__(self):
super().__init__()
self.title = 'PyQt5 layout - pythonspot.com'
self.left = 10
self.top = 10
self.width = 320
self.height = 100
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.createGridLayout()
windowLayout = QVBoxLayout()
windowLayout.addWidget(self.horizontalGroupBox)
self.setLayout(windowLayout)
self.show()
def createGridLayout(self):
self.horizontalGroupBox = QGroupBox("Grid")
layout = QGridLayout()
layout.setColumnStretch(1, 4)
layout.setColumnStretch(2, 4)
layout.addWidget(QPushButton('1'),0,0)
layout.addWidget(QPushButton('2'),0,1)
layout.addWidget(QPushButton('3'),0,2)
layout.addWidget(QPushButton('4'),1,0)
layout.addWidget(QPushButton('5'),1,1)
layout.addWidget(QPushButton('6'),1,2)
layout.addWidget(QPushButton('7'),2,0)
layout.addWidget(QPushButton('8'),2,1)
layout.addWidget(QPushButton('9'),2,2)
self.horizontalGroupBox.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
You can add a layout as an element of another layout in a similar fashion to adding a widget by using addLayout()
layout = QtWidgets.QHBoxLayout()
sublayout = QtWidgets.QGridLayout()
layout.addLayout(sublayout)
I'm trying to create a custom PyQt5 button, but am running across problems displaying it in a QMainWindow object. Here's the code I'm trying:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QMainWindow
class PicButton(QAbstractButton):
def __init__(self, pixmap, parent=None):
super(PicButton, self).__init__(parent)
self.pixmap = pixmap
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPixmap(event.rect(), self.pixmap)
def sizeHint(self):
return self.pixmap.size()
class App(QMainWindow):
def __init__(self):
super().__init__()
self.left = 0
self.top = 0
self.width = 800
self.height = 800
self.initUI()
def initUI(self):
self.setGeometry(self.left, self.top, self.width, self.height)
self.setAutoFillBackground(True)
p = self.palette()
p.setColor(self.backgroundRole(), Qt.white)
self.setPalette(p)
btn = PicButton('/home/user/Desktop/Untitled.png')
btn.move(0, 0)
btn.resize(80,80)
self.show()
app = QApplication(sys.argv)
window = App()
The button will work if you just use window = Widget()and put the button object in there as is shown in this answer: how code a Image button in PyQt?
According to your code you must pass a QPixmap to your PicButton, In addition to them if you are going to move it should tell you where, if you pass the parent, it will place in that position relative to the father, but will not be drawn.
To solve the problem you must change:
btn = PicButton('/home/user/Desktop/Untitled.png')
to:
btn = PicButton(QPixmap('/home/user/Desktop/Untitled.png'), self)