SVG load to Frame - python

I am trying to make a svg widget load inside my Krita docker to act as a cursor. Krita is a painting program and it uses python and PyQt5 to load up plugins. I made a blank docker with the bare minimum for it to load. If anything is add will be in the Area marked with "Testing".
However my SVG load method seems not to be working when compared with other references, and i think it might be the path to the frame itself as I try to place the viewer that I loaded.
# Import Krita
from krita import *
from PyQt5 import QtWidgets, QtCore, QtGui, QtSvg, uic
import os.path
from PyQt5 import QtWidgets, QtSvg
# Set Window Title Name
DOCKER_NAME = "Blank"
# Create Docker
class BlankDocker(DockWidget):
"""
Comments
"""
# Initialize the Dicker Window
def __init__(self):
super(BlankDocker, self).__init__()
# Window Title
self.setWindowTitle(DOCKER_NAME)
# Widget
self.window = QWidget()
self.layout = uic.loadUi(os.path.dirname(os.path.realpath(__file__)) + '/blank.ui', self.window)
self.setWidget(self.window)
# TESTING###################################################################################
self.viewer = QtSvg.QSvgWidget()
self.viewer.load('C:/Users/EyeOd/AppData/Roaming/krita/pykrita/blank/Notes/cursor_lmb.svg')
self.viewer.setGeometry(QtCore.QRect(0,0,200,200))
self.layout.frame.addWidget(self.viewer) #??
############################################################################################
# Change the Canvas
def canvasChanged(self, canvas):
pass
The area of interest of the *.UI file I load is the QFrame = "frame" in which I want to place the widget inside to move around inside.
http://pasteall.org/pic/show.php?id=b7083dbb5e13adafcddb9ee9fdfa8fcd

QFrame doesn't have any method called addWidget so you should have an exception. So if you want to place the QSvgWidget in the QFrame you have 2 alternatives:
Set as parent of QSvgWidget to QFrame.
self.viewer = QtSvg.QSvgWidget(self.window.frame)
self.viewer.load('C:/Users/EyeOd/AppData/Roaming/krita/pykrita/blank/Notes/cursor_lmb.svg')
self.viewer.setGeometry(QtCore.QRect(0, 0, 200, 200))
Use a layout:
self.viewer = QtSvg.QSvgWidget()
self.viewer.load('C:/Users/EyeOd/AppData/Roaming/krita/pykrita/blank/Notes/cursor_lmb.svg')
lay = QVBoxLayout(self.window.frame)
lay.addWidget(self.viewer)
Note: If you use self.layout = uic.loadUi(..., self.window) then you will see that self.layout is the same self.window object so to avoid confusion it is advisable to use uic.loadUi(..., self.window)

Related

Creating QPainter object by passing pixmap via ".pixmap()" causes: "QPaintDevice: Cannot destroy paint device that is being painted"

Ive just recently started to use PyQt6. I am trying to make a custom UI-element so i am (trying) to use QPainter. However even the example code on https://www.pythonguis.com/tutorials/pyqt6-bitmap-graphics/ doesn't work and outputs QPaintDevice: Cannot destroy paint device that is being painted. (I'll paste the code with a few added comments down below)
It seems that when i create an QPainter object by passing it the label's pixmap by using self.label.pixmap() something is happening that is causing the error.
Commenting the two following lines (painter.drawLine(10, 10, 300, 200) and painter.end() does not stop the error from occuring
However, when i pass it the canvas object directly, and then assign the canvas object to label it works. But now i would have to reassign the canvas to label everytime i want to update it.
This is the example code from the website:
import sys
from PyQt6 import QtCore, QtGui, QtWidgets, uic
from PyQt6.QtCore import Qt
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# create widget containing the pixmap
self.label = QtWidgets.QLabel()
# create the pixmap
canvas = QtGui.QPixmap(400, 300)
# set the pixmap to the label
self.label.setPixmap(canvas)
# i dont really know what this does but it was in the example code
self.setCentralWidget(self.label)
self.draw_something()
def draw_something(self):
# create the painter and tell it what pixmap to draw on
painter = QtGui.QPainter(self.label.pixmap())
# draw a line
painter.drawLine(10, 10, 300, 200)
# end / delete the printer
painter.end()
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
And this is my Version which somewhat works:
import sys
from PyQt6 import QtCore, QtGui, QtWidgets, uic
from PyQt6.QtCore import Qt
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# create widget containing the pixmap
self.label = QtWidgets.QLabel()
# create the pixmap
self.canvas = QtGui.QPixmap(400, 300)
# this was not in the example code but without it the background is just black and the line therefore not visible
self.canvas.fill(QtGui.QColor("white"))
# i dont really know what this does but it was in the example code
self.setCentralWidget(self.label)
self.draw_something()
# set the pixmap to the label
self.label.setPixmap(self.canvas)
def draw_something(self):
# create the painter and tell it what pixmap to draw on (in this case directly to canvas obj)
painter = QtGui.QPainter(self.canvas)
# draw a line
painter.drawLine(10, 10, 300, 200)
# end / delete the printer
painter.end()
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
So what is causing this problem? May this be a bug or am i missing something here?

How to draw button or widget upon QWidget using python-MPV on windows?

I use this library https://github.com/jaseg/python-mpv and PySide6 to embed MPV in container (QWidget) in python. I want to draw the button (or something else maybe qlabel for example?) over QWidget (embedded mpv) on top of player but how can I do that? I tried to draw button but when mpv starts playing player overrides the button with itself. How to place button on top of player?
Update:
I read here that I should use opengl as render api.:https://github.com/mpv-player/mpv-examples/blob/master/libmpv/README.md
Update 2:
Maybe i need to use qml?
How can I use opengl along with pyside6 (or pyqt5) and mpv to render properly?
My code there:
import os
from PySide6 import QtWidgets
os.environ['PATH'] += os.path.dirname(__file__) #you need to place mpv-2.dll (or mpv-1.dll) in folder with project
import mpv
import sys
from PySide6.QtWidgets import *
from PySide6.QtCore import *
class Ui_Form(object):
def setupUi(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
Form.resize(780, 477)
self.pushButton = QPushButton(Form)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setGeometry(QRect(30, 430, 75, 24))
self.retranslateUi(Form)
QMetaObject.connectSlotsByName(Form)
# setupUi
def retranslateUi(self, Form):
Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
self.pushButton.setText(QCoreApplication.translate("Form", u"PushButton", None))
# retranslateUi
class PlayerWidget(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent=None):
super(PlayerWidget, self).__init__(parent)
self.setupUi(self)
self.retranslateUi(self)
class Test(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.container = PlayerWidget()
self.setCentralWidget(self.container)
self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
self.container.setAttribute(Qt.WA_NativeWindow)
player = mpv.MPV(wid=str(int(self.container.winId())),
vo='gpu',
log_handler=print,
loglevel='debug', ytdl=True)
player.play('test.mp4') # place your test video in folder
app = QApplication(sys.argv)
# This is necessary since PyQT stomps over the locale settings needed by libmpv.
# This needs to happen after importing PyQT before creating the first mpv.MPV instance.
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
win = Test()
win.show()
win.resize(1280, 720)
sys.exit(app.exec_())
As far as I know this is not possible if you use the window id embedding method.
In order to draw over the video you would need to use the render api.
An example with qml exists: https://github.com/jaseg/python-mpv#using-opengl-from-pyqt5qml
It should also be possible to achieve this using QtWidgets (and not Qml).

Python PyQt5 how to show the full QMenuBar with a QWidget

I'm getting this weird result when using QMenuBar I've used this exact code before for the QMenuBar and it worked perfectly. But it doesn't show more than 1 QMenu
This is my code:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
from functools import partial
class MainMenu(QWidget):
def __init__(self, parent = None):
super(MainMenu, self).__init__(parent)
# background = QWidget(self)
lay = QVBoxLayout(self)
lay.setContentsMargins(5, 35, 5, 5)
self.menu()
self.setWindowTitle('Control Panel')
self.setWindowIcon(self.style().standardIcon(getattr(QStyle, 'SP_DialogNoButton')))
self.grid = QGridLayout()
lay.addLayout(self.grid)
self.setLayout(lay)
self.setMinimumSize(400, 320)
def menu(self):
menubar = QMenuBar(self)
viewMenu = menubar.addMenu('View')
viewStatAct = QAction('Dark mode', self, checkable=True)
viewStatAct.setStatusTip('enable/disable Dark mode')
viewMenu.addAction(viewStatAct)
settingsMenu = menubar.addMenu('Configuration')
email = QAction('Set Email', self)
settingsMenu.addAction(email)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainMenu()
main.show()
sys.exit(app.exec_())
Result:
I am aware that I am using QWidget when I should be using QMainWindow But is there a workaround???
(I apologize in advance for the terrible quality of the image, there is no good way to take a picture of a QMenuBar)
The problem is that with a QWidget you are not using the "private" layout that a QMainWindow has, which automatically resizes specific children widgets (including the menubar, the statusbar, the dock widgets, the toolbars and, obviously, the "centralWidget").
Remember that a QMainWindow has its own layout (which can't and shouldn't be changed), because it needs that specific custom layout to lay out the aforementioned widgets. If you want to set a layout for the main window, you'll need to apply it to its centralWidget.
Read carefully how the Main Window Framework behaves; as the documentation reports:
Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.
In order to work around that when using a basic QWidget, you'll have to manually resize the children widgets accordingly. In your case, you only need to resize the menubar, as long as you have a reference to it:
def menu(self):
self.menubar = QMenuBar(self)
# any other function has to be run against the *self.menubar* object
viewMenu = self.menubar.addMenu('View')
# etcetera...
def resizeEvent(self, event):
# calling the base class resizeEvent function is not usually
# required, but it is for certain widgets (especially item views
# or scroll areas), so just call it anyway, just to be sure, as
# it's a good habit to do that for most widget classes
super(MainMenu, self).resizeEvent(event)
# now that we have a direct reference to the menubar widget, we are
# also able to resize it, allowing all actions to be shown (as long
# as they are within the provided size
self.menubar.resize(self.width(), self.menubar.height())
Note: you can also "find" the menubar by means of self.findChild(QtWidgets.QMenuBar) or using the objectName, but using an instance attribute is usually an easier and better solution.
Set minimum width
self.setMinimumSize(320,240)

PyQt: How can i set stylesheet contents on scaled mode?

I had an application that contained a lot of widgets with stylesheet on them, However, I did not add any layout to interface, It neither had central widget included, But the application was running without any problems.
However, whenever i tried to resize the application (scaling it down) the widgets would not scale, of course.
I had an little research (Because i could not find anything else related to my problem) and i found this on Qt Documentation, stylesheet reference:
"The actual image that is drawn is determined using the same algorithm as QIcon (i.e) the image is never scaled up but always scaled down if necessary."
How can i make stylesheet scale down with window? (If stylesheet has background image on)
For example i have button with stylesheet:
btn = QtGui.QPushButton(self)
btn.move(0, 0)
btn.setObjectName('btn)
btn.setStyleSheet("#btn {background-image: url(':/images/somepicture.png'); border: none; }")
How can i make this button scale down with window, Can i achieve this without layouts? If not how can i do it with layouts? (without it limiting too much)
If you add the button as the central widget to a QMainWindow it should automatically adjust it's size to fit the available space. However, to get the button image to scale, you need to set the image as a border-image stylesheet property (a little strange). A working example for PyQt4:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
btn = QtGui.QPushButton(self)
btn.setStyleSheet("border-image: url('somepicture.png');") # Scaled
#btn.setStyleSheet("background-image: url('somepicture.png');") # Not scaled
self.setCentralWidget(btn)
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
Note that you don't need to set an id (objectName) to assign the CSS to a specific widget, you can simply pass in the CSS rule via .setStyleSheet().
You cannot set a layout on QMainWindow as it already has a complex layout system to accommodate docking widgets and toolbars. Therefore, if you want to use a layout to add more than one widget to the window, you need to use a container widget to hold it. The following working example demonstrates this:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
w = QtGui.QWidget() # container widget
l = QtGui.QVBoxLayout() # your layout
w.setLayout(l) # set the layout on your container widget
btn = QtGui.QPushButton(self)
btn.setStyleSheet("border-image: url('somepicture.png');")
label = QtGui.QLabel('Hello!')
l.addWidget(btn) # add your widget to the layout
l.addWidget(label) # add the label to the layout
self.setCentralWidget(w) # add the container widget to the QMainWindow
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
If you want to be able to position widgets absolutely, rather than adding them to a layout (which will control their size/position) you can pass the parent element (relative to which x,y coords are taken) when creating it:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
w = QtGui.QWidget() # container widget
btn = QtGui.QPushButton(w)
btn.move(100,100)
btn.setStyleSheet("border-image: url('somepicture.png');")
self.setCentralWidget(w) # add the container widget to the QMainWindow
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
But positioning a widget absolutely like this loses you the ability to auto-scale it to fit the parent widget. If you just want some padding/spacing around the element in the window, take a look at .setContentsMargins on the QLayouts, e.g. l.setContentsMargins(50,50,50,50) will put a 50px margin around the button.

Access QT Designer Objects Programmatically

Disclaimer: New to both python and qt designer
QT Designer 4.8.7
Python 3.4
PyCharm 5.0.3
Question - How do I add controls to the main form or a scroll area widget on the main form (created in QT Designer) programmatically?
I have created a MainWindow in qt designer and added my widgets. The following is the entire test program in PyCharm:
import sys
from PyQt4 import QtGui, QtCore, uic
from PyQt4.QtGui import *
from PyQt4.QtCore import *
qtCreatorFile = "programLauncher.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
# Cannot resize or maximize
self.setFixedSize(1045, 770)
# Add button test
self.dateLabel = QtGui.QLabel("Test")
self.pushButton = QtGui.QPushButton('Test button')
# self.scrollArea_programs.addWidget()
grid = QtGui.QGridLayout()
# self.scrollArea_programs.addWidget(self.pushButton)
grid.addWidget(self.dateLabel,0,0)
grid.addWidget(self.pushButton,0,1)
self.setLayout(grid)
self.pushButton_exit.clicked.connect(self.closeEvent)
def closeEvent(self):
QtGui.QApplication.quit()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
As you can see I tried to add controls to a grid but nothing shows up when the program runs - I have also tried to add a control to the scroll area. Can someone help me to just add 1 control to the scroll area at run time - so then I can know the proper way to do it or "a" proper way to do this.
Thanks in advance
Without having access to your programLauncher.ui and making minimal changes to your posted code, you can add your UI elements to the window like so:
from PyQt4 import QtGui
import sys
class MyApp(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# Cannot resize or maximize
self.setFixedSize(1045, 770)
widget = QtGui.QWidget(self)
self.setCentralWidget(widget)
# Add button test
self.dateLabel = QtGui.QLabel("Test")
self.pushButton = QtGui.QPushButton('Test button')
grid = QtGui.QGridLayout()
grid.addWidget(self.dateLabel, 0, 0)
grid.addWidget(self.pushButton, 0, 1)
widget.setLayout(grid)
self.pushButton.clicked.connect(self.closeEvent)
def closeEvent(self, event):
QtGui.QApplication.quit()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
This will get the controls on the screen, although the layout leaves a lot to be desired. You may have to make modifications to this based on what's in your .ui file. One thing that you'll want to note in this example is that the QMainWindow needs a central widget (widget in the example above). You then set the layout on that widget.
You can use the designer to create your .ui file
The you can load it in your .py using something like:
from PyQt4 import QtCore, QtGui, uic
class my_win(QtGui.QMainWindow):
def __init__(self):
self.ui = uic.loadUi('my_ui.ui',self)
then you can access all your widgets with something like
self.ui.actionQuit.triggered.connect(QtGui.qApp.quit)
or
self.ui.my_button.triggered.connect(self.do_someting)
Thanks to JCVanHamme (the programLauncher.ui hint) and also outside help I now learned most of what I need to know to access MainWindow at run time. So for anyone interested in this beginner tip:
Take a blank form in QT Designer
Add a control
Run pyuic4 batch file
Take a look at the generated .py file to learn EVERYTHING about how to add controls.
Don't let the power go to your head - cheers

Categories

Resources