How could I downscale images with kivy/python with good results? - python

I am trying to display images at a smaller size then their source and while I have achieved this, the quality is horrible on any resolution other then 1080p. Here's my code thus far:
import random
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.logger import Logger
from kivy.metrics import Metrics
from kivy.graphics import Color, Rectangle
from kivy.uix.scatter import Scatter
Window.fullscreen = True
Window.size = (1920, 1080)
Player1Hand=['Spade', 'Spade', 'Spade',
'Heart', 'Heart',
'Heart', 'Diamond', 'Club',
'Club', 'Club']
random.shuffle(Player1Hand)
global Draw_Card
Draw_Card = 0
class Sprite(Image):
def __init__(self, **kwargs):
super(Sprite, self).__init__(allow_stretch=True, **kwargs)
self.texture.mag_filter = 'linear'
w, h = self.texture_size
self.size = (params.scale * w, params.scale * h)
class CardSprite(Image):
def __init__(self, **kwargs):
super(CardSprite, self).__init__(allow_stretch=True, **kwargs)
self.texture.mag_filter = 'linear'
w = 210.9
h = 240
self.size = (params.scale * w, params.scale * h)
class PlayerHandArea(Widget):
def __init__(self):
super(PlayerHandArea, self).__init__()
with self.canvas:
Color(1,0,0,0.5)
Rectangle(size=((1152 * params.scale), (240 * params.scale)),
pos = ((384 * params.scale), 0))
class DrawCard(Widget):
def __init__(self):
super(DrawCard, self).__init__()
global Draw_Card
while Draw_Card > 0:
scatter = Scatter(do_rotation=False, do_scale=False,
size_hint=(None,None),
size=(210.9 * params.scale, 240 * params.scale))
self.NextCard = (CardSprite
(source='Images/'+(Player1Hand.pop(0))+'.png'))
self.add_widget(scatter)
scatter.add_widget(self.NextCard)
Draw_Card += -1
class Blank(Widget):
def __init__(self, pos, size):
super(Blank, self).__init__()
with self.canvas:
Color(0, 0, 0)
Rectangle(pos=pos, size=size)
Color(1, 1, 1)
class Game(Widget):
def __init__(self):
super(Game, self).__init__()
self.background = (Sprite
(source='Images/Background.jpg'))
self.size = self.background.size
self.add_widget(self.background)
self.add_widget(PlayerHandArea())
global Draw_Card
Draw_Card += 5
self.add_widget(DrawCard())
self.add_widget(Blank(*params.blank_rect))
class GameApp(App):
def build(self):
params.init()
top = Widget()
top.add_widget(Game())
return top
class params(object):
def init(self):
self.bg_width, self.bg_height = 1920, 1080
self.width, self.height = Window.size
self.center = Window.center
ws = float(self.width) / self.bg_width
hs = float(self.height) / self.bg_height
self.scale = min(ws, hs)
Logger.info('size=%r; dpi=%r; density=%r; SCALE=%r',
Window.size, Metrics.dpi, Metrics.density, self.scale)
if ws > hs:
gap = self.width - (self.bg_width * hs)
self.blank_rect = ((self.width - gap, 0), (gap, self.height))
else:
gap = self.height - (self.bg_height * ws)
self.blank_rect = ((0, self.height - gap), (self.width, gap))
params = params()
if __name__ == '__main__':
GameApp().run()
So I'm using Richard Jones example on setting the size of your images based on the screen size of whos using it. This works perfect when run at 1920 by 1080 but when I switch the Window.size to say 720p the downscaled images have so much noise in them it looks awful. Ivied tried using linear and nearest for the mag_filter and also setting the card's size to dp(210.9) and dp(240) but they come out the same. Does anyone know a better way to scale down with kivy using different resolutions or a document that explains how to get this to work? The original image sizes are 746 by 1037.

Related

Qt3DCore simple wireframe viewer

I'm learning PySide6, and my goal is to use Qt3DCore to draw some 3D wireframe entities (points, lines, bezier curves ..uso..), in a 3D viewer. I like that high level interface.
I used the nice basic torus sphere example (https://doc.qt.io/qtforpython/examples/example_3d__simple3d.html) of the documentation, and added some QLineF lines, but I don't see the lines in the viewer scene.
I think it's because I don't have the correct graphics setting for such wireframe entities... But I don't find any other example or documentation on the subject :-(.
As requested by musicamante, to be as simple as possible, I add the QT example with only 2 modifications : QLine import, and QLine statement in line 121 :
"""PySide6 port of the qt3d/simple-cpp example from Qt v5.x"""
import sys
from PySide6.QtCore import (Property, QObject, QPropertyAnimation, Signal,QPoint,QLine,QLineF)
from PySide6.QtGui import (QGuiApplication, QMatrix4x4, QQuaternion, QVector3D)
from PySide6.Qt3DCore import (Qt3DCore)
from PySide6.Qt3DExtras import (Qt3DExtras)
from PySide6.Qt3DRender import (Qt3DRender)
class OrbitTransformController(QObject):
def __init__(self, parent):
super().__init__(parent)
self._target = None
self._matrix = QMatrix4x4()
self._radius = 1
self._angle = 0
def setTarget(self, t):
self._target = t
def getTarget(self):
return self._target
def setRadius(self, radius):
if self._radius != radius:
self._radius = radius
self.updateMatrix()
self.radiusChanged.emit()
def getRadius(self):
return self._radius
def setAngle(self, angle):
if self._angle != angle:
self._angle = angle
self.updateMatrix()
self.angleChanged.emit()
def getAngle(self):
return self._angle
def updateMatrix(self):
self._matrix.setToIdentity()
self._matrix.rotate(self._angle, QVector3D(0, 1, 0))
self._matrix.translate(self._radius, 0, 0)
if self._target is not None:
self._target.setMatrix(self._matrix)
angleChanged = Signal()
radiusChanged = Signal()
angle = Property(float, getAngle, setAngle, notify=angleChanged)
radius = Property(float, getRadius, setRadius, notify=radiusChanged)
class Window(Qt3DExtras.Qt3DWindow):
def __init__(self):
super().__init__()
# Camera
self.camera().lens().setPerspectiveProjection(45, 16 / 9, 0.1, 1000)
self.camera().setPosition(QVector3D(0, 0, 40))
self.camera().setViewCenter(QVector3D(0, 0, 0))
# For camera controls
self.createScene()
self.camController = Qt3DExtras.QOrbitCameraController(self.rootEntity)
self.camController.setLinearSpeed(50)
self.camController.setLookSpeed(180)
self.camController.setCamera(self.camera())
self.setRootEntity(self.rootEntity)
def createScene(self):
# Root entity
self.rootEntity = Qt3DCore.QEntity()
# Material
self.material = Qt3DExtras.QPhongMaterial(self.rootEntity)
# Torus
self.torusEntity = Qt3DCore.QEntity(self.rootEntity)
self.torusMesh = Qt3DExtras.QTorusMesh()
self.torusMesh.setRadius(5)
self.torusMesh.setMinorRadius(1)
self.torusMesh.setRings(100)
self.torusMesh.setSlices(20)
self.torusTransform = Qt3DCore.QTransform()
self.torusTransform.setScale3D(QVector3D(1.5, 1, 0.5))
self.torusTransform.setRotation(QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45))
self.torusEntity.addComponent(self.torusMesh)
self.torusEntity.addComponent(self.torusTransform)
self.torusEntity.addComponent(self.material)
# Sphere
self.sphereEntity = Qt3DCore.QEntity(self.rootEntity)
self.sphereMesh = Qt3DExtras.QSphereMesh()
self.sphereMesh.setRadius(3)
self.sphereTransform = Qt3DCore.QTransform()
self.controller = OrbitTransformController(self.sphereTransform)
self.controller.setTarget(self.sphereTransform)
self.controller.setRadius(20)
self.sphereRotateTransformAnimation = QPropertyAnimation(self.sphereTransform)
self.sphereRotateTransformAnimation.setTargetObject(self.controller)
self.sphereRotateTransformAnimation.setPropertyName(b"angle")
self.sphereRotateTransformAnimation.setStartValue(0)
self.sphereRotateTransformAnimation.setEndValue(360)
self.sphereRotateTransformAnimation.setDuration(10000)
self.sphereRotateTransformAnimation.setLoopCount(-1)
self.sphereRotateTransformAnimation.start()
self.sphereEntity.addComponent(self.sphereMesh)
self.sphereEntity.addComponent(self.sphereTransform)
self.sphereEntity.addComponent(self.material)
self.lineEntity = QLineF(-2000.0, -2000.0, 2000.0, 2000.0)
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
view = Window()
view.show()
sys.exit(app.exec
())

How to call paintEvent in a Qframe PyQt5?

In pyqt5, I want to insert a circular bar in my QFrame named days. For that, I have painted a circular bar but I am unable to call it under a Qframe. In the last two lines, I am trying to call it under a frame but somehow it doesn't work. I would appreciate it greatly if anybody could help!
circular_bar.py
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
shwd = 10
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Hatchery System")
self.setStyleSheet("background-color: #2c313c;")
self.resize(900, 500)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
# self.setWindowFlags(Qt.FramelessWindowHint)
self.days_frame = QtWidgets.QFrame(self)
self.days_frame.setGeometry(QtCore.QRect(200, 100, 395, 300))
self.days_frame.setStyleSheet("background-color: #222931;" "border-radius: 10px;" "padding: 5px;")
shadow = QGraphicsDropShadowEffect()
shadow.setBlurRadius(shwd)
shadow.setColor(QColor(Qt.white))
shadow.setOffset(0)
self.days_frame.setGraphicsEffect(shadow)
self.days_label = QtWidgets.QLabel('Days', self.days_frame)
self.days_label.setGeometry(QtCore.QRect(10, 10, 131, 41))
self.days_label.setStyleSheet("color: white;")
font = QtGui.QFont()
font.setPointSize(13)
self.days_label.setFont(font)
# self.days_frame(paintEvent)
def paintEvent(self, e):
self.value = 0
self.width = 200
self.height = 200
self.progress_width = 10
self.progress_rounded_cap = True
self.max_value = 100
self.progress_color = 0xff79c6
# Text
self.enable_text = True
self.font_family = "Segoe UI"
self.font_size = 12
self.suffix = "%"
self.text_color = 0xff79c6
# BG
self.enable_bg = True
self.bg_color = 0x44475a
# SET PROGRESS PARAMETERS
width = self.width - self.progress_width
height = self.height - self.progress_width
margin = self.progress_width / 2
value = self.value * 360 / self.max_value
# PAINTER
paint = QPainter(self)
paint.begin(self)
paint.setRenderHint(QPainter.Antialiasing) # remove pixelated edges
paint.setFont(QFont(self.font_family, self.font_size))
# CREATE RECTANGLE
rect = QRect(0, 0, self.width, self.height)
paint.setPen(Qt.NoPen)
paint.drawRect(rect)
# PEN
pen = QPen()
pen.setWidth(self.progress_width)
# Set Round Cap
if self.progress_rounded_cap:
pen.setCapStyle(Qt.RoundCap)
# ENABLE BG
if self.enable_bg:
pen.setColor(QColor(self.bg_color))
paint.setPen(pen)
paint.drawArc(margin, margin, width, height, 0, 360 * 16)
# CREATE ARC / CIRCULAR PROGRESS
pen.setColor(QColor(self.progress_color))
paint.setPen(pen)
paint.drawArc(margin, margin, width, height, -90 * 16, -value * 16)
# CREATE TEXT
if self.enable_text:
pen.setColor(QColor(self.text_color))
paint.setPen(pen)
paint.drawText(rect, Qt.AlignCenter, f"{self.value}{self.suffix}")
# END
paint.end()
# tring to call paintEvent
self.days_frame(paintEvent(self, e))
#self.days_frame.paintEvent(self, e)
app = QApplication([])
mw = MainWindow()
mw.show()
app.exec_()
output
Updated Code
progress.py
from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class CircularProgress(QtWidgets.QWidget):
def __init__(self):
super().__init__()
# CUSTOM PROPERTIES
self.value = 0
self.width = 200
self.height = 200
self.progress_width = 10
self.progress_rounded_cap = True
self.max_value = 100
self.progress_color = 0xff79c6
# Text
self.enable_text = True
self.font_family = "Segoe UI"
self.font_size = 12
self.suffix = "%"
self.text_color = 0xff79c6
# BG
self.enable_bg = True
self.bg_color = 0x44475a
# SET DEFAULT SIZE WITHOUT LAYOUT
self.resize(self.width, self.height)
self.show()
# ADD DROPSHADOW
def add_shadow(self, enable):
if enable:
self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(15)
self.shadow.setXOffset(0)
self.shadow.setYOffset(0)
self.shadow.setColor(QColor(0, 0, 0, 80))
self.setGraphicsEffect(self.shadow)
# SET VALUE
def set_value(self, value):
self.value = value
self.repaint() # Render progress bar after change value
# PAINT EVENT (DESIGN YOUR CIRCULAR PROGRESS HERE)
def paintEvent(self, e):
# SET PROGRESS PARAMETERS
width = self.width - self.progress_width
height = self.height - self.progress_width
margin = self.progress_width / 2
value = self.value * 360 / self.max_value
# PAINTER
paint = QPainter()
paint.begin(self)
paint.setRenderHint(QPainter.Antialiasing) # remove pixelated edges
paint.setFont(QFont(self.font_family, self.font_size))
# CREATE RECTANGLE
rect = QRect(0, 0, self.width, self.height)
paint.setPen(Qt.NoPen)
paint.drawRect(rect)
# PEN
pen = QPen()
pen.setWidth(self.progress_width)
# Set Round Cap
if self.progress_rounded_cap:
pen.setCapStyle(Qt.RoundCap)
# ENABLE BG
if self.enable_bg:
pen.setColor(QColor(self.bg_color))
paint.setPen(pen)
paint.drawArc(margin, margin, width, height, 0, 360 * 16)
# CREATE ARC / CIRCULAR PROGRESS
pen.setColor(QColor(self.progress_color))
paint.setPen(pen)
paint.drawArc(margin, margin, width, height, -90 * 16, -value * 16)
# CREATE TEXT
if self.enable_text:
pen.setColor(QColor(self.text_color))
paint.setPen(pen)
paint.drawText(rect, Qt.AlignCenter, f"{self.value}{self.suffix}")
# END
paint.end()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = CircularProgress()
sys.exit(app.exec_())
main.py
from circularBar import CircularProgress
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.days_frame = QtWidgets.QFrame(self)
self.days_frame.setGeometry(QtCore.QRect(845, 150, 395, 300))
self.days_frame.setStyleSheet("background-color: #222931;" "border-radius: 10px;" "padding: 5px;")
shadow = QGraphicsDropShadowEffect()
shadow.setBlurRadius(shwd)
shadow.setColor(QColor(Qt.white))
shadow.setOffset(0)
self.days_frame.setGraphicsEffect(shadow)
self.days_label = QtWidgets.QLabel('Days', self.days_frame)
self.days_label.setGeometry(QtCore.QRect(10, 10, 131, 41))
self.days_label.setStyleSheet("color: white;")
font = QtGui.QFont
font.setPointSize(13)
self.days_label.setFont(font)
# self.days_frame
self.progress = CircularProgress(self.days_frame)
app = QApplication([])
mw = MainWindow()
mw.show()
app.exec_()
Error:

How to align a rectangle using kivy?

Does anybody know the code to center align a rectangle using kivy but in a py file not kv. This is my code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics import Color
class PongGame(Widget):
def __init__(self, **kwargs):
super(PongGame, self).__init__(**kwargs)
with self.canvas:
Color(255,255,255, mode='rgba')
self.rect = Rectangle(pos=(self.x + 50, self.y + 50), size=(50, 50))
# print(self.rect)
class PingyPong(App):
def build(self):
return PongGame()
# class MakeRectangle(Widget):
# def __init__(self, **kwargs):
# with self.canvas:
# # Color(255,255,255, mode='rgba')
# self.rect = Rectangle(pos=(200, 200), size=(50, 50))
# class PingPong(App):
# def build(self):
# return Rectangle()
if __name__ == '__main__':
PingyPong().run()
This is the output:
enter image description here
please help
You can do it by getting Window size and then create rectangle at the center
from kivy.core.window import Window
class PongGame(Widget):
def __init__(self, **kwargs):
super(PongGame, self).__init__(**kwargs)
with self.canvas:
Color(255,255,255, mode='rgba')
window_size = Window.size
size = 50
self.rect = Rectangle(pos=(window_size[0]/2-size/2, window_size[1]/2-size/2), size=(size, size))
print()

KivyLauncher: Incorret imaging on phone screen

I want to draw image (*.png file) at FlayoutBox, on pc is result OK, after using KivyLauncher is on phone screen only white square. I tested two variants for drawing (draw_img_cnv and draw_img_wdg), but both results are bad.
Cann you explain me:
why is result white square on phone screen,
why draw_img_wdg isn't centered on pc,
how is diferent between both solutions?
Thank You!
from kivy.app import App
from kivy.graphics import Color, Rectangle, Ellipse
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.image import Image
import math
class Screen(FloatLayout):
def __init__(self, **kwargs):
super(Screen, self).__init__(**kwargs)
self.li_item = []
return
def draw_img_cnv(self):
size_img = (498, 498)
pos_img = (self.center[0] -size_img[0]/2.0, self.center[1] -size_img[1]/2.0)
with self.canvas:
Color(0, 1, 0, 1)
self.rect1 = Rectangle(size=self.size, pos=self.pos)
self. rose = Image(source="rose.png",pos=pos_img,size=size_img )
def draw_img_wdg(self):
size_img = (498, 498)
pos_img = (self.center[0] -size_img[0]/2.0, self.center[1] -size_img[1]/2.0)
with self.canvas:
Color(0, 1, 0, 1)
self.rect1 = Rectangle(size=self.size, pos=self.pos)
self. rose = Image(source="rose.png",pos=pos_img,size=size_img )
self.add_widget(self.rose)
return
class MainApp(App):
def build(self):
self.pos_sun = None
root = BoxLayout(orientation = 'vertical')
self.screen = Screen()
button = Button(text = 'Press',size_hint=(1, None), height=50)
root.add_widget(self.screen)
root.add_widget(button)
self.screen.draw_img_wdg()
self.screen.bind(size=self._update_rect, pos=self._update_rect)
return root
def _update_rect(self, instance, value):
self.screen.rect1.pos = instance.pos
self.screen.rect1.size = instance.size
self.screen.rose. pos = (instance.center[0] -498/2.0, \
instance.center[1] -498/2.0)
return
if __name__ == '__main__':
MainApp().run()

How to update a texture from array in Kivy?

I'm new to Kivy, but have watched the tutorials. I want to have a widget containing a texture or image generated from an array, which will change at each frame. See below for what I currently have. Current behaviour is wrong when I resize the window - I think that the old Rectangle is never being deleted, but I can't see how to do that. It also shows the same image in a default (100,100) view at the bottom left of the main window. What do I need to change to achieve the desired behaviour, and not get artifacts when resizing the window?
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.layout import Layout
from kivy.graphics import Rectangle
from kivy.graphics.texture import Texture
from kivy.clock import Clock
import numpy as np
import random
class MainDisplay(Layout):
tex = ObjectProperty(None)
def __init__(self, **kwargs):
super(MainDisplay, self).__init__(**kwargs)
Clock.schedule_once(self.texture_init, 0)
def texture_init(self, instance):
self.tex = Texture.create()
def update(self, dt):
size = 64 * 64 * 3
buf = np.array([int(random.random() * x * 255 / size) for x in range(size)])
print('update', max(buf), min(buf), np.mean(buf))
# then blit the buffer
self.tex.blit_buffer(buf.tostring(), colorfmt='bgr', bufferfmt='ubyte')
print('end update')
print(self.canvas)
print(self.size, self.pos, self, self.parent)
with self.canvas:
Rectangle(texture=self.tex, size=(self.width / 2, self.height / 2), pos=(self.center_x / 2, self.center_y / 2))
class MainWindow(BoxLayout):
md = ObjectProperty(None)
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
def update(self, dt):
self.md.update(dt)
class ProtoApp(App):
def build(self):
mainWindow = MainWindow()
Clock.schedule_interval(mainWindow.update, 1.0/10.0)
return mainWindow
if __name__ == "__main__":
ProtoApp().run()
with the proto.kv file:
<MainWindow>:
md: md
MainDisplay:
id: md
size_hint: (0.5, 0.5)
Thanks in advance for your help!
Problem
Whenever the window is resized, it is creating new rectangle and leaving traces of the previous one.
Solution
Use the canvas's built-in function, clear()
Snippets
def update(self, dt):
size = 64 * 64 * 3
buf = np.array([int(random.random() * x * 255 / size) for x in range(size)])
# then blit the buffer
self.tex.blit_buffer(buf.tostring(), colorfmt='bgr', bufferfmt='ubyte')
with self.canvas:
self.rect = Rectangle(texture=self.tex, size=(self.width / 2, self.height / 2),
pos=(self.center_x / 2, self.center_y / 2))
self.bind(pos=self.update_rect, size=self.update_rect)
def update_rect(self, *args):
self.canvas.clear()
self.rect.pos = self.pos
self.rect.size = self.size

Categories