我正在使用Python-2.7
和kivy
我运行test.py
然后显示一个菜单Test
。当我点击它时,显示list
数据。
有人可以告诉我如何在列表中添加垂直scrollbar
。
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty, NumericProperty, DictProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
from kivy.clock import Clock
Window.size = (600, 325)
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
selected_row = NumericProperty(0)
def get_nodes(self):
nodes = self.get_selectable_nodes()
if self.nodes_order_reversed:
nodes = nodes[::-1]
if not nodes:
return None, None
selected = self.selected_nodes
if not selected: # nothing selected, select the first
self.select_node(nodes[0])
self.selected_row = 0
return None, None
if len(nodes) == 1: # the only selectable node is selected already
return None, None
last = nodes.index(selected[-1])
self.clear_selection()
return last, nodes
def select_next(self):
''' Select next row '''
last, nodes = self.get_nodes()
if not nodes:
return
if last == len(nodes) - 1:
self.select_node(nodes[0])
self.selected_row = nodes[0]
else:
self.select_node(nodes[last + 1])
self.selected_row = nodes[last + 1]
def select_previous(self):
''' Select previous row '''
last, nodes = self.get_nodes()
if not nodes:
return
if not last:
self.select_node(nodes[-1])
self.selected_row = nodes[-1]
else:
self.select_node(nodes[last - 1])
self.selected_row = nodes[last - 1]
def select_current(self):
''' Select current row '''
last, nodes = self.get_nodes()
if not nodes:
return
self.select_node(nodes[self.selected_row])
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Button '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
print("on_touch_down: self=", self)
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
class RV(BoxLayout):
data_items = ListProperty([])
row_data = DictProperty({})
col1_data = ListProperty([])
col2_data = ListProperty([])
col1_row_controller = ObjectProperty(None)
col2_row_controller = ObjectProperty(None)
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_states()
Clock.schedule_once(self.set_default_first_row, .0005)
self._request_keyboard()
def _request_keyboard(self):
self._keyboard = Window.request_keyboard(
self._keyboard_closed, self, 'text'
)
if self._keyboard.widget:
# If it exists, this widget is a VKeyboard object which you can use
# to change the keyboard layout.
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'down': # keycode[274, 'down'] pressed
# Respond to keyboard down arrow pressed
self.display_keystrokes(keyboard, keycode, text, modifiers)
self.col1_row_controller.select_next()
self.col2_row_controller.select_next()
elif keycode[1] == 'up': # keycode[273, 'up] pressed
# Respond to keyboard up arrow pressed
self.display_keystrokes(keyboard, keycode, text, modifiers)
self.col1_row_controller.select_previous()
self.col2_row_controller.select_previous()
# Keycode is composed of an integer + a string
# If we hit escape, release the keyboard
if keycode[1] == 'escape':
keyboard.release()
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def display_keystrokes(self, keyboard, keycode, text, modifiers):
print("\nThe key", keycode, "have been pressed")
print(" - text is %r" % text)
print(" - modifiers are %r" % modifiers)
def on_keyboard_select(self):
''' Respond to keyboard event to call Popup '''
# setup row data for Popup
self.row_data = self.col1_data[self.col1_row_controller.selected_row]
# call Popup
self.popup_callback()
def on_mouse_select(self, instance):
''' Respond to mouse event to call Popup '''
if (self.col1_row_controller.selected_row != instance.index
or self.col2_row_controller.selected_row != instance.index):
# Mouse clicked on row is not equal to current selected row
self.col1_row_controller.selected_row = instance.index
self.col2_row_controller.selected_row = instance.index
# Hightlight mouse clicked/selected row
self.col1_row_controller.select_current()
self.col2_row_controller.select_current()
# setup row data for Popup
# we can use either col1_data or col2_data because they are duplicate and each stores the same info
self.row_data = self.col1_data[instance.index]
# call Popup
self.popup_callback()
def popup_callback(self):
# enable keyboard request
self._request_keyboard()
def set_default_first_row(self, dt):
''' Set default first row as selected '''
self.col1_row_controller.select_next()
self.col2_row_controller.select_next()
def update(self):
self.col1_data = [{'text': str(x[0]), 'Id': str(x[0]), 'Name': x[1], 'key': 'Id', 'selectable': True}
for x in self.data_items]
self.col2_data = [{'text': x[1], 'Id': str(x[0]), 'Name': x[1], 'key': 'Name', 'selectable': True}
for x in self.data_items]
def get_states(self):
rows = [(1, 'abc'), (1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc'),(1, 'abc')]
i = 0
for row in rows:
self.data_items.append([row[0], row[1], i])
i += 1
print(self.data_items)
self.update()
class MainMenu(BoxLayout):
states_cities_or_areas = ObjectProperty(None)
rv = ObjectProperty(None)
def display_states(self):
self.remove_widgets()
self.rv = RV()
self.states_cities_or_areas.add_widget(self.rv)
def remove_widgets(self):
self.states_cities_or_areas.clear_widgets()
class TestApp(App):
title = "test"
def build(self):
return MainMenu()
if __name__ == '__main__':
TestApp().run()
#:kivy 1.10.0
<SelectableButton>:
canvas.before:
Color:
rgba: (0, 0.517, 0.705, 1) if self.selected else (0, 0.517, 0.705, 1)
Rectangle:
pos: self.pos
size: self.size
background_color: [1, 0, 0, 1] if self.selected else [1, 1, 1, 1] # dark red else dark grey
on_press: app.root.rv.on_mouse_select(self)
<RV>:
col1_row_controller: col1_row_controller
col2_row_controller: col2_row_controller
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 3
Label:
size_hint_x: .1
text: "Id"
Label:
size_hint_x: .5
text: "Name"
ScrollView:
id: kr_scroll
do_scroll_x: False
height: 2
BoxLayout:
RecycleView:
size_hint_x: .1
data: root.col1_data
viewclass: 'SelectableButton'
SelectableRecycleGridLayout:
id: col1_row_controller
key_selection: 'selectable'
cols: 1
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
RecycleView:
size_hint_x: .5
data: root.col2_data
viewclass: 'SelectableButton'
SelectableRecycleGridLayout:
id: col2_row_controller
key_selection: 'selectable'
cols: 1
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
<MenuButton@Button>:
text_size: self.size
valign: "middle"
padding_x: 5
size : (80,30)
size_hint : (None, None)
background_color: 90 , 90, 90, 90
background_normal: ''
color: 0, 0.517, 0.705, 1
border: (0, 10, 0, 0)
<MainMenu>:
states_cities_or_areas: states_cities_or_areas
BoxLayout:
orientation: 'vertical'
#spacing : 10
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
size_hint_y: 1
MenuButton:
id: btn
text: 'Test'
size : (60,30)
on_release: root.display_states()
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: (1,1,1)
Label:
size_hint_x: 45
BoxLayout:
id: states_cities_or_areas
size_hint_y: 10
Label:
size_hint_y: 1
答案 0 :(得分:4)
如何仅在customerId列和其他列中设置文本右对齐 应该保持对齐吗?
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
self.text_size = self.size
if index == rv.data[index]['range'][0]:
self.halign = 'right'
else:
self.halign = 'left'
可以垂直增长但以一定宽度包装文本的标签:
Label:
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
要在其X轴/水平和Y轴/垂直方向上滚动GridLayout,请将size_hint属性设置为(None,None)。
ScrollView - ScrollEffect, scroll_type, bar_width, etc.
默认情况下,ScrollView允许沿X轴和Y轴滚动。您可以通过将do_scroll_x或do_scroll_y属性设置为False来显式禁用轴上的滚动。
要在其Y轴/垂直方向上滚动GridLayout,请设置子项 宽度为ScrollView(size_hint_x = 1)的宽度,并设置 size_hint_y属性为无:
当滚动超出ScrollView的范围时,它会使用ScrollEffect来处理过度滚动。
scroll_type
设置用于滚动视图内容的滚动类型。 可用选项包括:['content'],['bars'],['bars','content']
['bars']通过拖动或滑动scoll bar来滚动内容。
cols_minimum
每列最小宽度的字典。字典键是 列号,例如0,1,2 ......
cols_minimum是DictProperty,默认为{}。
<RV>:
row_controller: row_controller
bar_width: 10
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
effect_cls: "ScrollEffect"
scroll_type: ['bars']
data: root.rv_data
viewclass: 'SelectableButton'
SelectableRecycleGridLayout:
id: row_controller
key_selection: 'selectable'
cols: root.total_col_headings
cols_minimum: root.cols_minimum
default_size: None, dp(26)
default_size_hint: 1, None
size_hint: None, None
height: self.minimum_height
width: self.minimum_width
orientation: 'vertical'
multiselect: True
touch_multiselect: True
以下示例演示:
chinook SQLite sample database
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty, NumericProperty, DictProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.recycleview import RecycleView
import sqlite3 as lite
import re
Window.size = (600, 325)
class PopupLabelCell(Label):
pass
class EditStatePopup(Popup):
def __init__(self, obj, **kwargs):
super(EditStatePopup, self).__init__(**kwargs)
self.populate_content(obj)
def populate_content(self, obj):
for x in range(len(obj.table_header.col_headings)):
self.container.add_widget(PopupLabelCell(text=obj.table_header.col_headings[x]))
textinput = TextInput(text=str(obj.row_data[x]))
if x == 0:
textinput.readonly = True
self.container.add_widget(textinput)
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
selected_row = NumericProperty(0)
obj = ObjectProperty(None)
def get_nodes(self):
nodes = self.get_selectable_nodes()
if self.nodes_order_reversed:
nodes = nodes[::-1]
if not nodes:
return None, None
selected = self.selected_nodes
if not selected: # nothing selected, select the first
self.selected_row = 0
self.select_row(nodes)
return None, None
if len(nodes) == 1: # the only selectable node is selected already
return None, None
last = nodes.index(selected[-1])
self.clear_selection()
return last, nodes
def select_next(self, obj):
''' Select next row '''
self.obj = obj
last, nodes = self.get_nodes()
if not nodes:
return
if last == len(nodes) - 1:
self.selected_row = nodes[0]
else:
self.selected_row = nodes[last + 1]
self.selected_row += self.obj.total_col_headings
self.select_row(nodes)
def select_previous(self, obj):
''' Select previous row '''
self.obj = obj
last, nodes = self.get_nodes()
if not nodes:
return
if not last:
self.selected_row = nodes[-1]
else:
self.selected_row = nodes[last - 1]
self.selected_row -= self.obj.total_col_headings
self.select_row(nodes)
def select_current(self, obj):
''' Select current row '''
self.obj = obj
last, nodes = self.get_nodes()
if not nodes:
return
self.select_row(nodes)
def select_row(self, nodes):
col = self.obj.rv_data[self.selected_row]['range']
for x in range(col[0], col[1] + 1):
self.select_node(nodes[x])
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Button '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
print("on_touch_down: self=", self)
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
self.text_size = self.size
if index == rv.data[index]['range'][0]:
self.halign = 'right'
else:
self.halign = 'left'
class HeaderCell(Label):
pass
class TableHeader(ScrollView):
"""Fixed table header that scrolls x with the data table"""
header = ObjectProperty(None)
col_headings = ListProperty([])
cols_minimum = DictProperty({})
def __init__(self, **kwargs):
super(TableHeader, self).__init__(**kwargs)
self.db = lite.connect('chinook.db')
self.db_cursor = self.db.cursor()
self.get_table_column_headings()
def get_table_column_headings(self):
self.db_cursor.execute("PRAGMA table_info(customers)")
col_headings = self.db_cursor.fetchall()
for col_heading in col_headings:
data_type = col_heading[2]
if data_type == "INTEGER":
self.cols_minimum[col_heading[0]] = 100
else:
self.cols_minimum[col_heading[0]] = int(re.findall(r'\d+', data_type).pop(0)) * 5
self.col_headings.append(col_heading[1])
self.header.add_widget(HeaderCell(text=col_heading[1], width=self.cols_minimum[col_heading[0]]))
class RV(RecycleView):
row_data = ()
rv_data = ListProperty([])
row_controller = ObjectProperty(None)
total_col_headings = NumericProperty(0)
cols_minimum = DictProperty({})
table_header = ObjectProperty(None)
def __init__(self, table_header, **kwargs):
super(RV, self).__init__(**kwargs)
self.table_header = table_header
self.total_col_headings = len(table_header.col_headings)
self.cols_minimum = table_header.cols_minimum
self.database_connection()
self.get_states()
Clock.schedule_once(self.set_default_first_row, .0005)
self._request_keyboard()
def database_connection(self):
self.db = lite.connect('chinook.db')
self.db_cursor = self.db.cursor()
def _request_keyboard(self):
self._keyboard = Window.request_keyboard(
self._keyboard_closed, self, 'text'
)
if self._keyboard.widget:
# If it exists, this widget is a VKeyboard object which you can use
# to change the keyboard layout.
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'down': # keycode[274, 'down'] pressed
# Respond to keyboard down arrow pressed
self.display_keystrokes(keyboard, keycode, text, modifiers)
self.row_controller.select_next(self)
elif keycode[1] == 'up': # keycode[273, 'up] pressed
# Respond to keyboard up arrow pressed
self.display_keystrokes(keyboard, keycode, text, modifiers)
self.row_controller.select_previous(self)
elif len(modifiers) > 0 and modifiers[0] == 'ctrl' and text == 'e': # ctrl + e pressed
# Respond to keyboard ctrl + e pressed, and call Popup
self.display_keystrokes(keyboard, keycode, text, modifiers)
self.on_keyboard_select()
# Keycode is composed of an integer + a string
# If we hit escape, release the keyboard
if keycode[1] == 'escape':
keyboard.release()
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def display_keystrokes(self, keyboard, keycode, text, modifiers):
print("\nThe key", keycode, "have been pressed")
print(" - text is %r" % text)
print(" - modifiers are %r" % modifiers)
def on_keyboard_select(self):
''' Respond to keyboard event to call Popup '''
# setup row data for Popup
self.setup_row_data(self.rv_data[self.row_controller.selected_row]['Index'])
# call Popup
self.popup_callback()
def on_mouse_select(self, instance):
''' Respond to mouse event to call Popup '''
if self.row_controller.selected_row != instance.index:
# Mouse clicked on row is not equal to current selected row
self.row_controller.selected_row = instance.index
# Hightlight mouse clicked/selected row
self.row_controller.select_current(self)
# setup row data for Popup
self.setup_row_data(self.rv_data[instance.index]['Index'])
# call Popup
self.popup_callback()
# enable keyboard request
self._request_keyboard()
def setup_row_data(self, value):
self.db_cursor.execute("SELECT * FROM customers WHERE CustomerId=?", value)
self.row_data = self.db_cursor.fetchone()
def popup_callback(self):
''' Instantiate and Open Popup '''
popup = EditStatePopup(self)
popup.open()
def set_default_first_row(self, dt):
''' Set default first row as selected '''
self.row_controller.select_next(self)
def get_states(self):
self.db_cursor.execute("SELECT * FROM customers ORDER BY CustomerId ASC")
rows = self.db_cursor.fetchall()
data = []
low = 0
high = self.total_col_headings - 1
for row in rows:
for i in range(len(row)):
data.append([row[i], row[0], [low, high]])
low += self.total_col_headings
high += self.total_col_headings
self.rv_data = [{'text': str(x[0]), 'Index': str(x[1]), 'range': x[2], 'selectable': True} for x in data]
class Table(BoxLayout):
rv = ObjectProperty(None)
def __init__(self, **kwargs):
super(Table, self).__init__(**kwargs)
self.orientation = "vertical"
self.header = TableHeader()
self.rv = RV(self.header)
self.rv.fbind('scroll_x', self.scroll_with_header)
self.add_widget(self.header)
self.add_widget(self.rv)
def scroll_with_header(self, obj, value):
self.header.scroll_x = value
class MainMenu(BoxLayout):
states_cities_or_areas = ObjectProperty(None)
table = ObjectProperty(None)
def display_states(self):
self.remove_widgets()
self.table = Table()
self.states_cities_or_areas.add_widget(self.table)
def remove_widgets(self):
self.states_cities_or_areas.clear_widgets()
class TestApp(App):
title = "test"
def build(self):
return MainMenu()
if __name__ == '__main__':
TestApp().run()
#:kivy 1.10.0
<PopupLabelCell>
size_hint: (None, None)
height: 30
text_size: self.size
halign: "left"
valign: "middle"
<EditStatePopup>:
container: container
size_hint: None, None
size: 400, 275
title_size: 20
# title_font: "Verdana"
auto_dismiss: False
BoxLayout:
orientation: "vertical"
ScrollView:
bar_width: 10
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
effect_cls: "ScrollEffect"
scroll_type: ['bars']
size_hint: (1, None)
GridLayout:
id: container
cols: 2
row_default_height: 30
cols_minimum: {0: 100, 1: 300}
# spacing: 10, 10
# padding: 20, 20
size_hint: (None, None)
height: self.minimum_height
BoxLayout:
Button:
size_hint: 1, 0.2
text: "Save Changes"
on_release:
root.dismiss()
Button:
size_hint: 1, 0.2
text: "Cancel Changes"
on_release: root.dismiss()
<SelectableButton>:
canvas.before:
Color:
rgba: (0, 0.517, 0.705, 1) if self.selected else (0, 0.517, 0.705, 1)
Rectangle:
pos: self.pos
size: self.size
background_color: [1, 0, 0, 1] if self.selected else [1, 1, 1, 1] # dark red else dark grey
on_press: app.root.table.rv.on_mouse_select(self)
<HeaderCell>
size_hint: (None, None)
height: 25
text_size: self.size
halign: "left"
valign: "middle"
background_disabled_normal: ''
disabled_color: (1, 1, 1, 1)
canvas.before:
Color:
rgba: 1, 0.502, 0, 1
Rectangle:
pos: self.pos
size: self.size
<TableHeader>:
header: header
bar_width: 0
do_scroll: False
size_hint: (1, None)
height: 25
effect_cls: "ScrollEffect"
GridLayout:
id: header
rows: 1
cols_minimum: root.cols_minimum
size_hint: (None, None)
width: self.minimum_width
height: self.minimum_height
<RV>:
row_controller: row_controller
bar_width: 10
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
effect_cls: "ScrollEffect"
scroll_type: ['bars']
data: root.rv_data
viewclass: 'SelectableButton'
SelectableRecycleGridLayout:
id: row_controller
key_selection: 'selectable'
cols: root.total_col_headings
cols_minimum: root.cols_minimum
default_size: None, dp(26)
default_size_hint: 1, None
size_hint: None, None
height: self.minimum_height
width: self.minimum_width
orientation: 'vertical'
multiselect: True
touch_multiselect: True
<MenuButton@Button>:
text_size: self.size
valign: "middle"
padding_x: 5
size : (80,30)
size_hint : (None, None)
background_color: 90 , 90, 90, 90
background_normal: ''
color: 0, 0.517, 0.705, 1
border: (0, 10, 0, 0)
<MainMenu>:
states_cities_or_areas: states_cities_or_areas
BoxLayout:
orientation: 'vertical'
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
size_hint_y: 1
MenuButton:
id: btn
text: 'Test'
size : (60,30)
on_release: root.display_states()
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: (1,1,1)
Label:
size_hint_x: 45
BoxLayout:
id: states_cities_or_areas
size_hint_y: 10
Label:
size_hint_y: 1
答案 1 :(得分:2)
诀窍是更改viewclass
以满足您的需求。您曾使用过Button
,但在我看来,这只是为了尝试同步您的两个RecycleViews
,所以我使用了标签。但是,由于我创建的TwoLabelListItem只是一个BoxLayout,因此您可以轻松地操作它以完全满足您的需求。
为viewclass
创建适当的布局后,您只需要确保传递给它的数据已正确应用。在这里,我将数据应用于两个不同的标签。我还会抓取数据,因为您的笔记表明您希望能够为不同的订单项设置弹出窗口。
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
self.ids.label1.text = data['text1']
self.ids.label2.text = data['text2']
self.data = data
因此,在选择元素时,保留数据将允许您引用它。
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if self.selected:
print(self.data)
向RecycleView添加滚动条只是在kv中告诉它你想要使用它。如果您希望用户能够移动内容,或者只是将内容添加到列表中。 ['bars', 'content']
RecycleView:
scroll_type: ['bars']
bar_width: 25
...
testApp.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty, NumericProperty, DictProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
from kivy.clock import Clock
Window.size = (600, 325)
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
selected_row = NumericProperty(0)
def get_nodes(self):
nodes = self.get_selectable_nodes()
if self.nodes_order_reversed:
nodes = nodes[::-1]
if not nodes:
return None, None
selected = self.selected_nodes
if not selected: # nothing selected, select the first
self.select_node(nodes[0])
self.selected_row = 0
return None, None
if len(nodes) == 1: # the only selectable node is selected already
return None, None
last = nodes.index(selected[-1])
self.clear_selection()
return last, nodes
def select_next(self):
''' Select next row '''
last, nodes = self.get_nodes()
if not nodes:
return
if last == len(nodes) - 1:
self.select_node(nodes[0])
self.selected_row = nodes[0]
else:
self.select_node(nodes[last + 1])
self.selected_row = nodes[last + 1]
def select_previous(self):
''' Select previous row '''
last, nodes = self.get_nodes()
if not nodes:
return
if not last:
self.select_node(nodes[-1])
self.selected_row = nodes[-1]
else:
self.select_node(nodes[last - 1])
self.selected_row = nodes[last - 1]
def select_current(self):
''' Select current row '''
last, nodes = self.get_nodes()
if not nodes:
return
self.select_node(nodes[self.selected_row])
class TwoLabelListItem(RecycleDataViewBehavior, BoxLayout):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
self.ids.label1.text = data['text1']
self.ids.label2.text = data['text2']
self.data = data
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(TwoLabelListItem, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if self.selected:
print(self.data)
class RV(BoxLayout):
data_items = ListProperty([])
row_data = DictProperty({})
data = ListProperty([])
col_row_controller = ObjectProperty(None)
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_states()
Clock.schedule_once(self.set_default_first_row, .0005)
self._request_keyboard()
def _request_keyboard(self):
self._keyboard = Window.request_keyboard(
self._keyboard_closed, self, 'text'
)
if self._keyboard.widget:
# If it exists, this widget is a VKeyboard object which you can use
# to change the keyboard layout.
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'down': # keycode[274, 'down'] pressed
# Respond to keyboard down arrow pressed
self.display_keystrokes(keyboard, keycode, text, modifiers)
self.col_row_controller.select_next()
elif keycode[1] == 'up': # keycode[273, 'up] pressed
# Respond to keyboard up arrow pressed
self.display_keystrokes(keyboard, keycode, text, modifiers)
self.col_row_controller.select_previous()
# Keycode is composed of an integer + a string
# If we hit escape, release the keyboard
if keycode[1] == 'escape':
keyboard.release()
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def display_keystrokes(self, keyboard, keycode, text, modifiers):
print("\nThe key", keycode, "have been pressed")
print(" - text is %r" % text)
print(" - modifiers are %r" % modifiers)
def on_keyboard_select(self):
''' Respond to keyboard event to call Popup '''
# setup row data for Popup
self.row_data = self.data[self.col_row_controller.selected_row]
# call Popup
self.popup_callback()
def on_mouse_select(self, instance):
''' Respond to mouse event to call Popup '''
if (self.col_row_controller.selected_row != instance.index):
# Mouse clicked on row is not equal to current selected row
self.col_row_controller.selected_row = instance.index
# Hightlight mouse clicked/selected row
self.col_row_controller.select_current()
# setup row data for Popup
self.row_data = self.data[instance.index]
# call Popup
self.popup_callback()
def popup_callback(self):
# enable keyboard request
self._request_keyboard()
def set_default_first_row(self, dt):
''' Set default first row as selected '''
self.col_row_controller.select_next()
def update(self):
self.data = [{'text1': str(x[0]), 'text2': x[1], 'Id': str(x[0]), 'Name': x[1], 'key': 'Id', 'selectable': True}
for x in self.data_items]
def get_states(self):
rows = [(x, 'abc') for x in range(25)]
i = 0
for row in rows:
self.data_items.append([row[0], row[1], i])
i += 1
print(self.data_items)
self.update()
class MainMenu(BoxLayout):
states_cities_or_areas = ObjectProperty(None)
rv = ObjectProperty(None)
def display_states(self):
self.remove_widgets()
self.rv = RV()
self.states_cities_or_areas.add_widget(self.rv)
def remove_widgets(self):
self.states_cities_or_areas.clear_widgets()
class TestApp(App):
title = "test"
def build(self):
return MainMenu()
if __name__ == '__main__':
TestApp().run()
test.kv
#:kivy 1.10.0
<TwoLabelListItem>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
Label:
id: label1
Label:
id: label2
<RV>:
col_row_controller: col_row_controller
RecycleView:
scroll_type: ['bars']
bar_width: 25
size_hint_x: .1
data: root.data
viewclass: 'TwoLabelListItem'
SelectableRecycleGridLayout:
padding: 0,0,25,0
id: col_row_controller
key_selection: 'selectable'
cols: 1
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: True
<MenuButton@Button>:
text_size: self.size
valign: "middle"
padding_x: 5
size : (80,30)
size_hint : (None, None)
background_color: 90 , 90, 90, 90
background_normal: ''
color: 0, 0.517, 0.705, 1
border: (0, 10, 0, 0)
<MainMenu>:
states_cities_or_areas: states_cities_or_areas
BoxLayout:
orientation: 'vertical'
#spacing : 10
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
size_hint_y: 1
MenuButton:
id: btn
text: 'Test'
size : (60,30)
on_release: root.display_states()
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: (1,1,1)
Label:
size_hint_x: 45
BoxLayout:
id: states_cities_or_areas
size_hint_y: 10
Label:
size_hint_y: 1