Does anyone know how to increase the linewidth of a MeshLinePlot in Kivy?
Thank you
Update
I got the answer from #Ikolim to work modifying the LinePLot function from kivy.graph
class LinePlot(Plot):
'''LinePlot draws using a standard Line object.
'''
'''Args:
line_width (float) - the width of the graph line
'''
def __init__(self, line_width=1, **kwargs):
self._line_width = line_width # instead of kwargs.get('line_width', 1)
super(LinePlot, self).__init__(**kwargs)
Base on the method __init__ for Kivy garden.graph at https://github.com/kivy-garden/garden.graph/blob/master/init.py, MeshLinePlot currently does not support line width. But LinePlot do support line width.
snippet
self.plot = LinePlot(line_width=4, color=[1, 0, 0, 1])
Example
main.py
import kivy
kivy.require("1.10.0")
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.garden.graph import Graph, MeshLinePlot, MeshStemPlot, LinePlot, SmoothLinePlot, ContourPlot
import matplotlib.pyplot as plt
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvas
from kivy.utils import get_color_from_hex as rgb
from numpy import sin
kv = """
<GraphCustom>:
<Test>:
orientation: 'vertical'
GraphCustom:
size: self.parent.size
pos: self.parent.pos
"""
Builder.load_string(kv)
class GraphCustom(Graph):
def __init__(self, **kwargs):
super(GraphCustom, self).__init__(**kwargs)
self.label_options = {'color': rgb('#FF0000'), 'bold': True}
self.background_color = rgb('f8f8f2')
self.tick_color = rgb('808080')
self.border_color = rgb('808080')
self.xlabel = 'X'
self.ylabel = 'Y'
self.x_ticks_minor = 5
self.x_ticks_major = 25
self.y_ticks_major = 1
self.y_grid_label = True
self.x_grid_label = True
self.padding = 5
self.x_grid = True
self.y_grid = True
self.xmin = -0
self.xmax = 100
self.ymin = -1
self.ymax = 1
self.stop = False
self.plot = LinePlot(line_width=4, color=[1, 0, 0, 1])
self.plot.points = [(x, sin(x / 10.)) for x in range(0, 101)]
self.add_plot(self.plot)
class Test(BoxLayout):
def __init__(self, *args, **kwargs):
super(Test, self).__init__(*args, **kwargs)
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
wid = FigureCanvas(fig1)
self.add_widget(wid)
class TestApp(App):
title = "Kivy Garden Graph - LinePlot Demo"
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
Output
Related
I am extremely new to kivy (and making apps in general), and I am having trouble switching between screens. I have watched several tutorials, but one of them uses a build method that returns a MDBoxLayout (CameraScreen), and the others use .kv files. I want to be able to switch between the two, or be able to reformat it so that I only use .kv files. Here is my code:
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.label import MDLabel
from kivy.uix.screenmanager import Screen
from kivy.uix.image import Image
from kivy.lang import Builder
from kivy.graphics.texture import Texture
from kivy.clock import Clock
import cv2
import pytesseract
class HomeScreen(Screen):
pass
class ManageDataScreen(Screen):
pass
class CameraScreen(MDApp):
def build(self):
layout = MDBoxLayout(orientation='vertical')
self.image = Image()
self.label = MDLabel()
layout.add_widget(self.image)
layout.add_widget(self.label)
self.save_img_button = MDRaisedButton(
text="Capture",
pos_hint={'center_x': .5, 'center_y': .5},
size_hint=(None, None))
self.save_img_button.bind(on_press=self.take_picture)
layout.add_widget(self.save_img_button)
self.capture = cv2.VideoCapture(0)
Clock.schedule_interval(self.load_video, 1.0/30.0)
return layout
def load_video(self, *args):
ret, frame = self.capture.read()
# Frame initialize
self.image_frame = frame
buffer = cv2.flip(frame, 0).tostring()
texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
texture.blit_buffer(buffer, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def take_picture(self, *args):
image_name = "img_TIMESTAMP.png"
img = cv2.cvtColor(self.image_frame, cv2.COLOR_BGR2GRAY)
img = cv2.GaussianBlur(img, (3, 3), 0)
img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
text_data = pytesseract.image_to_string(img, lang='eng', config="--psm 6")
self.label.text = text_data
cv2.imshow("cv2 final image", img)
cv2.imwrite(image_name, self.image_frame)
GUI = Builder.load_file("kv/main.kv")
class MainApp(MDApp):
def build(self):
return GUI
def change_screen(self, screen_name):
screen_manager = self.root.ids['screen_manager']
if screen_name == "camera_screen":
CameraScreen().run()
else:
screen_manager.current = screen_name
if __name__ == '__main__':
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
MainApp().run()
and here is my main.kv file
#:include kv/home_screen.kv
#:include kv/manage_data_screen.kv
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
ManageDataScreen:
name: "manage_data_screen"
id: manage_data_screen
I can see the effects from the CanvasWidget() class but not any effects from the MyApp() class. Why is it so
#Importing kivy module
import kivy
kivy.require("1.10.1")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
from kivy.uix.image import Image
from kivy.config import Config
#Creating the canvas
class CanvasWidget(Widget):
def __init__(self, **kwargs):
super(CanvasWidget, self).__init__(**kwargs)
with self.canvas:
Color(1, 1, 1, 1)
self.rect = Rectangle(pos = self.center, size = (self.width / 2., self.height / 2.))
self.bind(pos = self.update_rect, size = self.update_rect)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
class CanvasApp(App):
def build(self):
return CanvasWidget()
CanvasApp().run()
#Putting the image
Config.set("graphics", "resizable", True)
class MyApp(App):
def build(self):
self.img = Image(source = "voithos.jpg")
self.img.allow_stretch = True
self.img.keep_ratio = False
self.img.size_hint_x = 1
self.img.size_hint_y = 1
self.img.pos = (200, 100)
self.img.opacity = 1
s = Widget()
s.add_widget(self.img)
return s
MyApp().run()
This is the output i get
The line:
CanvasApp().run()
will not return until the CanvasApp is shut down. So nothing after that line will be executed until you kill CanvasApp.
I am trying to add 4 boxes in Relativelayout. Able to horizontally move only the box which gets added last in relativelayout. All other box gets strucked up when it is moved closer to its box which got added later. In given sample code below 'Actor 400' freely moves horizontally across the scene. 'Actor 300' able to move freely over 'Actor 200' and 'Actor 100'. But when I try to move Actor 300 above Actor 400, it get strucked and only after mouse moves beyond Actor 400, I am able to move.
Can I change the zindex of the widget dynamically when I touch it.
from kivy.app import App
from kivy.graphics import Line, Color
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
<Actor>:
canvas:
Color:
rgba: 0,1,0,.8
Rectangle:
id: _rect_
size: 100, 30
pos: 0, root.height - 30
Line:
points: 50, root.height - 30, 50, 20
width:2
Label:
id: _actr_lbl
text: 'Hello World'
color: 0,0,0,1
size_hint: None, None
size: 100, 30
pos: 0, root.height - 30
'''
Builder.load_string(KV)
class Actor(Scatter):
def __init__(self, **kwargs) :
Name = kwargs.pop('Name', 'Actor-NoName')
super(Actor, self).__init__(**kwargs)
self.Name = Name
self.ids._actr_lbl.text = Name
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.selected = True
self.pos_hint = {'top':1}
self.hint_diff_touch_to_x = self.right - touch.x
return True
return super(Actor, self).on_touch_down(touch)
def on_touch_move(self, touch):
if (self.selected) :
future_right = touch.x + self.hint_diff_touch_to_x
if (future_right <= self.parent.size[0]) and ((future_right - self.width) >= 0) :
self.right = future_right
self.pos_hint = {'top':1}
return True
return super(Actor, self).on_touch_move(touch)
def on_touch_up(self, touch):
if (self.selected) :
self.selected = False
self.hint_diff_touch_to_x = 0
self.iy = 0
return True
return super(Actor, self).on_touch_up(touch)
class MyPaintApp(App):
def build(self):
root = RelativeLayout()
(ix, iy) = (100,100)
clr = Color(0.2, 0.2, 1)
for ix in [100, 200, 300, 400 ] :
root.add_widget(Actor(Name = 'Actor ' + str(ix), pos=(ix,0), size_hint=(None,1)))
return root
if __name__ == '__main__':
Window.clearcolor = (1, 1, 1, 1)
MyPaintApp().run()
When one of your Actor Widgets is selected, you can move it to the top by removing it and re-adding it, like this:
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.selected = True
self.pos_hint = {'top':1}
self.hint_diff_touch_to_x = self.right - touch.x
parent = self.parent
# move this Actor to the front
parent.remove_widget(self)
parent.add_widget(self)
return True
return super(Actor, self).on_touch_down(touch)
I am trying to use DragBehavior to help in moving my custom widget across RelativeLayout. Find below sample code. Why my widget is not moving on Drag action please. For simplicity I had included only rectangle in my custom widget MyPaintWidget
from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
from kivy.graphics import Color, Rectangle
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def create_figure(self, **kwargs):
print ('position is {}'.format(self.pos))
print ('width Height {}'.format(self.to_parent(self.width, self.height)))
self.canvas.add(Rectangle(pos = self.pos, size = self.size))
return self
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget(pos_hint={"center_x": 0.5, 'center_y':0.5}, size_hint=(.2,.1))
parent.add_widget(self.painter.create_figure())
return parent
if __name__ == '__main__':
MyPaintApp().run()
The DragBehavior works by adjusting the pos of your MyPaintWidget, but you have set pos_hint on the MyPaintWidget. The pos_hint takes precedence over pos, so while the drag changes pos, it is ignored because there is a pos_hint. Also, the Rectangle that you draw in create_figure has its size and pos set when that method is called, and there is no mechanism to change it when the MyPaintWidget is moved. So, even if the Widget was being dragged, the Rectangle would not move.
Here is a version of your code with those problems corrected:
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
canvas:
Color:
rgba: 1,0,0,1
Rectangle:
pos: 0,0 # only do this for RelativeLayout
size: self.size
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget( pos=(240, 200), size_hint=(.2,.1))
parent.add_widget(self.painter)
return parent
if __name__ == '__main__':
MyPaintApp().run()
I collect experiencies with kivy and I have problem with using widget center. My problem shows next script:
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from functools import partial
from kivy.graphics import Color, Ellipse
class CanvasApp(App):
def draw(self,wid, color):
with wid.canvas:
Color(color[0], color[1], color[2])
Ellipse(size= (100, 100), pos=(wid.center_x, wid.center_y))
return
def painter(self, wid, *largs):
self.draw(wid, (0, 1, 0))
return
def build(self):
wid = Widget()
btn_painter = Button(text='Painter',
on_press=partial(self.painter, wid))
ctrl = BoxLayout(size_hint=(1, None), height=50)
ctrl.add_widget(btn_painter)
root = BoxLayout(orientation='vertical')
root.add_widget(wid)
root.add_widget(ctrl)
self.draw(wid, (1, 0, 0))
return root
if __name__ == '__main__':
CanvasApp().run()
I want to draw circle in the widget center (black rectangle) without using button painter. I don't able obtain actually center coordinates of center. Without using button are available only default center coordinates. How is OK process?
Thank you for answers.
The problem is that the wid size and position are not yet set inside the build() method. So you need to delay the call of draw() a bit. Try replacing the call to draw() with:
Clock.schedule_once(partial(self.draw, wid, (1,0,0)))
You will also need to change the draw() method slightly to handle the dt argument that gets added:
def draw(self,wid, color, *args):
I found solution of my problem. All explains follow script:
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
class DrawArea(FloatLayout):
def __init__(self, **kwargs):
# make sure we aren't overriding any important functionality
super(DrawArea, self).__init__(**kwargs)
self.rect1 = None
self.li_item = []
return
def clear_area(self):
for item in self.li_item:
self.canvas.remove(item)
self.li_item.clear()
def draw_rect(self):
with self.canvas:
Color(0, 1, 0, 1) # green; colors range from 0-1 not 0-255
self.rect1 = Rectangle(size=self.size, pos=self.pos)
Color(1, 0, 0, 1) # green; colors range from 0-1 not 0-255
width = self.width/2
height = self.height/2
pos_x = self.center_x -width/2
pos_y = self.center_y - height/2
self.rect2 = Rectangle(size=(width, height), pos=(pos_x, pos_y))
self.li_item.append(self.rect1)
self.li_item.append(self.rect2)
return
def draw_circle(self):
Color(1, 0, 0)
d = 120
with self.canvas:
Color(0, 1, 0)
self.circle1 = Ellipse(size = (d,d), pos = (self.center_x-d/2, self.center_y-d/2))
Color(0, 0, 1)
self.circle2 = Ellipse(size = (d/2,d/2), pos = (self.center_x - self.size[0]/2, self.center_y - self.size[1]/2))
self.li_item.append(self.circle1)
self.li_item.append(self.circle2)
return
class MainApp(App):
def build(self):
self.task = 'circle'
root = BoxLayout(orientation = 'vertical')
self.dra = DrawArea()
button = Button(text = 'painter',size_hint=(1, None), height=50, on_press = self.but_paint)
root.add_widget(self.dra)
root.add_widget(button)
if self.task == 'circle':
self.dra.bind(pos = self._update_circ, size = self._update_circ)
self.dra.draw_circle()
else:
self.dra.bind(size=self._update_rect, pos=self._update_rect)
self.dra.draw_rect()
return root
def but_paint(self, instance):
#self.dra.clear_area()
if self.task == 'circle':
self.task = 'rectangle'
else:
self.task = 'circle'
if self.task == 'circle':
self.dra.bind(pos = self._update_circ, size = self._update_circ)
self.dra.draw_circle()
else:
self.dra.bind(size=self._update_rect, pos=self._update_rect)
self.dra.draw_rect()
return
def _update_circ(self, instance, value):
d = 120
pos1_x = instance.center[0] -d/2
pos1_y = instance.center[1] -d/2
self.dra.circle1.pos = (pos1_x, pos1_y)
pos2_x = instance.center[0] - instance.size[0]/2
pos2_y = instance.center[1] - instance.size[1]/2
self.dra.circle2.pos = (pos2_x, pos2_y)
return
def _update_rect(self, instance, value):
if self.dra.rect1 != None:
self.dra.rect1.pos = instance.pos
self.dra.rect1.size = instance.size
width = instance.size[0]/2
height = instance.size[1]/2
pos_x = instance.center[0] -width/2
pos_y = instance.center[1] - height/2
self.dra.rect2.pos = (pos_x, pos_y)
self.dra.rect2.size = (width, height)
def draw_rect(self):
with self.dra.canvas.before:
Color(0, 1, 0, 1) # green; colors range from 0-1 not 0-255
self.rect1 = Rectangle(size=self.dra.size, pos=self.dra.pos)
Color(1, 0, 0, 1) # green; colors range from 0-1 not 0-255
width = self.width/2
height = self.height/2
self.rect2 = Rectangle(size=(width, height), pos=(self.center_x -width/2, self.center_y - height/2))
return
if __name__ == '__main__':
MainApp().run()