How to move a pixel column in PyQt5 Pixmap? - python

I am making a python project,I am using pyqt. The aim is to show a picture (I am using Pixmap) and I want that any column of pixel of this picture go down randomly according to the time. The aim is to create an effect of 'dissolve' the screen.
Here is my code :
#-*- coding: utf-8 -*-
# ---------- Imports ----------
import sys, time, os, random
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
# ---------- Main ----------
class Ui (QMainWindow):
def __init__(self):
QWidget.__init__(self)
# ----- Fichiers -----
folder_path = os.path.dirname(os.path.abspath(__file__))
self.picture_path = str(folder_path + '\\solar_wallpaper.jpg')
self.icon_path = str(folder_path + '\\solar_icon.ico')
# ----- Configuration de la fenĂȘtre -----
self.setWindowFlags(self.windowFlags() &~ Qt.WindowCloseButtonHint)
self.setWindowFlags(self.windowFlags() &~ Qt.WindowMinimizeButtonHint)
self.setWindowFlags(self.windowFlags() &~ Qt.WindowMaximizeButtonHint)
self.setWindowTitle('Solar')
self.setWindowIcon(QIcon(self.icon_path))
self.setStyleSheet("background-color: black;")
# ----- Appel des méthodes -----
self.init_components()
self.init_layout()
self.showFullScreen()
def init_components (self):
self.setCentralWidget(QGroupBox())
self.picture = QLabel(self)
self.picture.setScaledContents(True)
self.picture.setPixmap(QPixmap(self.picture_path))
self.picture.setAlignment(Qt.AlignCenter)
colonne = self.picture.width()
for i in range (colonne):
None
def init_layout (self):
self.layout = QVBoxLayout()
self.layout.addWidget(self.picture)
self.centralWidget().setLayout(self.layout)
# ---------- Launcher ----------
app = QApplication.instance()
if not app :
app = QApplication(sys.argv)
ui = Ui()
app.exec()

A possible solution is to copy and paste pieces of rectangles but with a vertical distance:
import random
import sys
from PyQt5.QtCore import QRect, QTimer
from PyQt5.QtGui import QColor, QPainter, QPixmap
from PyQt5.QtWidgets import QApplication, QLabel
COLUMN_WIDTH = 10
DELTA = 10
def build_pixmap():
delta = 20
pixmap = QPixmap(512, 512)
pixmap.fill(QColor("blue"))
painter = QPainter(pixmap)
for i in range(5, 15):
dt = i * delta
r = QRect(pixmap.rect().adjusted(0, dt, 0, -dt))
color = QColor(*random.sample(range(255), 3))
painter.fillRect(r, color)
painter.end()
return pixmap
def aply_effect(pixmap, number_of_columns):
i = round(pixmap.width() / COLUMN_WIDTH)
for _ in range(number_of_columns):
j = random.randint(0, i)
rect = QRect(j * COLUMN_WIDTH, 0, COLUMN_WIDTH, pixmap.height())
pix = pixmap.copy(rect)
painter = QPainter(pixmap)
painter.drawPixmap(rect.translated(0, DELTA), pix)
painter.end()
return pixmap
def main():
app = QApplication(sys.argv)
pixmap_demo = build_pixmap()
label = QLabel(pixmap=pixmap_demo)
label.show()
def on_timeout():
out_pixmap = aply_effect(label.pixmap(), 40)
label.setPixmap(out_pixmap)
timer = QTimer(interval=40, timeout=on_timeout)
timer.start()
app.exec_()
if __name__ == "__main__":
main()

Related

PyQt5 QGraphicsPathItem to draw a Resistor

I am coding an app to solve electrical circuits and I need to model the circuit. To do so, I need to draw Resistors and other shapes on a scene, being able to move it and so on.
The thing is that I am tryng to show a resistor en the scene and I can't find the way. I am tryng by using QGraphicsPathItem but looking at the documentation I am not capable of doing it. I'll show a bit of the code I am writing to solve this part of the app:
1. The first code I show is the Resistor as it should be shown
2. The second part is the way I want to do it, but instead an ellipsis, I want to show a Resistor
### 1st Code
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtCore import Qt
x0 = 100
y0 = 50
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.x1 = 12
self.y1 = 33
self.y2 = 18
self.y3 = 15
self.y4 = 9
self.y5 = 3
self.p1 = QtCore.QPoint(0, 0 + self.y1)
self.p2 = QtCore.QPoint(0, 0 + self.y2)
self.p3 = QtCore.QPoint(self.x1, self.y3)
self.p4 = QtCore.QPoint(-self.x1, self.y4)
self.p5 = QtCore.QPoint(self.x1, self.y5)
self.p6 = QtCore.QPoint(-self.x1, -self.y5)
self.p7 = QtCore.QPoint(self.x1, -self.y4)
self.p8 = QtCore.QPoint(-self.x1, -self.y3)
self.p9 = QtCore.QPoint(0, 0 - self.y2)
self.p10 = QtCore.QPoint(0, 0 - self.y1)
def draw_resistor(self,angle=0, x0=0, y0=0):
self.x0 = x0
self.y0 = y0
self.label = QtWidgets.QLabel()
self.canvas = QtGui.QPixmap(200, 100) # This is to create the canvas
self.canvas.fill() # To set the canvas background color to white. If not, we will only see a black frame
self.label.setPixmap(self.canvas)
self.setCentralWidget(self.label)
self.painter = QtGui.QPainter(self.label.pixmap())
self.painter.translate(self.x0,self.y0) # To change the axis origin
self.painter.rotate(angle)
self.painter.drawLines(self.p1,self.p2,self.p2,self.p3,self.p3,self.p4,self.p4,self.p5,self.p5,
self.p6,self.p6,self.p7,self.p7,self.p8,self.p8,self.p9,self.p9,self.p10)
self.painter.end()
def rotate(self,angle=0):
self.painter.rotate(angle)
self.painter.drawLines(self.p1,self.p2,self.p2,self.p3,self.p3,self.p4,self.p4,self.p5,self.p5,
self.p6,self.p6,self.p7,self.p7,self.p8,self.p8,self.p9,self.p9,self.p10)
self.label.update() # Research about this, it could be the key
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
window.draw_resistor(45,x0,y0)
app.exec_()
###################
###################
###################
###################
### 2nd Code
import sys
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QGraphicsPathItem, QGraphicsView, QGraphicsScene, QGraphicsEllipseItem, QLabel
from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
from PyQt5.QtGui import QPixmap, QPainter
class MovingObject(QGraphicsEllipseItem):
def __init__(self, x, y, r):
super().__init__(0, 0, r, r)
self.setPos(x, y)
self.setBrush(Qt.blue)
self.setAcceptHoverEvents(True)
# Mouse hover events
def hoverEnterEvent(self, event):
app.instance().setOverrideCursor(Qt.OpenHandCursor)
def hoverLeaveEvent(self, event):
app.instance().restoreOverrideCursor()
# Mouse click events
def mousePressEvent(self, event):
pass
def mouseMoveEvent(self, event):
orig_cursor_position = event.lastScenePos()
updated_cursor_position = event.scenePos()
orig_position = self.scenePos()
updated_cursor_x = updated_cursor_position.x() - orig_cursor_position.x() + orig_position.x()
updated_cursor_y = updated_cursor_position.y() - orig_cursor_position.y() + orig_position.y()
self.setPos(QPointF(updated_cursor_x, updated_cursor_y))
def mouseReleaseEvent(self, event):
print("x: {0}, y: {1}".format(self.pos().x(), self.pos().y()))
class GraphicView(QGraphicsView):
def __init__(self):
super().__init__()
self.scene = QGraphicsScene()
self.setScene(self.scene)
self.setSceneRect(0, 0, 1200, 1000)
self.moveObject = MovingObject(50, 50, 40)
self.moveObject2 = MovingObject(100, 100, 100)
self.scene.addItem(self.moveObject)
self.scene.addItem(self.moveObject2)
app = QApplication(sys.argv)
view = GraphicView()
view.show()
sys.exit(app.exec_())
I solved it, this is the solution:
class Resistor(QGraphicsPathItem):
def __init__(self, x, y):
super(Resistor, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.setAcceptHoverEvents(True)
self.isSelected = False
self.setPath(self.create_path())
self.setPos(x, y)
def create_path(self):
path = QPainterPath()
path.moveTo(0, 33)
path.lineTo(0, 18)
path.lineTo(12, 15)
path.lineTo(-12, 9)
path.lineTo(12, 3)
path.lineTo(-12, -3)
path.lineTo(12, -9)
path.lineTo(-12, -15)
path.lineTo(0, -18)
path.lineTo(0, -33)
return path

How can I "Print Preview" of document created by QTextDocument in PyQt5?

Hello Experts!! I hope you are having great day. I am new in GUI programming specially PyQt5. I am practicing on simple GUI invoice application. In this application, I successfully generated the Invoice By QTextDocument. Now i want to add print dialogue and print preview option. I am having trouble in the code. This is saying
AttributeError: 'InvoiceForm' object has no attribute
'printpreviewDialog
As i am new, i am little bit confused in there. Could you please fix the code? That will help me a lot to study. Many Many Thanks.
The code has given below:-
import sys
from PyQt5.QtCore import pyqtSignal, QSize, QSizeF, QDate
from PyQt5.QtGui import QTextDocument, QTextCursor, QFont
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import QWidget, QFormLayout, QLineEdit, QPlainTextEdit, QSpinBox, QDateEdit, QTableWidget, \
QHeaderView, QPushButton, QHBoxLayout, QTextEdit, QApplication, QMainWindow
font= QFont('Arial',16)
class InvoiceForm(QWidget):
submitted = pyqtSignal(dict)
def __init__(self):
super().__init__()
self.setLayout(QFormLayout())
self.inputs = dict()
self.inputs['Customer Name'] = QLineEdit()
self.inputs['Customer Address'] = QPlainTextEdit()
self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True)
self.inputs['Days until Due'] = QSpinBox()
for label, widget in self.inputs.items():
self.layout().addRow(label, widget)
self.line_items = QTableWidget(rowCount=10, columnCount=3)
self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours'])
self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.layout().addRow(self.line_items)
for row in range(self.line_items.rowCount()):
for col in range(self.line_items.columnCount()):
if col > 0:
w = QSpinBox()
self.line_items.setCellWidget(row, col, w)
submit = QPushButton('Create Invoice', clicked=self.on_submit)
print = QPushButton('Print Invoice', clicked=self.printpreviewDialog)
self.layout().addRow(submit,print)
def on_submit(self):
data = {'c_name': self.inputs['Customer Name'].text(),
'c_addr': self.inputs['Customer Address'].toPlainText(),
'i_date': self.inputs['Invoice Date'].date().toString(),
'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(),
'i_terms': '{} days'.format(self.inputs['Days until Due'].value()),
'line_items': list()}
for row in range(self.line_items.rowCount()):
if not self.line_items.item(row, 0):
continue
job = self.line_items.item(row, 0).text()
rate = self.line_items.cellWidget(row, 1).value()
hours = self.line_items.cellWidget(row, 2).value()
total = rate * hours
row_data = [job, rate, hours, total]
if any(row_data):
data['line_items'].append(row_data)
data['total_due'] = sum(x[3] for x in data['line_items'])
self.submitted.emit(data)
# remove everything else in this function below this point
class InvoiceView(QTextEdit):
dpi = 72
doc_width = 8.5 * dpi
doc_height = 6 * dpi
def __init__(self):
super().__init__(readOnly=True)
self.setFixedSize(QSize(self.doc_width, self.doc_height))
def build_invoice(self, data):
document = QTextDocument()
self.setDocument(document)
document.setPageSize(QSizeF(self.doc_width, self.doc_height))
document.setDefaultFont(font)
cursor = QTextCursor(document)
cursor.insertText(f"Customer Name: {data['c_name']}\n")
cursor.insertText(f"Customer Address: {data['c_addr']}\n")
cursor.insertText(f"Date: {data['i_date']}\n")
cursor.insertText(f"Total Due: {data['total_due']}\n")
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
central = QWidget()
self.setCentralWidget(central)
layout = QHBoxLayout(central)
self.invoiceForm = InvoiceForm()
layout.addWidget(self.invoiceForm)
self.invoiceView = InvoiceView()
layout.addWidget(self.invoiceView)
# hide the widget right now...
self.invoiceView.setVisible(False)
self.invoiceForm.submitted.connect(self.showPreview)
def showPreview(self, data):
self.invoiceView.setVisible(True)
self.invoiceView.build_invoice(data)
def printpreviewDialog(self):
printer = QPrinter(QPrinter.HighResolution)
previewDialog = QPrintPreviewDialog(printer, self)
previewDialog.paintRequested.connect(self.printPreview)
previewDialog.exec_()
def printPreview(self, printer):
self.invoiceView.build_invoice.print_(printer)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()
The main problem is that self.printpreviewDialog is a member of MainWindow, not of InvoiceForm, so you should connect the clicked signal from the main window instead.
Also note that you tried to use self.invoiceView.build_invoice.print_(), but this wouldn't work as you are not calling build_invoice, and even if you did, that function doesn't return anything.
You should use the self.invoiceView.document() instead, but you must ensure that the data has been built before.
class InvoiceForm(QWidget):
submitted = pyqtSignal(dict)
def __init__(self):
# ...
submit = QPushButton('Create Invoice', clicked=self.on_submit)
# make the button a member of the instance instead of a local variable,
# so that we can connect from the main window instance
self.printButton = QPushButton('Print Invoice')
self.layout().addRow(submit, self.printButton)
# ...
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.invoiceForm.printButton.clicked.connect(self.printpreviewDialog)
# ...
def printPreview(self, printer):
self.invoiceView.document().print_(printer)
Note: never, never use built-in functions and statements for variable names, like print.
Try it:
import sys
from PyQt5.QtCore import pyqtSignal, QSize, QSizeF, QDate
from PyQt5.QtGui import QTextDocument, QTextCursor, QFont
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import (QWidget, QFormLayout, QLineEdit, QPlainTextEdit,
QSpinBox, QDateEdit, QTableWidget, QHeaderView, QPushButton, QHBoxLayout,
QTextEdit, QApplication, QMainWindow)
font = QFont('Arial',16)
class InvoiceForm(QWidget):
submitted = pyqtSignal(dict)
def __init__(self, parent=None): # + parent=None
super().__init__(parent) # + parent
self.setLayout(QFormLayout())
self.inputs = dict()
self.inputs['Customer Name'] = QLineEdit()
self.inputs['Customer Address'] = QPlainTextEdit()
self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True)
self.inputs['Days until Due'] = QSpinBox()
for label, widget in self.inputs.items():
self.layout().addRow(label, widget)
self.line_items = QTableWidget(rowCount=10, columnCount=3)
self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours'])
self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.layout().addRow(self.line_items)
for row in range(self.line_items.rowCount()):
for col in range(self.line_items.columnCount()):
if col > 0:
w = QSpinBox()
self.line_items.setCellWidget(row, col, w)
submit = QPushButton('Create Invoice', clicked=self.on_submit)
# + vvvvvv vvvvvvvvvvvvv
_print = QPushButton('Print Invoice', clicked=self.window().printpreviewDialog) # + _print, + self.window()
self.layout().addRow(submit, _print) # + _print
def on_submit(self):
data = {'c_name': self.inputs['Customer Name'].text(),
'c_addr': self.inputs['Customer Address'].toPlainText(),
'i_date': self.inputs['Invoice Date'].date().toString(),
'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(),
'i_terms': '{} days'.format(self.inputs['Days until Due'].value()),
'line_items': list()}
for row in range(self.line_items.rowCount()):
if not self.line_items.item(row, 0):
continue
job = self.line_items.item(row, 0).text()
rate = self.line_items.cellWidget(row, 1).value()
hours = self.line_items.cellWidget(row, 2).value()
total = rate * hours
row_data = [job, rate, hours, total]
if any(row_data):
data['line_items'].append(row_data)
data['total_due'] = sum(x[3] for x in data['line_items'])
self.submitted.emit(data)
# remove everything else in this function below this point
# +
return data # +++
class InvoiceView(QTextEdit):
dpi = 72
doc_width = 8.5 * dpi
doc_height = 6 * dpi
def __init__(self):
super().__init__(readOnly=True)
self.setFixedSize(QSize(self.doc_width, self.doc_height))
def build_invoice(self, data):
document = QTextDocument()
self.setDocument(document)
document.setPageSize(QSizeF(self.doc_width, self.doc_height))
document.setDefaultFont(font)
cursor = QTextCursor(document)
cursor.insertText(f"Customer Name: {data['c_name']}\n")
cursor.insertText(f"Customer Address: {data['c_addr']}\n")
cursor.insertText(f"Date: {data['i_date']}\n")
cursor.insertText(f"Total Due: {data['total_due']}\n")
# +
return document # +++
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
central = QWidget()
self.setCentralWidget(central)
layout = QHBoxLayout(central)
# + vvvv
self.invoiceForm = InvoiceForm(self) # + self
layout.addWidget(self.invoiceForm)
self.invoiceView = InvoiceView()
layout.addWidget(self.invoiceView)
# hide the widget right now...
self.invoiceView.setVisible(False)
self.invoiceForm.submitted.connect(self.showPreview)
def showPreview(self, data):
self.invoiceView.setVisible(True)
self.invoiceView.build_invoice(data)
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
def printpreviewDialog(self):
previewDialog = QPrintPreviewDialog()
previewDialog.paintRequested.connect(self.printPreview)
previewDialog.exec_()
def printPreview(self, printer):
# self.invoiceView.build_invoice.print_(printer)
data = self.invoiceForm.on_submit()
document = self.invoiceView.build_invoice(data)
document.print_(printer)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()

Getting a Qt Widget to update a mouse event inside a Qt form made with Qt Designer

I have made a custom QGraphicsView that I'm adding as a widget into an app form I made using Qt Designer. Everything appears to be working including the mouse clicks. The method that does the drawing gets called but I'm having trouble getting to actually repaint on the screen. I tried creating a copy of the paintEvent method and calling in the mouseEvent method. I also tried calling the paintEvent directly. I Can't seem to get it to repaint with the mouseEvent. Here is the code:
import sys
from PySide2 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QPushButton, QLineEdit, QAction, QSlider
from PySide2.QtWidgets import QListWidget, QTabWidget, QGraphicsView, QGraphicsScene
from PySide2.QtWidgets import QSpinBox, QWidget, QDialog, QVBoxLayout
from PySide2.QtGui import QPixmap, QImage, QMatrix, QPainter, QColor
from PySide2.QtGui import QMouseEvent, QCursor, QPaintEvent
from PySide2.QtCore import QFile, QObject, SIGNAL
import cv2
import numpy as np
import math
class Display_Pixels(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent=parent)
#super().__init__()
self.initUI()
self.img = cv2.imread('roi.jpg')
def initUI(self):
#self.setGeometry(100, 100, 450, 450)
#self.setWindowTitle('By Pixel')
#self.setMouseTracking(True)
#self.show()
res = 40
self.grid = np.array([ [-1] * res for n in range(res)]) # list comprehension
#print(self.grid.shape)
def paintEvent(self, e):
qp = QPainter()
qp.begin(self.viewport())
self.drawRectangles(qp)
qp.end()
def mousePaintEvent(self):
qp = QPainter()
qp.begin(self.viewport())
self.drawRectangles(qp)
qp.end()
def drawRectangles(self, qp, w = 20):
print("Drawing")
mode = 0
x,y = 0,0 # starting position
lr = 20
hr = 35
col = QColor(0, 0, 0)
col.setNamedColor('#d4d4d4')
qp.setPen(col)
#print(self.img.shape)
for g_row, img_row in zip(self.grid, self.img):
#print(img_row.shape)
for g_col, img_col in zip(g_row, img_row):
r, g, b = (img_col[0], img_col[1], img_col[2])
#print(r,g,b)
if g_col == 1:
if mode == 0:
r = int(math.log(r+1)*lr)
g = int(math.log(g+1)*hr)
b = int(math.log(b+1)*lr)
elif mode == 1:
if r+50 <= 220: r = r+50
if g+80 <= 255: g = g+80
if b+50 <= 220: b = b+50
else:
if r+70 <= 220: r = r+70
if g+140 <= 255: g = g+140
if b+70 <= 220: b = b+70
qp.setBrush(QColor(r, g, b))
qp.drawRect(x, y, w, w)
else:
qp.setBrush(QColor(r, g, b))
qp.drawRect(x, y, w, w)
#qp.setBrush(QColor(200, 0, 0))
#qp.drawRect(x, y, w, w)
x = x + w # move right
y = y + w # move down
x = 0 # rest to left edge
def mousePressEvent(self, QMouseEvent):
w = 16.0
#print("MOUSE:")
#print('(', int(QMouseEvent.x()/w), ', ', int(QMouseEvent.y()/w), ')')
#print (QMouseEvent.pos())
x = float(QMouseEvent.x())
y = float(QMouseEvent.y())
self.grid[int(y/w)][int(x/w)] = -1 * self.grid[int(y/w)][int(x/w)]
#print(img[int(y/w), int(x/w), :])
self.paintEvent(QPaintEvent)
#self.mousePaintEvent()
self.update()
self.repaint()
if __name__ == '__main__':
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
px = Display_Pixels()
px.show()
sys.exit(app.exec_())
You should not call the paintEvent method directly, creating a method with a similar name will not magically be called. You have to call the update() method of the viewport().
def mousePressEvent(self, event):
w = 16.0
x = int(event.x()*1.0/w)
y = int(event.y()*1.0/w)
s1, s2 = self.grid.shape
# verify
if 0 <= y < s1 and 0 <= x < s2:
self.grid[x][y] = -self.grid[x][y]
self.viewport().update()

The button with transparent circular edge cannot be drawn correctly above the widget embedded with LibVLC

I have a program that plays video with LibVLC, and I want to put some circular buttons on top of the VLC Widget for a barrage effect.
This is my test code.
# -*- coding: utf-8 -*-
import sys
import os
import vlc
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
class demo_widget(QWidget):
def __init__(self):
super(demo_widget, self).__init__()
self.__ui__()
def __ui__(self):
self.resize(600, 400)
t_lay_parent = QHBoxLayout()
t_lay_parent.setContentsMargins(0, 0, 0, 0)
self.frame_media = QFrame(self)
t_lay_parent.addWidget(self.frame_media)
self.setLayout(t_lay_parent)
self.pushButton_test = QPushButton("Test", self)
self.pushButton_test.setFixedSize(70, 30)
self.pushButton_test.setStyleSheet("QPushButton{background-color:#36404A;border:1px solid #36404A;border-radius:10px;color:#98A6AD;}")
self.pushButton_test.move(265, 185)
self.pushButton_test.clicked.connect(self.slt_play)
self.instance = vlc.Instance("--network-caching=1000 --http-continuous")
self.player = self.instance.media_player_new()
if sys.platform.startswith('linux'): # for Linux using the X Server
self.player.set_xwindow(self.frame_media.winId())
elif sys.platform == "win32": # for Windows
self.player.set_hwnd(self.frame_media.winId())
elif sys.platform == "darwin": # for MacOS
self.player.set_nsobject(self.frame_media.winId())
def slt_play(self):
media = self.instance.media_new("1111.m4v")
self.player.set_media(media)
self.player.play()
app = QApplication(sys.argv)
demo = demo_widget()
demo.show()
sys.exit(app.exec_())
This is the ideal working screenshot.
But it actually produces white edges.
I tried to add the following Settings to the button, and it successfully worked as expected, but it ran outside the window.
self.pushButton_test.setWindowFlags(Qt.SplashScreen | Qt.FramelessWindowHint)
self.pushButton_test.setAttribute(Qt.WA_TranslucentBackground, True)
Is there a better way to draw a button with a transparent circular Angle?
A possible solution is to use QRegion with setMask():
# -*- coding: utf-8 -*-
import sys
import os
import vlc
from PySide2 import QtCore, QtGui, QtWidgets
class RoundButton(QtWidgets.QPushButton):
def __init__(self, radius, *args, **kwargs):
super(RoundButton, self).__init__(*args, **kwargs)
self._radius = radius
def resizeEvent(self, event):
r = self.rect()
rb = QtCore.QRect(0, 0, 2*self._radius, 2*self._radius)
reg = QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
rb.moveRight(r.right())
reg += QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
rb.moveBottom(r.bottom())
reg += QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
rb.moveLeft(r.left())
reg += QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
reg += QtGui.QRegion(self._radius, 0, r.width() - 2*self._radius, r.height())
reg += QtGui.QRegion(0, self._radius, r.width(), r.height() - 2*self._radius)
self.setMask(reg)
super(RoundButton, self).resizeEvent(event)
class demo_widget(QtWidgets.QWidget):
def __init__(self):
super(demo_widget, self).__init__()
self.__ui__()
def __ui__(self):
self.resize(600, 400)
t_lay_parent = QtWidgets.QHBoxLayout(self)
t_lay_parent.setContentsMargins(0, 0, 0, 0)
self.frame_media = QtWidgets.QFrame()
t_lay_parent.addWidget(self.frame_media)
self.pushButton_test = RoundButton(10, "Test", self)
self.pushButton_test.setFixedSize(70, 30)
self.pushButton_test.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
self.pushButton_test.setStyleSheet("""QPushButton{
background-color:#36404A;
border:1px solid #36404A;
color:#98A6AD;
}""")
self.pushButton_test.move(265, 185)
self.pushButton_test.clicked.connect(self.slt_play)
self.instance = vlc.Instance("--network-caching=1000 --http-continuous")
self.player = self.instance.media_player_new()
if sys.platform.startswith('linux'): # for Linux using the X Server
self.player.set_xwindow(self.frame_media.winId())
elif sys.platform == "win32": # for Windows
self.player.set_hwnd(self.frame_media.winId())
elif sys.platform == "darwin": # for MacOS
self.player.set_nsobject(self.frame_media.winId())
def slt_play(self):
media = self.instance.media_new("1111.m4v")
self.player.set_media(media)
self.player.play()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
demo = demo_widget()
demo.show()
sys.exit(app.exec_())

How to create a grid of splitters

What I'm trying to do is add splitter to a QGridLayout in order to resize the layout with the mouse. So for instance with this :
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.setFixedWidth(300)
self.setFixedHeight(100)
self.wid = QWidget()
self.setCentralWidget(self.wid)
self.grid = QGridLayout()
l_a = QLabel('A')
l_b = QLabel('B')
l_c = QLabel('C')
l_d = QLabel('D')
l_e = QLabel('E')
l_f = QLabel('F')
l_g = QLabel('G')
l_h = QLabel('H')
l_i = QLabel('I')
self.grid.addWidget(l_a, 0, 0)
self.grid.addWidget(l_b, 0, 1)
self.grid.addWidget(l_c, 0, 2)
self.grid.addWidget(l_d, 1, 0)
self.grid.addWidget(l_e, 1, 1)
self.grid.addWidget(l_f, 1, 2)
self.grid.addWidget(l_g, 2, 0)
self.grid.addWidget(l_h, 2, 1)
self.grid.addWidget(l_i, 2, 2)
self.wid.setLayout(self.grid)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
I get this:
What I would like is instead of the colored line, have the possibility to click and drag vertically (for green lines) and horizontally (for red lines) the grid borders.
I tried something with QSplitter directly, but I end up with:
The Horizontal splits are okay, but the vertical ones are not aligned any more:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.setFixedWidth(300)
self.setFixedHeight(100)
self.wid = QWidget()
self.setCentralWidget(self.wid)
# self.grid = QGridLayout()
self.globallayout = QVBoxLayout()
self.split_V = QSplitter(Qt.Vertical)
l_a = QLabel('A')
l_b = QLabel('B')
l_c = QLabel('C')
l_d = QLabel('D')
l_e = QLabel('E')
l_f = QLabel('F')
l_g = QLabel('G')
l_h = QLabel('H')
l_i = QLabel('I')
split_H = QSplitter(Qt.Horizontal)
split_H.addWidget(l_a)
split_H.addWidget(l_b)
split_H.addWidget(l_c)
self.split_V.addWidget(split_H)
split_H = QSplitter(Qt.Horizontal)
split_H.addWidget(l_d)
split_H.addWidget(l_e)
split_H.addWidget(l_f)
self.split_V.addWidget(split_H)
split_H = QSplitter(Qt.Horizontal)
split_H.addWidget(l_g)
split_H.addWidget(l_h)
split_H.addWidget(l_i)
self.split_V.addWidget(split_H)
self.globallayout.addWidget(self.split_V)
self.wid.setLayout(self.globallayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
Update
I think I almost found a solution where a function is used so that whenever the vertical splits are changed, it re-aligns them:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.setFixedWidth(300)
self.setFixedHeight(100)
self.wid = QWidget()
self.setCentralWidget(self.wid)
# self.grid = QGridLayout()
self.globallayout = QVBoxLayout()
self.split_V = QSplitter(Qt.Vertical)
l_a = QLabel('A')
l_b = QLabel('B')
l_c = QLabel('C')
l_d = QLabel('D')
l_e = QLabel('E')
l_f = QLabel('F')
l_g = QLabel('G')
l_h = QLabel('H')
l_i = QLabel('I')
self.split_H1 = QSplitter(Qt.Horizontal)
self.split_H1.addWidget(l_a)
self.split_H1.addWidget(l_b)
self.split_H1.addWidget(l_c)
self.split_V.addWidget(self.split_H1)
self.split_H2 = QSplitter(Qt.Horizontal)
self.split_H2.addWidget(l_d)
self.split_H2.addWidget(l_e)
self.split_H2.addWidget(l_f)
self.split_V.addWidget(self.split_H2)
self.split_H3 = QSplitter(Qt.Horizontal)
self.split_H3.addWidget(l_g)
self.split_H3.addWidget(l_h)
self.split_H3.addWidget(l_i)
self.split_V.addWidget(self.split_H3)
self.globallayout.addWidget(self.split_V)
self.wid.setLayout(self.globallayout)
self.split_H1.splitterMoved.connect(self.moveSplitter)
self.split_H2.splitterMoved.connect(self.moveSplitter)
self.split_H3.splitterMoved.connect(self.moveSplitter)
# self.split_H1.splitterMoved
# self.moveSplitter(0,self.split_H1.at )
def moveSplitter( self, index, pos ):
# splt = self._spltA if self.sender() == self._spltB else self._spltB
self.split_H1.blockSignals(True)
self.split_H2.blockSignals(True)
self.split_H3.blockSignals(True)
self.split_H1.moveSplitter(index, pos)
self.split_H2.moveSplitter(index, pos)
self.split_H3.moveSplitter(index, pos)
self.split_H1.blockSignals(False)
self.split_H2.blockSignals(False)
self.split_H3.blockSignals(False)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
However, I still have an issue at the begining - the alignment is not correct :
I don't know How call the function moveSplitter in the __init__
It seems that directly calling moveSplitter (which is a protected method) may be problematic. Using Qt-5.10.1 with PyQt-5.10.1 on Linux, I found that it can often result in a core dump when called during __init__. There is probably a good reason why Qt provides setSizes as a public method for changing the position of the splitters, so it may be wise to prefer it over moveSplitter.
With that in mind, I arrived at the following implementation:
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
...
self.split_H1.splitterMoved.connect(self.moveSplitter)
self.split_H2.splitterMoved.connect(self.moveSplitter)
self.split_H3.splitterMoved.connect(self.moveSplitter)
QTimer.singleShot(0, lambda: self.split_H1.splitterMoved.emit(0, 0))
def moveSplitter(self, index, pos):
sizes = self.sender().sizes()
for index in range(self.split_V.count()):
self.split_V.widget(index).setSizes(sizes)
The single-shot timer is needed because on some platforms the geometry of the window may not be fully initialized before it is shown on screen. And note that setSizes does not trigger splitterMoved, so there is no need to block signals when using it.

Categories