I'm starting to learn Kivy.
The code below generates a 10x10 button grid:
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.uix.button import Button
class MyApp(App):
def build(self):
layout = GridLayout(cols=10)
for i in range (1, 101):
layout.add_widget(Button(text=str(i)))
return layout
MyApp().run()
Now, I'd like to add a png image to an independent layer, which randomly "wander" over these buttons, independently.
Then the user should click on the button on which the image is going, as in a game.
That is, the image should not be clickable and will be shown only visually over the buttons, meanwhile, the buttons should respond perfectly as if there was no image over them.
How to do this?
You can draw the image in the Canvas of the GridLayout using a Rectangle. And the position can be updated using Clock_schedule_interval(). Like this:
from kivy.clock import Clock
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.uix.button import Button
class MyApp(App):
def build(self):
layout = GridLayout(cols=10)
with layout.canvas.after:
Color(1,1,1,0.5) # if you want to see through the image
self.bg = Rectangle(source='KQxab.png') # source is the image
for i in range (1, 101):
layout.add_widget(Button(text=str(i)))
Clock.schedule_interval(self.update_bg, 1.0/24.0) # schedule the image movement
return layout
def update_bg(self, dt):
self.bg.pos = (self.bg.pos[0] + 1, self.bg.pos[1] + 1)
MyApp().run()
This code just moves the image in a straight line, but you can improve that.
Related
I´m currently developing a software to process spectroscopic data using python. I´m using PyQt6 to design the gui and matplotlib to plot the data. To simplify the data selection I added a cursor widget from matplotlib to the axes. For improved performace I used the blitting mode.
Here´s a minimal code example:
# Import packages
from PyQt6.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.widgets import Cursor
from matplotlib.figure import Figure
import numpy as np
# Main window
class MainWindow(QMainWindow):
def __init__(self):
# Initialize QMainWindow
super().__init__()
# Get MplWidget
self.mpl_widget = MplWidget()
# Set MplWidget as central widget
self.setCentralWidget(self.mpl_widget)
# Create axes
self.ax = self.mpl_widget.canvas.figure.add_subplot(111)
# Reference Cursor
self.cursor = Cursor(self.ax, useblit=True)
# Plot some random stuff
x, y = np.random.rand(100), np.random.rand(100)
self.ax.scatter(x, y)
# Show app
self.show()
# MplCanvas
class MplWidget(QWidget):
def __init__(self, parent=None):
# Initialize QWidget
super().__init__(parent)
# Create canvas
self.canvas = FigureCanvas(Figure())
# Set constrained layout
self.canvas.figure.set_constrained_layout(True)
# Create layout
layout = QVBoxLayout()
# Add canvas
layout.addWidget(self.canvas)
# Set layout
self.setLayout(layout)
# Initialize app
app = QApplication([])
UI = MainWindow()
app.exec()
Generally everything works fine, the cursor appears when hovering the axis and dissapears when leaving it, but when the mouse enters the axis, the whole axis content moves a little bit. Sometimes the figure edges also change their colors, e.g. from black to gray.
Here are two gifs to explain the phenomenon:
Does anyone know how to solve this problem?
I'm developing a kivy app, for desktop, i separated some goals to archive, but struggling in the fist one, i need to drawn a image as i move the mouse when it's pressed, but right now, only show a image when is touch down event and at end of the move event
here is my code
import kivy
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.graphics import *
kv = """
<MainScreen>:
FloatLayout:
# on_touch_move: print_cordinates(root.touch)
# on_touch_move: add_images(root.touch)
on_touch_down: root.on_touch_down_2(args[1])
on_touch_move: root.on_touch_move_2(args[1])
<Image>:
size: self.texture_size
"""
brush_size = 10
class MainScreen(FloatLayout):
def on_touch_down_2(self, touch):
self.add_widget(Image(source='img/brush/square.png',
pos=(touch.x - 10/2, touch.y - 10/2)))
def on_touch_move_2(self, touch):
self.add_widget(Image(source='img/brush/square.png',
pos=(touch.x - 10/2, touch.y - 10/2)))
class MainApp(App):
def build(self):
return MainScreen()
MainApp().run()
the image i use is simple 10px x 10px green square(the color don't matter now)
I wrote this to display a button with given image
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class DatesPage(FloatLayout):
def __init__(self, **kwargs):
super(DatesPage, self).__init__(**kwargs)
self.add_butt = Button(background_normal='plus_butt.png', size_hint=(0.2, 0.1))
self.add_widget(self.add_butt)
class DiaryApp(App):
def build(self):
return DatesPage()
if __name__=='__main__':
DiaryApp().run()
How can i make sure that the image is displayed on the button
You can't get a window with this code. Because you are not calling DiaryApp class correctly.Add brackets after class name.
if __name__=='__main__':
DiaryApp().run()
my application is built from a .kv file.
I want to add buttons to a Screen from my python file.
class Profiles(Screen):
def suh(self):
for i in range(5):
self.add_widget(Button(text=i))
The suh function seem to have no effect. Is this because the function is called after the app is built? Is there a better way to do this?
Solution 1
Since Screen is a RelativeLayout, use on_pre_enter or on_enter to inovke method, suh(), plus add size and pos to Button widget.
Solution 2
Use a container e.g. BoxLayout or Gridayout on top of Screen.
Note
Whenever widget is added to a screen via on_pre_enter or on_enter, use on_pre_leave or on_leave to remove the widget. This is to prevent doubling your widget each time the screen is entered.
If the widgets are cleared too fast, use Clock.schedule_once with a time interval.
Note 1: Screen Events
Kivy Screen has the following events.
Kivy Screen » Events
on_pre_enter: ()
Event fired when the screen is about to be used: the entering
animation is started.
on_enter: ()
Event fired when the screen is displayed: the entering animation is
complete.
on_pre_leave: ()
Event fired when the screen is about to be removed: the leaving
animation is started.
on_leave: ()
Event fired when the screen is removed: the leaving animation is
finished.
Note 2: Screen is a RelativeLayout
Kivy Screen » RelativeLayout
Please note that by default, a Screen displays nothing: it’s just
a RelativeLayout. You need to use that class as a root widget for
your own screen, the best way being to subclass.
Warning
As Screen is a RelativeLayout, it is important to understand
the Common Pitfalls.
Example - Solution 1
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.lang import Builder
Builder.load_string("""
<Profiles>:
# on_pre_enter: self.suh()
on_enter: self.suh()
""")
class Profiles(Screen):
def suh(self):
for i in range(5):
self.add_widget(Button(text=str(i), size_hint=(0.1, 0.1), pos=(i*100, i*100)))
sm = ScreenManager()
sm.add_widget(Profiles(name='profiles'))
class TestApp(App):
def build(self):
return sm
if __name__ == "__main__":
TestApp().run()
Output - Solution 1
Example - Solution 2
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.lang import Builder
Builder.load_string("""
<Profiles>:
# on_pre_enter: self.suh()
on_enter: self.suh()
BoxLayout:
id: container
""")
class Profiles(Screen):
def suh(self):
for i in range(5):
self.ids.container.add_widget(Button(text=str(i), size_hint=(0.1, 0.1)))
sm = ScreenManager()
sm.add_widget(Profiles(name='profiles'))
class TestApp(App):
def build(self):
return sm
if __name__ == "__main__":
TestApp().run()
Output - Solution 2
Add the widgets through the App class in stead of the Screen. So you would have to create a function that creates the widgets, and in your build function, you would have to use Clock.schedule_once from the Clock module to run the other function. Example:
class TestApp(App):
def build(self):
Clock.schedule_once(self.yoyo, 0)
return MainWin()
def yoyo(self, *args):
for i in memu_acc:
self.root.ids["prof"].ids["pro"].add_widget(Button(text=i))
I was trying to make an window with two block with vertical box layout, with upper widget larger than lower one. But rather than that, the widgets are stacking on top of other at the bottom left corner, both being of same size.
Here is my code
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.codeinput import CodeInput
from kivy.core.window import Window
from kivy.uix.button import Button
Window.maximize()
class Editor(Widget):
def __init__(self, *arg, **kwarg):
super(Editor, self).__init__(*arg, **kwarg)
self.size_hint= (1, 0.8)
self.add_widget(Button(text= "1"))
class Output(Widget):
def __init__(self, *arg, **kwarg):
super(Output, self).__init__(*arg, **kwarg)
self.size_hint= (1, 0.2)
self.add_widget(Button(text= "2"))
class IDE(BoxLayout):
def __init__(self, *arg, **kwarg):
super(IDE, self).__init__(*arg, **kwarg)
self.orientation= "vertical"
box1= Editor()
self.add_widget(box1)
box2= Output()
self.add_widget(box2)
class MainApp(App):
def build(self):
return IDE()
if __name__=="__main__":
MainApp().run()
(The buttons are used in Output and Editor class just to check their respective parent widget position in resultant window)
Someone help me to figure out what am I doing wrong.
Your Editor and Output behave as expected, but are completely invisible. What you are seeing are their own child widgets, the two Buttons, each of which has no position or size applied to it and so takes the default of pos (0, 0) and size (100, 100).
Make the Editor and Output classes some kind of layout, e.g. FloatLayout, or simply remove them and add the Buttons directly to the BoxLayout.