I am trying to put an icon to the right hand side of a text list item, but this code below is giving me an error AttributeError: 'super' object has no attribute '__getattr__'
at this line: items.add_widget(icon).
Here's what I want it to look like:
List item with icon
Here's my code. It can be copied, and run as-is.
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivymd.uix.button import MDFlatButton
from kivymd.uix.list import OneLineIconListItem, IconRightWidget, MDList
from kivymd.uix.dialog import MDDialog
KV = '''
<Content>
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "400dp"
ScrollView:
MDList:
id: Mcontainer
MDFloatLayout:
'''
class Content(BoxLayout):
pass
class Example(MDApp):
def on_start(self):
Mcontent=Content()
for x in range(0,7):
icon = IconRightWidget(icon="lock")
items = OneLineIconListItem(text="This is a test")
items.add_widget(icon)
Mcontent.ids.Mcontainer.add_widget(items)
self.MSetFileOptionsdialog = MDDialog(type="custom",content_cls=Mcontent,)
self.MSetFileOptionsdialog.open()
def build(self):
return Builder.load_string(KV)
Example().run()
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivymd.uix.list import OneLineAvatarIconListItem
from kivymd.uix.dialog import MDDialog
KV = '''
<Item>
_txt_left_pad: "12dp"
IconRightWidget:
icon: root.icon
<Content>
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "400dp"
ScrollView:
MDList:
id: Mcontainer
MDFloatLayout:
'''
class Item(OneLineAvatarIconListItem):
icon = StringProperty()
class Content(BoxLayout):
pass
class Example(MDApp):
def on_start(self):
Mcontent = Content()
for x in range(0, 7):
items = Item(text="This is a test", icon="lock")
Mcontent.ids.Mcontainer.add_widget(items)
self.MSetFileOptionsdialog = MDDialog(type="custom", content_cls=Mcontent)
self.MSetFileOptionsdialog.open()
def build(self):
return Builder.load_string(KV)
Example().run()
When you want to add widget to an Id from your load_string or .kv file, you can just use the following; this is to be done in the .py file. There are limitations if screens are involved.
self.root.ids.name_of_the_id_referenced_container_widget_or_layout.add_widget(the one you already have in your.kv file)
I have several screens run by ScreenManager, and on one I have an image that is loaded with an image taken from a URL (for weather). My issue is, I can't seem to quite get the URL & image updated periodically:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
import pyowm
class Weather(Screen):
addr = StringProperty()
def wx_forecast(self):
owm = pyowm.OWM('******************')
observation = owm.weather_at_place('London,uk')
w = observation.get_weather()
i = w.get_weather_icon_name()
addr = str("http://openweathermap.org/img/w/" + i + ".png")
print(addr)
return addr
def update(self):
Clock.schedule_interval(self.ids.wxlabel.source, 45)
#def update(self):
#self.wx_forecast()
#Clock.schedule_interval(lambda dt: self.ids.wxlabel, 30)
class ScreenManagement(ScreenManager):
pass
smsettings = Builder.load_string( """
ScreenManagement:
Weather:
<Weather>:
name: 'weather'
FloatLayout:
BoxLayout:
AsyncImage:
id: wxlabel
source: root.wx_forecast()
BoxLayout:
size_hint_y: None
height: 30
Button:
text: 'Change Location'
on_press:
Button:
text: 'More Details'
on_release:
""" )
class HomeUtilities(App):
def build(self):
return smsettings
if __name__=='__main__':
HomeUtilities().run()
Where the def update is where I am having issues; I have tried calling the function wx_forecast again, even trying another function outside the Weather class, but all it says is str: has no attribute 'wx_forecast'
So I believe I am on the right lines? Or I could be completely mistaken, but nothing I try seems to work. Even putting self.ids.wxlabel.source.sunny.png brings up str: has no attribute image but the file is there.
I was working with ma project XYZ
and I got stuck in extracting text in from the source
gifts
I want to extrack the href as content
I tried this
from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
from XYZ.items import XYZ
class MySpider(BaseSpider):
name = "main"
allowed_domains = ["XYZ"]
start_urls = ["XYZ"]
def parse(self, response):
hxs = HtmlXPathSelector(response)
titles = hxs.select("//a[#data-tracking-id='mdd-heading']")
items = []
for titles in titles:
item = XYZ()
item ["title"] = titles.select("text()").extract()
item ["link"] = titles.select("#href").extract()
items.append(item)
print "www.xyz.com"+str(item["link"])
return items
and the output was
www.xyz.com[u'/gifts']
I was expecting output as
www.xyz.com/gifts
What i did wrong.... ?
According to the documentation for Selector's extract():
extract()
Serialize and return the matched nodes as a list of unicode
strings. Percent encoded content is unquoted.
So, extract() returns a list and you need the first item from it. Use item['link'][0].
Also, there are other problems in the code:
for titles in titles loop doesn't make sense, you need a separate loop variable
HtmlXPathSelector is deprecated, use Selector
use urljoin() to join the parts of a url
Here's the complete code with fixes and other improvements:
from urlparse import urljoin
from scrapy.spider import BaseSpider
from scrapy.selector import Selector
from XYZ.items import XYZ
class MySpider(BaseSpider):
name = "main"
allowed_domains = ["XYZ"]
start_urls = ["XYZ"]
def parse(self, response):
titles = response.xpath("//a[#data-tracking-id='mdd-heading']")
for title in titles:
item = XYZ()
item ["title"] = title.xpath("text()").extract()[0]
item ["link"] = title.xpath("#href").extract()[0]
print urljoin("www.xyz.com", item["link"])
yield item
I'm scraping few items from this site, but it grabs items only from the first product and doesn't loop further. I know I'm doing simple stupid mistake, but if you can just point out where I got this wrong, I'll appreciate it.
Here is the spider:
from scrapy.item import Item, Field
from scrapy.spider import BaseSpider
from scrapy.selector import Selector
import re
from zoomer.items import ZoomerItem
class ZoomSpider(BaseSpider):
name = "zoomSp"
allowed_domains = ["zoomer.ge"]
start_urls = [
"http://zoomer.ge/index.php?cid=35&act=search&category=1&search_type=mobile"
]
def parse(self, response):
sel = Selector(response)
titles = sel.xpath('//div[#class="productContainer"]/div[5]')
items = []
for t in titles:
item = ZoomerItem()
item["brand"] = t.xpath('//div[#class="productListContainer"]/div[3]/text()').re('^([\w, ]+)')
item["price"] = t.xpath('//div[#class="productListContainer"]/div[4]/text()').extract()[0].strip()
item["model"] = t.xpath('//div[#class="productListContainer"]/div[3]/text()').re('\s+(.*)$')[0].strip()
items.append(item)
return(items)
P.S. Also can't get regex for "brand" string to get only the first word "Blackberry" from the string:
"BlackBerry P9981 Porsche Design".
The <div/> element with the class productContainer is just a container and only appears one time, thus it is not repeating. The repeating element which you want to iterate over is the one with the class productListContainer.
def parse(self, response):
sel = Selector(response)
titles = sel.xpath('//div[#class="productContainer"]/div[5]/div[#class="productListContainer"]')
items = []
for t in titles:
item = ZoomerItem()
item["brand"] = t.xpath('div[3]/text()').re('^([\w\-]+)')
item["price"] = t.xpath('div[#class="productListPrice"]/div/text()').extract()
item["model"] = t.xpath('div[3]/text()').re('\s+(.*)$')[0].strip()
items.append(item)
items.append(item)
return(items)
This function is untested as I am not a python guy, so you might have to fiddle around a bit.
I'm working on a kivy app that pulls data from an sqlite3 database and populates a TreeView with it. The TreeView becomes too large to fit on my screen when I expand a few of the groups so I want to put it inside a ScrollView so I can still scroll down and see the items that have gone off the bottom of my screen. I can get a basic ScrollView to work, but when I put my TreeView inside it there is no scrolling and the top part of my TreeView is off the top of my screen.
I have trimmed down the code into this working example of the problem that runs without a .kv file:
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.treeview import TreeView, TreeViewNode
from kivy.uix.treeview import TreeViewLabel
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.button import Button
class TreeViewButton(Button, TreeViewNode):
pass
modGroups = [u'Fruit', u'Fruit', u'Meat', u'Dairy', u'Dairy', u'Fruit']
modItems = [u'Apple', u'Pear', u'Spam', u'Egg', u'Milk', u'Banana']
modDict = dict()
modDictUnique = dict()
def populate_tree_view(tv):
modDict = zip(modGroups, modItems)
print modGroups
print modItems
for k, v in modDict:
if k not in modDictUnique:
modDictUnique[k] = [v]
else:
modDictUnique[k].append(v)
sortedGroups = modDictUnique.keys()
sortedGroups.sort()
#print modItems
#print modDictUnique
n = tv.add_node(TreeViewLabel(text='Food', is_open=True))
for group in sortedGroups:
g = tv.add_node(TreeViewLabel(text='%s' % group), n)
for item in modDictUnique[group]:
tv.add_node(TreeViewButton(text='%s' % item), g)
class POSFMApp(App):
def build(self):
layout = GridLayout(cols=1, spacing=50, size_hint_y=None,width=800)
layout.bind(minimum_height=layout.setter('height'))
#for i in range(30):
# btn = Button(text=str(i), size=(480, 40),
# size_hint=(None, None))
# layout.add_widget(btn)
tv = TreeView(root_options=dict(text='Tree One'), hide_root=True, indent_level=4, minimum_height=5000)
populate_tree_view(tv)
layout.add_widget(tv)
root = ScrollView(size_hint=(None, None), size=(800, 700))
root.center = Window.center
root.add_widget(layout)
return root
if __name__ == '__main__':
POSFMApp().run()
In my actual app, modGroups and modItems are populated from an sqlite3 database, but this example presents the problem without having to mess around with sqlite3. I put in the (commented out) lines:
#for i in range(30):
# btn = Button(text=str(i), size=(480, 40),
# size_hint=(None, None))
# layout.add_widget(btn)
from this kivy ScrollView example to show that if I uncomment these lines and comment out the three lines about my TreeView
tv = TreeView(root_options=dict(text='Tree One'), hide_root=True, indent_level=4, minimum_height=5000)
populate_tree_view(tv)
layout.add_widget(tv)
Then I can get a working ScrollView with a scroll bar on the right as expected when I use my mouse's scroll wheel.
My best guess is that the TreeView doesn't tell the ScrollView how long it is vertically so the ScrollView doesn't realize it needs to scroll on the y-axis. That's just a guess, though.
How can I get a TreeView to work inside a ScrollView so I can scroll (especially on the y-axis) through my TreeView?
a) Using a GridLayout just for one child, pease don't. I'll asume that it's a left over from when/if you add more children to it.
b) Documentation of TreeView states that it has minimun_height property which indicates the minimum width/height needed to hold all it's children. The Treeview does not change it's height on it's own depending on the no of children. You should update (in this case) TreeViews height to it's minimum_height... tv.bind(minimum_height=tv.setter('height'))
c) taking into account the information provided in the points above, you can just do::
tv = TreeView(root_options=dict(text='Tree One'), hide_root=True, indent_level=4)
tv.size_hint = 1, None
tv.bind(minimum_height = tv.setter('height'))
populate_tree_view(tv)
root = ScrollView(pos = (0, 0))
root.add_widget(tv)
Here is the entire code including these changes so one can just copy and paste the code to a .py file and run it.
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.treeview import TreeView, TreeViewNode
from kivy.uix.treeview import TreeViewLabel
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.button import Button
class TreeViewButton(Button, TreeViewNode):
pass
modGroups = [u'Fruit', u'Fruit', u'Meat', u'Dairy', u'Dairy', u'Fruit']
modItems = [u'Apple', u'Pear', u'Spam', u'Egg', u'Milk', u'Banana']
modDict = dict()
modDictUnique = dict()
def populate_tree_view(tv):
modDict = zip(modGroups, modItems)
print modGroups
print modItems
for k, v in modDict:
if k not in modDictUnique:
modDictUnique[k] = [v]
else:
modDictUnique[k].append(v)
sortedGroups = modDictUnique.keys()
sortedGroups.sort()
#print modItems
#print modDictUnique
n = tv.add_node(TreeViewLabel(text='Food', is_open=True))
for group in sortedGroups:
g = tv.add_node(TreeViewLabel(text='%s' % group), n)
for item in modDictUnique[group]:
tv.add_node(TreeViewButton(text='%s' % item), g)
class POSFMApp(App):
def build(self):
#for i in range(30):
# btn = Button(text=str(i), size=(480, 40),
# size_hint=(None, None))
# layout.add_widget(btn)
tv = TreeView(root_options=dict(text='Tree One'), hide_root=True, indent_level=4)
tv.size_hint = 1, None
tv.bind(minimum_height = tv.setter('height'))
populate_tree_view(tv)
root = ScrollView(pos = (0, 0))
root.add_widget(tv)
return root
if __name__ == '__main__':
POSFMApp().run()
Add hight to TreeView then it will scroll. Like this.
ScrollView:
id: kr_scroll
do_scroll_x: False
TreeView:
id: trvMenu
root_options: { 'text': 'Home', 'font_size': 15}
hide_root: False
indent_level: 4
size_hint_y: None
height: self.parent.height*2