Error on ScrollView+StackLayout when binding minimum_height on Kivy - python

I'm new to Kivy and I am trying to create a scroll view based the official ScrollView example on Kivy docs.
I'm using the Kivy portable package for Windows with Python version 3.3.3.
When i try to run the code below with the layout.bind line uncommented i got repeated lines of the following error:
Warning, too much iteration done before the next frame. Check your code, or increase the Clock.max_iteration attribute
When i comment the layout.bind line I get a normal startup with the buttons i added where i would expect them, but the scroll doesn't work.
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
class Example(App):
def build(self):
layout = StackLayout(size_hint_y=None)
# If i uncomment this line, i got the error:
# layout.bind(minimum_height=layout.setter('height'))
for i in range(30):
btn = Button(text=str(i), size_hint=(.33,.8))
layout.add_widget(btn)
root = ScrollView(size_hint=(None,None), size=(400, 400))
root.add_widget(layout)
return root
if __name__ == '__main__':
Example().run()
The question is why the scroll doesn't work? and why the layout.bind is causing an error.
How should i do to have the same visual and the scroll working on x axis without the error?
I made this piece of code as close as possible to the Kivy official example.

This is happening because you're creating an infinite loop. The size of each Button is set based on the size of the StackLayout. This causes the StackLayout to increase in size, causing the Button sizes to be recalculated, causing the StackLayout to increase in size, ad infinitum.
You're telling Kivy that you want the Button size based on the StackLayout size, while also wanting the StackLayout size based on the combined Buttons size.
To fix this, you need to specify a real size instead of using size_hint:
btn = Button(text=str(i), size_hint=(None, None), size=(100, 100))

Related

Kivy button example closes immediately when testing

I'm trying to learn Kivy using their examples, however I'm having an issue. I'm using their button doc example:
from kivy.uix.button import Button
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
btn1 = Button(text='Hello world 1')
btn1.bind(on_press=callback)
btn2 = Button(text='Hello world 2')
btn2.bind(on_press=callback)
However, the program runs and immediately closes. I assumed maybe its tkinter, where the program runs on a constant loop and you need to add something at the end so it doesn't close, but I couldn't find anything on their docs about that.
To reiterate, I don't get any errors, the file just runs, I get a very brief pop up, and then it ends. I don't get an interface.
Firstly, kivy need to loop for control all own functions. So we need a App class and have to return our layouts directly or layouts under Screen Manager. In Kivy-Button documentation, Kivy shows you only related part. So there is a no any App class or loop for control.So program runs and closes immediately because app class doesn't loop window.
If you're beginner and trying to learn kivy from documentation, you need to figure how Kivy actually works and how documentation explain things. I'm sharing this code below for you, you need to understand add-remove widgets ,set layouts,... in kivy from documentations or search for full-code examples not part.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class TestLayout(BoxLayout):
def __init__(self, **kwargs):
super(TestLayout, self).__init__(**kwargs)
self.orientation = 'vertical'
but1 = Button(text='Button1')
self.add_widget(but1)
but2 = Button(text='Button2')
self.add_widget(but2)
class MyApp(App):
def build(self):
return TestLayout()
if __name__ == '__main__':
MyApp().run()
When you understand how it works, you should start to use Screen Manager for easily create pages, send-get values (and many things) for your applications.I hope these helps you at the beginning. Good luck.

PyQt4 ScrollArea widget issues: Scrollbar not working correctly

I'm writing a program in python 2.7 with PyQt4 and i'm having the following problem with the scrollArea widget. As seen in the images below, the scrollbar is not working correctly. Its not moving the graph at all. The scrollbar in the middle is showing the same thing as in the original position.
scroll bar at original position
scroll bar almost in the middle
I've already try the program in others computers with differents SO and got the same result, which means the problem is the code. The program is too long so i'll just show where the scroll area widget is being called.
class graphWidget(QtGui.QWidget):
def __init__(self):
super(graphWidget, self).__init__()
self.graph=CreateGraphFromInputs.createGraph()
self.slider=QtGui.QSlider(QtCore.Qt.Vertical)
self.setup()
def setup(self):
#slider setup#
self.slider.setMinimum(1)
self.slider.setMaximum(20)
self.slider.setValue(1)
self.slider.setTickPosition(QtGui.QSlider.TicksBelow)
self.slider.setTickInterval(1)
self.slider.valueChanged.connect(self.sliderValueChange)
self.zoomLabel=QtGui.QLabel(str(self.slider.value())+"x")
grid=QtGui.QGridLayout()
grid.addWidget(QtGui.QLabel(""),0,0)
grid.addWidget(QtGui.QLabel("Zoom"),1,1)
grid.addWidget(self.slider,2,1)
grid.addWidget(self.zoomLabel,3,1)
grid.addWidget(QtGui.QLabel(""),4,0)
grid.setRowMinimumHeight(0,55)
grid.setRowMinimumHeight(4,55)
grid.setSpacing(0)
#slider setup#
#scrollArea setup#
scrollArea=QtGui.QScrollArea()
scrollArea.setWidgetResizable(False)
scrollArea.setWidget(self.graph)
scrollArea.setMaximumHeight(270)
scrollArea.setMinimumWidth(600)
#scrollarea setup#
#layout setup
hbox = QtGui.QHBoxLayout()
hbox.addStretch(0)
hbox.setSpacing(5)
hbox.addWidget(scrollArea)
hbox.addLayout(grid)
self.setLayout(hbox)
#layout setup#
def sliderValueChange(self):
if self.graph.lista!=[]:
self.graph.setSize(50+(len(self.graph.lista)/float(self.slider.value())))
self.graph.setSizeDivision(1/float(self.slider.value()))
self.graph.setZoom(50*self.slider.value())
self.zoomLabel.setText(str(self.slider.value())+"x")
self.graph.repaint()
The graph widget is resizable but if i set the line bellow to true, the scroll bar won't work at all.
scrollArea.setWidgetResizable(False)
I've already checked others questions with this problem but did not work for me. So, setting the widget resizable to false makes the scroll bar work, except for the related problem i'm having.
The following code can reproduce the scroll area problem mentioned.
from PyQt4 import QtGui
import sys
import GraphWidget
#graph data
graphList=[1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0]*50
def main():
app = QtGui.QApplication(sys.argv)
graphW=GraphWidget.graphWidget()
graphW.graph.setLista(graphList)
graphW.graph.setSize(50+len(graphList))
graphW.graph.repaint()
graphW.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The codes and the file in theses links below are needed to make it work.
CreateGraphFromInputs.py
DrawGraph.py
ListCleaning.py
ConfigGraphFile.py
graphWidget.py
configGraphFile
Just found out the solution. The problem was that the graph was not being resized correctly. In the DrawGraph.py file, i was setting the geometry instead of resizing it.
So, i just changed this code line
self.setGeometry(0,0,self.size,self.height)
to this one
self.resize(self.size,self.height)
and now its working fine.

ScrollView in a popup appearing at the bottom and too small?

I am trying to add a ScrollView to a popup in kivy but the scroll view only seems to be taking up a small portion of the popup and down the bottom for some reason.
The only thing I can think to do is to set the height of the scroll view explicitly but I don't know how to do this because it needs to scale with the window size and the popup.height seems to include the header bar.
Here is my code:
scroll = ScrollView(size_hint=(1, None))
popup = Popup(title='Thanks Stack Overflow!', size_hint=(0.9, 0.9), content=scroll)
box = BoxLayout(orientation='vertical', size_hint=(1, None))
scroll.add_widget(box)
box.bind(minimum_height=box.setter('height'))
for i in range(1000):
box.add_widget(Button(text='test button {}'.format(i), size_hint=(1, None)))
and this is the result I get:
It is scrollable and works fine it is just that it doesn't take up the correct space.
EDIT: So for now I am setting the height manually to be with respect to the popup size as it seems like the popup title doesn't change too much. But I would still like a better method if available.
Here is my updated python code file
from kivy.uix.popup import Popup
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.boxlayout import BoxLayout
from kivy.app import runTouchApp
scroll = ScrollView()
popup = Popup(title='Thanks Stack Overflow!', size_hint=(0.9, 0.9), content=scroll)
grid = GridLayout(cols=1, size_hint=(1, None))
scroll.add_widget(grid)
grid.bind(minimum_height=grid.setter('height'))
for i in range(1000):
grid.add_widget(Button(text='test button {}'.format(i), size_hint=(1, None)))
box = BoxLayout()
box.add_widget(popup)
runTouchApp(box)
I removed the size_hint(1, None) for the ScrollView. Now they are the default value (1,1). Furthermore it always makes sense to use GridLayout with ScrollView and not BoxLayout. BoxLayout just takes the size of its parent. You need something bigger than its parent, so that it is scrollable.
Sidenote: Take a look at kivy files, I think they make it a lot easier.
Also, I would have set the height of the Buttons. I don't know where they get the height if you don't set them. I did not add that, since I wanted to keep my solution as close as possible to your code.

Kivy: Add ScreenManager to window not as root?

I'm using Kivy and I'm trying to setup a ScreenManager, but I don't want that ScreenManager to be the root widget in my window. Here's a test code snippet that demonstrates what I'm trying to do. (This code demonstrates my problem.)
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
class MyRootWidget(Widget):
def __init__(self, **kwargs):
super(MyRootWidget, self).__init__(**kwargs)
self.screen_manager = ScreenManager()
self.add_widget(self.screen_manager)
# self.add_widget(Label(text='label direct'))
class MyApp(App):
def build(self):
root = MyRootWidget()
new_screen = Screen(name='foo')
new_screen.add_widget(Label(text='foo screen'))
root.screen_manager.add_widget(new_screen)
root.screen_manager.current = 'foo'
for x in root.walk():
print x
return root
if __name__ == '__main__':
MyApp().run()
When I run this code, the window is blank, though I would expect that it would show the text "foo screen"?
The print of the walk() command shows that the root widget contains the screenmanager, my screen, and the label, like this:
<__main__.MyRootWidget object at 0x109d59c80>
<kivy.uix.screenmanager.ScreenManager object at 0x109eb4ae0>
<Screen name='foo'>
<kivy.uix.label.Label object at 0x109ecd390>
So that's working as I would expect.
If I uncomment the line which adds the label widget directly to the root widget, that label shows up as expected.
Also if I change the MyApp.build() method so that it returns new_screen instead of returning root, it also works in that I see the label "foo screen" on the display.
BTW, the reason I want to not have the screen manager be the root widget is because I want to be able to print messages (almost like popups) on the screen in front of whichever screen is active (even if screens are in the process of transitioning), so I was thinking I would need the screen manager not to be root in order to do that?
Also my ultimate goal is to have multiple "quadrants" in the window, each with its own screen manager, so I was thinking I needed to make sure I can show screen managers that are not the root widget in order to do this.
So if there's a better way for me to do this, please let me know. Also I do not want to use .kv files for this as I want to set this entire environment up programmatically based on other config options (which I've left out of this example.)
Overall though I wonder if anyone knows why this code doesn't work?
Thanks!
Brian
The problem is you are sticking your ScreenManager in a generic Widget. If you put it in a Layout, it will display properly, ie:
class MyRootWidget(BoxLayout):
There are several layouts available: http://kivy.org/docs/gettingstarted/layouts.html

Changing image in widget - Kivy - Python

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
class Imglayout(FloatLayout):
def __init__(self,**args):
super(Imglayout,self).__init__(**args)
with self.canvas.before:
Color(0,0,0,0)
self.rect=Rectangle(size=self.size,pos=self.pos)
self.bind(size=self.updates,pos=self.updates)
def updates(self,instance,value):
self.rect.size=instance.size
self.rect.pos=instance.pos
class MainTApp(App):
im=Image(source='img1.jpg')
def build(self):
root = BoxLayout(orientation='vertical')
c = Imglayout()
root.add_widget(c)
self.im.keep_ratio= False
self.im.allow_stretch = True
cat=Button(text="Categories",size_hint=(1,.07))
cat.bind(on_press=self.callback)
c.add_widget(self.im)
root.add_widget(cat);
return root
def callback(self,value):
self.im=Image(source='img2.jpg')
if __name__ == '__main__':
MainTApp().run()
What i am trying to do here, is change the image first loaded during object creation, which is shown when the app starts, and then change it when the button cat is pressed. I am trying it to do this way but it isnt happening. I would eventually want it to change with a swipe gesture.(with a little bit of swipe animation like it happens in the phone
what i am trying to build is a slideshow, which will change image in t seconds, unless swiped, and then when a new image comes the timer resets.
When the category button is pressed the image will not be there and a list of categories to select from. and when an item from the list is touched the images from that list will be displayed on the screen.
And at the end when everything has been done i would want to make it such that it automatically detects categories(based on directories in a specified location.) and then all the images will be available to it.(that is not telling it explicitly how many images and what images.)
But, i am not able to do the first thing, so i would really like some help on that. And maybe a few pointers on how to achieve the other things as well.
def callback(self,value):
self.im=Image(source='img2.jpg')
Your problem is in the definition of the callback. You create a new image with the new source, but you don't do anything with it, like adding it to your widget tree.
What you really want to do is modify the source of the existing image:
def callback(self, instance, value):
self.im.source = 'img2.jpg'
This will immediately replace the source of the existing image, which will immediately update in your gui. Note that I also added an instance parameter, which will be passed to the callback so you need to catch it or you'll crash with the wrong number of arguments.
Also, I'd define your image inside build rather than as a class level variable. I'm not sure if the current way will actually cause you problems, but it could in some circumstances.

Categories

Resources