I want to draw my own object on a layout, so I'm trying to subclass gdk.drawable,
class link(gtk.gdk.Drawable):
def __init__(self,comp1,comp2,layout):
super(link, self).__init__()
self.x1=comp1.xpos
self.y1=comp1.ypos
self.x2=comp2.xpos
self.y2=comp2.ypos
self.layout=layout
error:
cannot create instance of abstract (non-instantiable) type `GdkDrawable'
I can do it without subclassing drawable using layout.bin_window.draw_line() in a method drawlink() of my link object, but I'm not able to create a custom graphic context gdk.gc for each object and I have to use the layout.get_style() which will be the same for all my links!
def drawlink(self):
gc = self.layout.get_style().fg_gc[gtk.STATE_NORMAL]
gc.line_style=gtk.gdk.LINE_ON_OFF_DASH
gc.line_width=6
self.layout.bin_window.draw_line(gc,self.x1, self.y1, self.x2, self.y2)
this is the reason I want to subclass drawable. if I can use a custom gc without subclassing drawable or(window, pixmap) it would be great.
Thanks
any alternative?
If I understand you correctly, what you want to do is not subclassing gtk.gdk.Drawable but populating a gtk.DrawingArea widget with your own content. The description of that widget is:
The gtk.DrawingArea widget is used for creating custom user interface elements. It's essentially a blank widget containing a gtk.gdk.Window that you can draw on.
Related
How can I change an attribute on the object when the attribute is Frame? I want to change the color of the frame.
class MyFrame:
def __init__(self, bg_color)
self.window = tk.Frame(self.parent, bg=bg_color)
mainApp:
frame_obj = MyFrame("blue")
#want to change the color after the frame obj has been created
frame_obj.window.bg = "red" #this does not work
Tkinter is based on different progamming language, named tcl and thats why things seem a bit unpythonic here.
The tk.Frame object isnt a pure python object, rather its an wrapped object in a tcl interpreter and thats why you cant address the attributes you intuitivly think you can. You need to adress the attributes in a way the tcl interpreter is abel to handle and therefor methods are created like widget.configure.
To achive what you want with your current code, it would look like:
import tkinter as tk
root = tk.Tk()
class MyFrame():
def __init__(self, bg_color):
self.window = tk.Frame(width=500,height=500,bg=bg_color)
frame_obj = MyFrame('blue')
frame_obj.window.pack(fill=tk.BOTH)
frame_obj.window.configure(bg='yellow')
root.mainloop()
In addition, the proper way for an OOP approach for a frame would look like this:
class MyFrame(tk.Frame):
def __init__(self,master,**kwargs):
super().__init__(master)
self.configure(**kwargs)
frame_obj = MyFrame(root,width=500,height=500,bg='green')
frame_obj.pack(fill=tk.BOTH)
This way your class becomes a child of the tk.Frame object and you can adress it directly. The syntax self in this context refers directly to the tk.Frame object. Also it is good practice to use the format of
def __init__(self,master,**kwargs):
while its the same for the parent/tk.Frame. It has a single positional argument, named master and keywords arguments for the configuration of the Frame.
Please take a look at a tutorial for tkinter and for OOP. If you had you would know that. Please dont feel offended, but StackOverflow requiers a brief reasearch and that includes to read documentation and take tutorials.
I am trying to make GUI elements using pygame (I know it's odd, but they are to be used inside a pygame made game). Here is a very simplified piece of code of how a UI element class looks like in my code:
import pygame
from typing import Tuple
class UIElement(pygame.Surface):
def __init__(self, size: Tuple[int, int]) -> None:
super().__init__(size)
self.children = {}
def draw(self) -> None:
...
Note that the class is just an extension of the pygame.Surface class. Now, what I would like to do is make those elements resizable, for example if the size is not prior knowledge, implementing a method with this signature:
def resize(self, size: Tuple[int, int]) -> None:
...
However, pygame doesn't allow for Surface objects to be resized in-place, which forces me to create a new UIElement instance with the right size. I would like this new instance to be a copy of my first instance, except for the size of the pygame.Surface, and this new instance should replace the first one. How would I do it from within the first instance with my resize method?
I looked through the copy module, but it doesn't seem to fit my issue.
Furthermore, I know it may seem simpler to just add a surface attribute to my class, which would be holding the pygame.Surface object, and set a new value for this attribute when needed but this would force me to go through tremendous architecture redesign work and I would prefer to avoid it.
If you have any idea as how to fix my issue, I would appreciate it.
No you can't. I suggest using the simple approach. UIElement should not be a subclass of pygame.Surface. The pygame.Surface object should only be an attribute of the UIElement. All your problems are caused by inheritance.
A pygame.Surface object cannot be resized. A Surface is just a proxy object for an internally managed, SDL surface object with immutable size. PyGame has no API to change the object under the hood. All you can do is to create a new Surface with a different size and use pygame.transform.scale() to set the content of the scaled Surface:
class UIElement(pygame.Surface):
# [...]
def resize(self, size):
scaled_element = UIElement(size)
pygame.transform.scale(self, size, scaled_element)
scaled_element.children = self.children
return scaled_element
I wanted to ask if we could just animate the fontsize of the QLabel in PyQt5 with QPropertyAnimation.
I tried by adding QPropertyAnimation(self.label , b"fontSize") but It was not working and I got the warning
I want you increase the text of the QLabel and then again switch to normal size
From the QPropertyAnimation description:
QPropertyAnimation interpolates over Qt properties. As property values are stored in QVariants, the class inherits QVariantAnimation, and supports animation of the same meta types as its super class.
A class declaring properties must be a QObject. To make it possible to animate a property, it must provide a setter (so that QPropertyAnimation can set the property's value).
All classes that inherit from QObject (including widgets) support Qt properties, and in fact most of them have "builtin" properties, but, obviously, not all properties support animations: they must be based on numeric values. You can animate a numeric change (eg: from 0 to 100), not a text (eg. from 'ABC' to 'PYZ').
For instance, all QWidget have a pos property, and since such property (which is a QPoint) is based on numbers, you can create an animation. You can see the list of supported variant types for animations in the QVariantAnimation docs. Consider that all documentation pages regarding QObject subclasses contain a property section (see the QWidget properties for example), and that properties obviously are inherited from their super classes.
There is no property for the font size, though, so there are two possibilities:
create a custom property (by using the pyqtProperty decorator in PyQt);
use a QVariantAnimation, which is not bound to any property;
Depending on the situation, one might prefer one approach or the other, but for this specific case it doesn't change that much; the QVariantAnimation is certainly simpler, the Qt property approach is more "compliant".
Here you can see how the property is created using the pyqtProperty decorator. Since the animation writes the property, having a setter is mandatory.
class AnimationTest(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.startButton = QtWidgets.QPushButton('Start')
self.label = QtWidgets.QLabel('Hello!')
self.labelFont = self.font()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.startButton)
layout.addWidget(self.label)
self.ani = QtCore.QPropertyAnimation(self, b'labelFontSize')
self.ani.setStartValue(10)
self.ani.setEndValue(80)
self.ani.setDuration(1500)
self.startButton.clicked.connect(self.ani.start)
#QtCore.pyqtProperty(int)
def labelFontSize(self):
return self.labelFont.pointSize()
#labelFontSize.setter
def labelFontSize(self, size):
self.labelFont.setPointSize(size)
self.label.setFont(self.labelFont)
import sys
app = QtWidgets.QApplication(sys.argv)
test = AnimationTest()
test.show()
sys.exit(app.exec_())
The variant animation is certainly shorter:
class AnimationTest(QtWidgets.QWidget):
def __init__(self):
# ...
self.ani = QtCore.QVariantAnimation()
self.ani.setStartValue(10)
self.ani.setEndValue(80)
self.ani.setDuration(1500)
self.ani.valueChanged.connect(self.updateLabelFont)
self.startButton.clicked.connect(self.ani.start)
def updateLabelFont(self, value):
self.labelFont.setPointSize(value)
self.label.setFont(self.labelFont)
Please consider that font size animations are often problematic, as most fonts have slightly different glyphs and spacings depending on the size. In the examples above I used a pretty long animation that will probably show the effect: while the size increases linearly, the change in the font usually is not very smooth.
I understand that QLabel attributes such as frameGeometry, pixmap and text can be recovered using their respective commands. But is it possible to get the value of "frame shadow" around each of these label widgets?
I have 3 Labels placed inside a Frame (inside a Window) using qt-designer. I assigned shadows for each of these labels by calling self.label_1.setFrameShadow(QFrame.Raised) or self.label_1.setFrameShadow(QFrame.Plain) within the QMainWindow class.
Now I wish to update their shadow attributes after checking to see if one of them is Raised or Plain. The error says that: 'QLabel' object has no attribute 'FrameShadow'. But why so if I was able to set it?
QLabel inherits from QFrame and therefore has an accessor frameShadow() for that property.
Unlike in other frameworks, Qt accessors don't start with get...
I am trying to display a QGraphicsView inside a QGraphicsWidget, but I can't seem to get it to work.
This is what I want to achieve, where the QGraphicsWidgets are all added to a QGraphicsScene, and each widget has their own QGraphicsScene and QGraphicsView.
The large box represents the main view, and the smaller boxes represent widgets, each with their own view and scenes.
My current implementation doesn't work, due to the following error:
TypeError: QGraphicsLinearLayout.addItem(QGraphicsLayoutItem):
argument 1 has unexpected type 'QGraphicsView'
My implemetation in a subclass of the QGraphicsWidget:
self.scene = QtGui.QGraphicsScene(self)
self.view = QtGui.QGraphicsView(self.scene)
self.view.setRenderHint(QtGui.QPainter.Antialiasing)
self.setLayout(QtGui.QGraphicsLinearLayout(Qt.Vertical))
self.layout().addItem(self.view)
I've tried to implement this with a QGraphicsLayoutItem, but I couldn't find anywhere to stick the QGraphicsView in.
Any help would be appreciated.
The function QGraphicsLinearLayout.addItem expects a QGraphicsWidget parameter and you are passing a normal QWidget (QGraphicsView).
You can use the class QGraphicsProxyWidget which is specially designed for wrapping QWidget into QGraphicsWidget.
I do not have Qt + Python on hands but I think it should look as follows:
proxy = parentView.addWidget(nestedView) # QWidget -> QGraphicsWidget
self.layout().addItem(proxy)