I looked online and I am still stuck on how to bind 'on_node_expand' of TreeView to a function.
Is it possible to get the proper syntax?
def createSubNode(elemObj,designTree,parent=None):
if type(elemObj) is (designClass.PathGroup):
pass
else:
for (name, obj) in elemObj.items():
subroot = designTree.add_node(TreeViewLabel(text=name,font_size=12),parent)
subroot.fbind('on_node_expand', bind_object)
createSubNode(obj,designTree,subroot)
def bind_object(node):
print("testing")
if __name__ == '__main__':
from kivy.app import App
class TestApp(App):
def build(self):
designTree = TreeView(root_options=dict(text='Designs',font_size=12))
designTree.size_hint = (1,None)
designTree.bind(minimum_height=designTree.setter('height'))
createSubNode(dsgnManObj, designTree)
svRoot = ScrollView(size=(Window.width, Window.height))
svRoot.add_widget(designTree)
root = BoxLayout(orientation ='horizontal')
root.add_widget(svRoot)
return root
TestApp().run()
on_node_expand is an event on the TreeView, not on the node, so you shouldn't need to bind it on subroot objects, but only on the designTree object.
Related
when I press on a button i would like run a function everey minute.
I am using sched.schedulerbut my kivy app crash.
s.enter(60, 1, self.graph_data, (s,))
s.run()
Someone can help me ?
You can use the Clock object provided by kivy:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
class TestApp(App):
def build(self):
self.timer = None
btn1 = Button(text='Click to start')
btn1.bind(state=self.callbackStart)
btn2 = Button(text='Click to stop')
btn2.bind(state=self.callbackStop)
bl = BoxLayout()
bl.add_widget(btn1)
bl.add_widget(btn2)
return bl
def callbackStart(self, instance, value):
print('My button1 <%s> state is <%s>' % (instance, value))
if self.timer is not None:
Clock.unschedule(self.timer)
self.timer = Clock.schedule_interval(self.timedAction, 0.5)
def callbackStop(self, instance, value):
print('My button2 <%s> state is <%s>' % (instance, value))
Clock.unschedule(self.timer)
def timedAction(self, dt):
print("Repeat")
if __name__ == '__main__':
TestApp().run()
You should also be able to do something like this by using the threading build-in module with the Thread and Event objects.
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
import threading
class MyTimedAction(threading.Thread):
def __init__(self, event):
threading.Thread.__init__(self)
self.stopped = event
def run(self):
while not self.stopped.wait(0.5):
print("Periodic action performed")
class TestApp(App):
def build(self):
# Create the stopTimer event. Initialize as stopped.
self.stopTimer = threading.Event()
self.stopTimer.set()
btn1 = Button(text='Click to start')
btn1.bind(state=self.callbackStart)
btn2 = Button(text='Click to stop')
btn2.bind(state=self.callbackStop)
bl = BoxLayout()
bl.add_widget(btn1)
bl.add_widget(btn2)
return bl
def callbackStart(self, instance, value):
print('My button1 <%s> state is <%s>' % (instance, value))
if self.stopTimer.isSet(): # (Eventually) avoid duplicates if timer already started
self.stopTimer.clear()
thread = MyTimedAction(self.stopTimer)
thread.start()
def callbackStop(self, instance, value):
print('My button2 <%s> state is <%s>' % (instance, value))
self.stopTimer.set()
if __name__ == '__main__':
TestApp().run()
The examples above assumes that if the timer is already started it doesn't start again another timed job on a new thread.
The brief GUI code is just for demo purpose.
I am a complete noob on kivy (and object oriented approach in python programming) and I struggle trying to implement a simple file checker (I think I am missing a key element of kivy's logic). I want my app to indicate whether my file is a csv.
class CsvLoader(BoxLayout):
def __init__(self, **kwargs):
super(CsvLoader, self).__init__(**kwargs)
self.btn = Button(text='Please, Drag and Drop your CSV')
self.add_widget(self.btn)
csv = Window.bind(on_dropfile=self._on_file_drop) # this is the s*** part: how to get the file_path?
if str(csv).split('.')[-1] != "csv'" and csv != None:
self.clear_widgets()
btn_error = Button(text='Wrong file type')
self.add_widget(btn_error)
def _on_file_drop(self, window, file_path):
return file_path
class VisualizeMyCsv(App):
def build(self):
return CsvLoader()
if __name__ == '__main__':
VisualizeMyCsv().run()
What am I missing? Thanks
with the function bind you link an action, which in your case on_dropfile that triggers when you place a file, with a function that is called when the event is triggered, so the logic must be done within that method:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.core.window import Window
class CsvLoader(BoxLayout):
def __init__(self, **kwargs):
super(CsvLoader, self).__init__(**kwargs)
self.btn = Button(text='Please, Drag and Drop your CSV')
self.add_widget(self.btn)
Window.bind(on_dropfile=self._on_file_drop)
def _on_file_drop(self, window, file_path):
if str(file_path).split('.')[-1] == "csv'":
print(file_path)
# here you must indicate that it is a .csv file
else:
# it is not a .csv file
print("it is not a .csv file")
class VisualizeMyCsv(App):
def build(self):
return CsvLoader()
if __name__ == '__main__':
VisualizeMyCsv().run()
My question is similar to this one, except in one thing: what if classes widget1 and widget2 are big and I want to put them in two separate files. Problem gets in method Check(). So, is there a way to call self.parent().setCentralWidget(w2) even though widget2 isn't in the same file as widget1 (but they are both imported in main file containing MainWindow).
Code taken from How to set the central widget of existing MainWidnow in Python PyQt4?
class widget1(QtGui.QFrame):
def __init__(self,parent = None):
......
......
def Check(self):
if (condition):
w2=widget2(self)
self.parent().setCentralWidget(w2)
class widget2(QtGui.QFrame):
def __int__(self,parent = None):
.....
.....
class MainWindow(QtGui.QMainWindow):
def __init__(self,parent = None):
QtGui.QMainWindow.__init__(self,parent)
....
mywidgetone = widget1()
self.setCentralWidget(mywidgetone)
if __name__ == '__main__':
app = QtGui.QApplicaiton(sys.argv)
main = MainWindow()
main.show()
app.exec_()
I have a class to create a PyQt4 widget and another class to parse xml to get inputs. I want to create an UI dyanamically add buttons to the widget, reading from the xml(s) passed, which is not happening:
import sys
import xml.etree.ElementTree as ET
from PyQt4 import QtGui
ui = None
class userInterface(QtGui.QWidget):
def __init__(self):
super(userInterface, self).__init__()
def getFilesWindow(self):
self.filesSelected = []
fDialog = QtGui.QFileDialog.getOpenFileNames(self, 'Open file', '/Learning/Python/substance_XML_reading/', "SBS (*.sbs)")
for path in fDialog:
if path:self.filesSelected.append(path)
return self.filesSelected
class ParseXML():
def getXMLTags(self,fileList):
self.tags = []
if fileList:
print fileList
for eachFile in fileList:
fileToParse = ET.parse(eachFile)
root = fileToParse.getroot()
for child in root:
self.tags.append(child.tag)
return self.tags
def getSetUI(flist):
global ui
if flist:
tagsForBtns = ParseXML().getXMLTags(flist)
print tagsForBtns
for eachitem in tagsForBtns:
btn = QtGui.QPushButton(ui,eachitem)
def main():
app = QtGui.QApplication(sys.argv)
ui = userInterface()
fileListForUIGen = ui.getFilesWindow() # will return a list of files
getSetUI(fileListForUIGen) # Parses each file, gets tags, creates buttons and has to add to the current window..NOT WORKING
ui.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
In order to add buttons to a widget, you need to place them inside a QLayout
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
self.ui_lay = QtGui.QVBoxLayout()
self.setLayout(self.ui_lay)
def addButton(self, text):
btn = QtGui.QPushButton(text, self)
self.ui_lay.addWidget(btn)
...
for eachitem in tagsForBtns:
ui.addButton(eachitem)
Also, make sure to use global ui in your main() function if you're going to be doing it this way.
Personally, I don't see the reasons for splitting up all the function calls. I would just put them all in the UserInterface class.
Consider following code. I would like to update multiple widget instances when prefix changes. As it is the same for all the instances it seems efficient to store/update it only once on class level (so that when instance does not have its own self.prefix, it will automatically refer to class level prefix attribute)
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import StringProperty
import random
kivy_lang = '''
<MainWidget>:
Button:
id: my_button
text: 'increase indice'
<MyLabel>:
on_prefix: self.text = self.prefix +':'+ self.indice
on_indice: self.text = self.prefix +':'+ self.indice
'''
class MyLabel(Label):
prefix = StringProperty('1')
indice = StringProperty('0')
pass
class MainWidget(BoxLayout):
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
self.my_label1 = MyLabel()
self.my_label2 = MyLabel()
self.add_widget(self.my_label1)
self.add_widget(self.my_label2)
self.ids.my_button.bind(on_press=self.my_method)
def my_method(self,*args,**kwargs):
MyLabel.prefix = str(random.randint(0,9))
self.my_label1.indice = str(int(self.my_label1.indice) + 1)
# my_label2 would also be updated if its 'indice' got changed as below
# self.my_label2.indice = str(int(self.my_label2.indice) + 2)
class MyApp(App):
def build(self):
Builder.load_string(kivy_lang)
return MainWidget()
if __name__ == '__main__':
MyApp().run()
As from the python side this seems right, from Kivy side it looks like kivy has problem recognising when prefix got changed (my_label1 only gets updated because indice was also updated and on_indice is triggered).
Is there a way to get 'class level Property' prefix change to trigger on_prefix ?
I don't think this is possible directly, but you could mimic that functionality with AliasProperty and another property stored, say, on App. As long as the instance of MyLabel hasn't changed prefix, the value set for App is used (and automatically updated). Once prefix is set on an instance, _my_prefix is not None, and will be used to retrieve the value for prefix.
Change the <MyLabel> rule to
<MyLabel>:
_prefix: app.prefix
text: self.prefix +':'+ self.indice
And change the python code to
class MyLabel(Label):
indice = StringProperty('0')
_prefix = StringProperty('')
_my_prefix = StringProperty(None)
def get_prefix(self):
if self._my_prefix is None:
return self._prefix
else:
return self._my_prefix
def set_prefix(self, value):
self._my_prefix = value
prefix = AliasProperty(get_prefix, set_prefix, bind=('_prefix', '_my_prefix'))
[...]
def my_method(self,*args,**kwargs):
App.get_running_app().prefix = str(random.randint(0,9))
self.my_label1.indice = str(int(self.my_label1.indice) + 1)
if int(self.my_label1.indice) == 2:
self.my_label2.prefix = 'changed'
[...]
class MyApp(App):
prefix = StringProperty('1')