how to display a random point every second in kivy - python-3.x

I try to display a random point every second in kivy.
Here is my code. I know how to display the point, an ellipse in this case. But I don't know how to make its position to update every second.
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.graphics import Ellipse
import time
import numpy as np
class RandomPoint(Widget):
def __init__(self,dimension):
super(RandomPoint,self).__init__()
self.d = dimension
self.point = Ellipse(pos=list(np.random.randint(0,1000,2)),size = (self.d, self.d))
def update(self, *args):
self.point = Ellipse(pos=list(np.random.randint(0,1000,2)),size = (self.d, self.d))
class TimeApp(App):
def build(self):
wid = Widget()
with wid.canvas:
p = RandomPoint(25)
Clock.schedule_interval(p.update, 1)
return wid
TimeApp().run()
How would you do that ?

Putting the Clock.schedule_interval call in the canvas block won't satisfy the requirement of these calls to happen in a canvas block. They are executed later, when the code exited the with block long ago. What you can do is use the same construction, but inside both __init__ and update, around your Ellipse instructions.
Also, at no point do you add your RandomPoint widget, to your root widget, so it won't be visible at all, whatever happens with its instructions.
class RandomPoint(Widget):
def __init__(self,dimension):
super(RandomPoint,self).__init__()
self.d = dimension
self.points = []
with self.canvas:
self.point.append(Ellipse(pos=list(np.random.randint(0,1000,2)),size = (self.d, self.d)))
def update(self, *args):
with self.canvas:
self.points.append(Ellipse(pos=list(np.random.randint(0,1000,2)),size = (self.d, self.d)))
class TimeApp(App):
def build(self):
wid = Widget()
p = RandomPoint(25)
wid.add_widget(p)
Clock.schedule_interval(p.update, 1)
return wid
TimeApp().run()

Related

Resizing QDialog after removing QWidget

Is there any way by which I can resize a QDialog after removing a QWidget from its layout?
I'm still a beginner so bear with me if the code looks a bit silly.
The main dialog geometry is stored during the resizeEvent()
Before creating the widget:
https://imgur.com/a/vFORp4t
When the widget is created:
https://imgur.com/z7KUa3I
When the widget is removed:
https://imgur.com/KdzULUe
def resizeEvent(self, event):
QtWidgets.QMainWindow.resizeEvent(self, event)
window = self.window()
self.dialog_rect = window.geometry()
self.DialogSizeChanged.emit() # pylint: disable=E1101
Here's the "create widget" code:
def create_info_widget(self):
px = self.dialog_rect.x()
py = self.dialog_rect.y()
w = self.dialog_rect.width()
h = self.dialog_rect.height()
self.dialog_stored_rect = self.dialog_rect
self.info_wdg = abw.ThumbnailInfo("Asset Info")
self.asset_wdg_layout.addWidget(self.info_wdg)
data = self.list_wdg.get_list_thumbnails_data()
path = data['thumbnail']
self.info_wdg.set_info_thumbnail(path)
wdg_h = self.info_wdg.height()
self.setGeometry(px, py, w, h + wdg_h)
self.updateGeometry()
And the "remove widget" code:
def remove_info_widget(self):
wdg = self.findChild(QtWidgets.QFrame, "Asset Info")
if wdg:
self.asset_wdg_layout.removeWidget(wdg)
wdg.deleteLater()
self.info_wdg = None
self.setGeometry(self.dialog_stored_rect)
self.updateGeometry()
As shown on grabbed images, when the widget is removed, it doesn't get back to its size before it was created.
Thank you,
Jacques.
We will first resize it and then use adjustSize to let QApplication know.
We are using resize for just resizing, not for fixed values
Make sure not to apply this when maximized
from PySide2.QtWidgets import (
QApplication,
QPushButton,
QWidget,
QVBoxLayout
)
class Test(QWidget):
def __init__(self):
super().__init__()
self.setLayout(QVBoxLayout(self))
self.add = QPushButton(self, text="Toggle")
self.added = QPushButton(self, text="Hello There")
self.added.setMinimumHeight(600)
self.added.setVisible(False)
self.layout().addWidget(self.add)
self.layout().addWidget(self.added)
self.add.clicked.connect(
lambda: self.added.setVisible(not self.added.isVisible())
)
self.add.clicked.connect(self.toggle)
# trick is to use resize and self.adjustSize
def toggle(self):
if self.isMaximized():
return ...
self.resize(self.width(), 600 if self.added.isVisible() else 100)
self.adjustSize()
sample = QApplication([])
test = Test()
test.show()
sample.exec_()

Kivy strange behavior when it updates Image Texture

I am trying to update the Kivy Image.texture with the OpenCV image, and I am using a new thread to do it. I found some discussion that "the graphics operation should be in the main thread". But still, I want to figure out why the code below works.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread
class MainApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.image = Image()
self.layout.add_widget(self.image)
Thread(target=self.calculate).start()
def build(self):
return self.layout
def calculate(self):
img = cv2.imread("test.png")
w, h = img.shape[1], img.shape[0]
img = cv2.flip(img, flipCode=0)
buf = img.tostring()
texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def main():
Image(source='test.png') # remove this line will froze the app
MainApp().run()
if __name__ == "__main__":
main()
If I remove this line:
Image(source='test.png')
the app is frozen. Can someone help me to understand why decalring an Image object outside the main loop will affect the MainApp running.
The "test.png" image can be any simple image, like below. You need to put the image in the same directory as this script.
Not sure exactly why that line affects your code, but the main problem is that not only is the GUI manipulation required to be done on the main thread, but also any blit_buffer() must also be done on the main thread. So, a working version of your code (with the Image() removed) looks like this:
from functools import partial
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread
class MainApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.image = Image()
self.layout.add_widget(self.image)
Thread(target=self.calculate).start()
def build(self):
return self.layout
def calculate(self):
img = cv2.imread("test.png")
w, h = img.shape[1], img.shape[0]
img = cv2.flip(img, flipCode=0)
buf = img.tostring()
Clock.schedule_once(partial(self.do_blit, buf, w, h))
def do_blit(self, buf, w, h, dt):
texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def main():
# Image(source='test.png') # remove this line will froze the app
MainApp().run()
if __name__ == "__main__":
main()

How to work with a isometric / orthogonal view in Kivy?

The image below is a GridLayout 10 x 10 with buttons.
I'd like to create the same Grid but in an isometric / orthogonal 2d view.
It means that every button, instead of being a square, it might be like a Rhombus, as the image below:
How can I do this?
I don't think you can actually do a 3D rotation on kivy UIX widgets, but you can do 2D rotations, and scaling. Here is an example of an App that does it in the build() method:
from kivy.app import App
from kivy.graphics.context_instructions import PushMatrix, Rotate, Scale, PopMatrix
from kivy.properties import BooleanProperty
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
import numpy as np
def matrixToNumpy(mat):
a = []
for i in range(4):
b = []
for j in range(4):
b.append(mat[i*4+j])
a.append(b)
npmat = np.mat(a)
return npmat
class MyButton(Button):
def on_touch_down(self, touch):
if not self.parent.touched:
self.parent.touched = True
if self.parent.mat is None:
scale = matrixToNumpy(self.parent.sca.matrix)
rotate = matrixToNumpy(self.parent.rot.matrix)
self.parent.mat = np.matmul(rotate, scale)
self.parent.inv_mat = self.parent.mat.I
npTouch = np.mat([touch.x, touch.y, 0, 1.0])
convTouch = np.matmul(npTouch, self.parent.inv_mat)
touch.x = convTouch[0,0]
touch.y = convTouch[0,1]
return super(MyButton, self).on_touch_down(touch)
def on_touch_up(self, touch):
self.parent.touched = False
return super(MyButton, self).on_touch_up(touch)
class MyGridLayout(GridLayout):
touched = BooleanProperty(False)
def __init__(self, **kwargs):
super(MyGridLayout, self).__init__(**kwargs)
self.mat = None
self.inv_mat = None
class MyApp(App):
def build(self):
layout = MyGridLayout(cols=10)
with layout.canvas.before:
PushMatrix()
layout.sca = Scale(1.0, 0.5, 1.0)
layout.rot = Rotate(angle=45, axis=(0,0,1), origin=(400,300,0))
with layout.canvas.after:
PopMatrix()
for i in range (1, 101):
layout.add_widget(MyButton(text=str(i)))
return layout
MyApp().run()
I suspect that with clever application of those two kivy.graphics.context_instructions, you can simulate what you want. The PushMatrix() and PopMatrix() confine the effects of the Scale and Rotate to just the MyGridLayout. You should be able to adjust these values using the layout.sca and layout.rot references.
I noticed after my original answer that the Buttons looked good, but no longer worked. I added a bunch of code to address that issue. All the numpy matrix stuff is just to get the mouse press position into the same coordinates as the MyGridLayout. Unfortunately, applying Canvas scaling and rotations isn't automatically taken into account by the Kivy events, so the additional code is necessary.
Here is what it looks like:

Python - How to switch frames in a Tkinter window

I want to switch frames in a Tkinter window but the previous frame always stays visible in the background.
This is the code I have:
from tkinter import *
class frame(Frame):
def __init__(self,display):
Frame.__init__(self,display)
l = Label(self,text="frame1")
l.pack()
class frame2(Frame):
def __init__(self,display):
Frame.__init__(self,display)
l = Label(self,text="frame2")
l.pack()
class test(Tk):
def __init__(self):
Tk.__init__(self)
f = frame(self)
f.grid(row=0)
f2 = frame2(self)
f2.grid(row=0)
f.tkraise()
t = test()
t.mainloop()
This works if the layout of the two frames is the same but if I add another label to the second frame, it will still be visible in the Background. Is there a way to switch frames so that only elements from the raised frame are visible?
As requested, this is what I used to fix my problem:
from tkinter import *
class frame(Frame):
def __init__(self,display):
Frame.__init__(self,display)
l = Label(self,text="frame1")
l.pack()
class frame2(Frame):
def __init__(self,display):
Frame.__init__(self,display)
l = Label(self,text="frame2")
l.pack()
class test(Tk):
def __init__(self):
Tk.__init__(self)
f2 = frame2(self)
f2.grid(row=0)
#To raise the first frame, I used the following
frame2.grid_remove()
f = frame(self)
f.grid(row=0)
t = test()
t.mainloop()

Kivy: Manipulating dynamically created widgets in update function

I am trying to extend the code in the answer here provided by Nykakin where widgets are defined on the fly in Kivy, dynamically assigned IDs, and then manipulated based on ID.
The 'end game' is to implement some basic 2D physics by changing position of widgets based on ID.
I am not able to 'walk' the widget tree from the update function (errors inline).
Is it possible to do such a thing?
Here is the code:
#code begins here
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.factory import Factory
class MyWidget(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text="Print IDs", id="PrintIDsButton")
button.bind(on_release=self.print_label)
self.add_widget(button)
# crate some labels with defined IDs
for i in range(5):
self.add_widget(Button(text=str(i), id="button no: " + str(i)))
# update moved as per inclement's recommendation
Clock.schedule_interval(MyApp.update, 1 / 30.0)
def print_label(self, *args):
children = self.children[:]
while children:
child = children.pop()
print("{} -> {}".format(child, child.id))
# we can change label properties from here! Woo!
children.extend(child.children)
if child.id == "PrintIDsButton":
child.text = child.text + " :)"
class MyApp(App):
def build(self):
return MyWidget()
def update(self, *args):
#ERROR# AttributeError: 'float' object has no attribute 'root'
children = self.root.children[:]
#ERROR# TypeError: 'kivy.properties.ListProperty' object is not subscriptable
#children = MyWidget.children[:]
#while children:
# child = children.pop()
# print("{} -> {}".format(child, child.id))
# children.extend(child.children)
#ERROR# TypeError: 'kivy.properties.ListProperty' object is not iterable
#for child in MyWidget.children:
# print("{} -> {}".format(child, child.id))
if __name__ == '__main__':
MyApp().run()
Windows 7 SP1
Kivy 1.8.0 / Python 3.3
My apologies if my terminology is incorrect! (and thank you for your time)
children = MyWidget.children[:]
MyWidget is the class itself, not an instance of it, and so the children is a ListProperty object and not the actual list you want.
You want instead the children of the MyWidget instance that is your root widget, self.root.children.
Also, you clock schedule the update function at class level; I think this is bad practice, and could lead to subtle bugs. It would be more normal to do it in the __init__ method of the widget instead.
So putting the update function within the 'MyWidget' class and then accessing elements within 'MyWidget' by calling self.children worked!
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.factory import Factory
class MyWidget(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text="Print IDs", id="PrintIDsButton")
button.bind(on_release=self.print_label)
self.add_widget(button)
# crate some labels with defined IDs
for i in range(5):
self.add_widget(Button(text=str(i), id="button no: " + str(i)))
# update moved as per inclement's recommendation
Clock.schedule_interval(self.update, 1 / 5.0)
def print_label(self, *args):
children = self.children[:]
while children:
child = children.pop()
print("{} -> {}".format(child, child.id))
# Add smiley by ID
children.extend(child.children)
if child.id == "PrintIDsButton":
child.text = child.text + " :)"
def update(self, *args):
children = self.children[:]
while children:
child = children.pop()
# remove smiley by ID
if child.id == "PrintIDsButton":
child.text = "Print IDs"
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
Thanks again to inclement!

Resources