Create pyside application in different shape than rectangle - python

I am building an application in Pyside2.
As we know when we use mainwindow for our application it comes with rectangle shape.
But I want user define shape of application.
For example check below image of Zoiper application.
The background is my editor with some text, You can easily feel the outer border of application.
Can we achieve the same thing using pyside2?
Thanks in advance.

Create a transparent widget. Two lines are important:
self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
self.setAttribute(Qt.WA_TranslucentBackground)
Working example:
from PySide2.QtCore import Qt
from PySide2.QtGui import QColor, QPainterPath, QPainter
from PySide2.QtWidgets import QApplication, QWidget
class WCustomShape(QWidget):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
self.setFixedSize(400, 300)
self.setAttribute(Qt.WA_TranslucentBackground)
def paintEvent(self, e):
painter = QPainter(self)
path = QPainterPath()
path.addEllipse(200, 150, 100, 50)
painter.fillPath(path, QColor(Qt.blue))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
main_window = WCustomShape()
main_window.show()
main_window.raise_()
sys.exit(app.exec_())

Related

How to make an image selectable using pixmap and Qlabel?

I am trying to show multiple images using Pyqt5. It would be nice to make the image selectable within the GUI so that the users can select and copy that image right away easily.
By "selectable", I meant the user can right click the image and then copy it and then potentially paste it to somewhere else outside of the GUI. Just like a normal image saved in a Word. User can select/copy an image in Word and then paste it to somewhere else.
I know for Text in Qlabel this can easily achieved by using self.my_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse). However, it seems for images there is no such method handling it. Is there any way I can work it out for images?
import sys
import PyQt5
from PyQt5.QtWidgets import (
QLabel,
QVBoxLayout,
QWidget
)
from PyQt5 import QtCore
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QSize
class Display_Window(QWidget):
def __init__(self):
super().__init__()
self.setMinimumSize(QSize(980,700))
self.layout = QVBoxLayout(self)
self.label1 = QLabel(self)
self.pixmap = QPixmap(path_to_my_image)
self.pixmap = self.pixmap.scaled(900, 900, QtCore.Qt.KeepAspectRatio)
self.label1.setPixmap(self.pixmap)
self.label1.resize(self.pixmap.width(), self.pixmap.height())
# Run if Script
if __name__ == "__main__":
app = PyQt5.QtWidgets.QApplication(sys.argv)
MainWindow = Display_Window() # Initialize GUI
MainWindow.show() # Show Window
app.exec_()
You can subclass the label and create a menu whenever it has a valid pixmap, then use the system clipboard to copy it.
class CopiableLabel(QLabel):
def contextMenuEvent(self, event):
pixmap = self.pixmap()
if not pixmap.isNull():
menu = QMenu()
copyAction = menu.addAction('Copy image to clipboard')
if menu.exec_(event.globalPos()) == copyAction:
QApplication.clipboard().setPixmap(pixmap)
return
super().contextMenuEvent(event)
class Display_Window(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout(self)
self.label1 = CopiableLabel(self)
self.layout.addWidget(self.label1)
self.pixmap = QPixmap(path_to_my_image)
self.pixmap = self.pixmap.scaled(900, 900, Qt.KeepAspectRatio)
self.label1.setPixmap(self.pixmap)
Note that setting a pixmap on a QLabel automatically resizes it (unless it has the scaledContents property set to True.
You also should add the label to the layout, as I did in the above modifications.

How to get size of widgets

I am developing a PyQt5 application however I am having issues with the heights of the widgets. Below is a simplified version of my issue:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class App(QWidget):
def __init__(self):
super().__init__()
self.showMaximized()
self.setStyleSheet("QWidget {background: blue;}")
print(self.frameGeometry().height())
self.show()
if __name__ == "__main__":
window = QApplication(sys.argv)
app = App()
window.setStyle(QStyleFactory.create("Fusion"))
window.exec_()
Here I create a window and maximise it. Using a tkinter window, it tells me the height maximised is 841, which is the size of my screen, however the PyQt5 application prints the height to be 519. Is this an issue with the self.showMaximized() method, or some other issue.
Resizing is not instantaneous in Qt. What Qt does is take the information from showMaximized to activate the flag of the native window (library that depends on each OS) then after a time T the OS applies that flag and sends it the new geometry. So in your case you have to give it a delay to get the correct information.
import sys
from PyQt5.QtWidgets import QApplication, QStyleFactory, QWidget
from PyQt5.QtCore import QTimer
class App(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("QWidget {background: blue;}")
self.showMaximized()
QTimer.singleShot(100, self.calculate)
def calculate(self):
print(self.frameGeometry().height())
if __name__ == "__main__":
window = QApplication(sys.argv)
app = App()
window.setStyle(QStyleFactory.create("Fusion"))
window.exec_()
On the other hand, if your objective is to know the size of the initial screen then you should not use a QWidget for that since it will depend on the time it takes for Qt and the native library to create the native window, instead use the Screen class :
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QGuiApplication
if __name__ == "__main__":
window = QApplication(sys.argv)
print(QGuiApplication.primaryScreen().availableGeometry().height())

PyQt user logo in center of MDI Area

I have been working on PyQt QMainWindow QMdiArea Class. I have been able to change the background colour as it is needed for my application.
However, I am unable to add a logo in the centre of the window.
I have tried QBrush but that just insert logo in full QMdiArea.
Furthermore, I have tried paintEvent overridden method but that seems to not work.
Please find enclosed my code and snapshot of the code output below:
# Import necessary libraries
import sys
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtGui import QColor, QBrush, QPainter
from PyQt5.QtWidgets import QStyleFactory, QWidget, QMainWindow, QMdiArea
class MDI_Window(QMainWindow, QWidget):
def __init__(self):
super().__init__()
self.centralWidget = QWidget(self)
self.mdi = QMdiArea()
self.setCentralWidget(self.mdi)
self.window_initialize()
self.show()
def window_initialize(self):
title = 'MDI'
self.setWindowTitle(title)
self.setWindowIcon(QtGui.QIcon("Some_Icon.png"))
self.setMinimumSize(800, 600)
self.mdi.setBackground(QBrush(QColor(169, 169, 169)))
self.showMaximized()
def paintEvent(self, event):
self.mdi.paintEvent(event)
self.painter = QPainter(self)
# For testing logo
self.painter.drawPixmap(500, 500, 500, 500, QtGui.QPixmap("KDS_Main-Window.png"))
if __name__ == "__main__":
# Create App with the design
LMS_App = QtWidgets.QApplication(sys.argv)
LMS_App.setStyle(QStyleFactory.create('Fusion'))
a = MDI_Window()
# Exit application when system is terminated
sys.exit(LMS_App.exec_())
You cannot implement the paintEvent like that, mostly because a paintEvent has to be called by Qt and for the specific widget that is being painted. The paint event must be implemented in the widget.
The easiest solution is to subclass the QMdiArea:
class MdiArea(QMdiArea):
def paintEvent(self, event):
# call the base implementation to draw the default colored background
super().paintEvent(event)
# create the painter *on the viewport*
painter = QPainter(self.viewport())
painter.drawPixmap(500, 500, 500, 500, QtGui.QPixmap("KDS_Main-Window.png"))
Note that:
you shoud remove the paintEvent for the main window, now;
as you can see, the painter is called on the viewport: this is mandatory for all QAbstractScrollArea subclasses;
QPainter instances used in a paintEvent should not be set as instance attributes (as you can see, I didn't use self.painter), as the painter must be destroyed at the end of the function, otherwise you'll face performance and drawing issues; you could theoretically avoid this issue by manually calling painter.end(), but, since a new QPainter instance is most likely going to be recreated very soon and very often, making it a persistent attribute each time has really no use.

how to close a window in pyqt5 and terminate the program? program gets stuck

I want to create a window in pyqt5 and then close it. For some reason the program does not exit after closing the window. It gets stuck. I have been reading several related posts but none give a clear answer.
I have already tried code such as "self.object.close()", "app.quit()", even "self.object.destroy()", but all work in the same way. The only thing that really closes the window is by clicking the x (close) at the window itself. But this is not the behavior I need. I would like to close the window using my code.
import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSlot, pyqtSignal
class window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(300, 300, 480, 300)
self.setWindowTitle('Hello World')
self.setWindowLayout()
def setWindowLayout(self):
self.w = QtWidgets.QWidget(self)
self.layout = QtWidgets.QHBoxLayout()
self.label = QtWidgets.QLabel('Hello World Label')
self.layout.addWidget(self.label)
self.w.setLayout(self.layout)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = window()
main_window.show()
main_window.close()
sys.exit(app.exec_())
Once I close the window using
main_window.close(). I want my program to exit.
I apologize if this was resolved in a different post. I searched the answer and none solves my problem.
Thanks.
It seems to me that it is a bug since according to the docs the application should be closed if there is no top-level window but it seems that it is not verified if the closing of the window is not after the event-loop starts. A workaround is to use QTimer.singleShot(0, ...) to close the window:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(300, 300, 480, 300)
self.setWindowTitle('Hello World')
self.setWindowLayout()
def setWindowLayout(self):
self.w = QtWidgets.QWidget(self)
self.layout = QtWidgets.QHBoxLayout()
self.label = QtWidgets.QLabel('Hello World Label')
self.layout.addWidget(self.label)
self.w.setLayout(self.layout)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = window()
main_window.show()
QtCore.QTimer.singleShot(0, main_window.close) # <---
sys.exit(app.exec_())

pyqt5 / qtdesigner textbox default label

I am trying to create a GUI for my python program. One of the tools that I need is a text input box.
Now, I want a text label for this box saying "Please insert texts." Is there a function to add a label that shows inside the input textbox as default and disappear when user click the box to type?
I don't mind to use qt designer or pyqt5 coding.
Thank you guys.
placeholderText : QString
This property holds the line edit's placeholder text
import sys
from PyQt5.QtWidgets import QLineEdit, QVBoxLayout, QApplication, QWidget
class Test(QWidget):
def __init__(self):
super().__init__()
self.lineEdit = QLineEdit(placeholderText="Please insert texts.") # <---
vbox = QVBoxLayout(self)
vbox.addWidget(self.lineEdit)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())
I am begginer like you and my English is not so good. But I recommend you use Qt Designer. It's easier, fastter for you draw your app. I am using pyside2 project and recommend you read docummentatio each widgets you wanna use in PySide2 project and Qt Project. Try code below
enter image description here
import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QDialog
from PySide2.QtWidgets import QTextEdit
from PySide2.QtWidgets import QVBoxLayout
from PySide2.QtCore import Qt
class MainDialog(QDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
# Create Widget TextEdit
self.text = QTextEdit()
# I think that you wanna this function in your program
# https://doc.qt.io/qtforpython/PySide2/QtWidgets/QLineEdit.html?highlight=qlineedit#PySide2.QtWidgets.PySide2.QtWidgets.QLineEdit.setPlaceholderText
# http://doc.qt.io/qt-5/qlineedit.html#placeholderText-prop
self.text.setPlaceholderText('''Yes! this is exactly what I want!
Thank you, what if you have a big text box (more than 10 lines) and
you want to scale up the place holder and align it in center?? ''')
# https://doc.qt.io/qtforpython/PySide2/QtWidgets/QLineEdit.html?highlight=qlineedit#PySide2.QtWidgets.PySide2.QtWidgets.QLineEdit.setAlignment
# http://doc.qt.io/qt-5/qlineedit.html#alignment-prop
self.text.setAlignment(Qt.AlignCenter)
# Layout
layout = QVBoxLayout()
layout.addWidget(self.text)
self.setLayout(layout)
def main():
app = QApplication()
mainDialog = MainDialog()
mainDialog.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories