每当我尝试使用添加股票按钮 运行 Body() class 时,它都会显示错误 "Exception: ScrollView accept only one widget"
Whenever I try to run Body() class using Add Stock button, it shows the error "Exception: ScrollView accept only one widget"
每当我尝试 运行 Body() class 使用 Add Stock 按钮时,它会显示错误“异常:ScrollView 只接受一个小部件”,之前 运行 没问题,但现在我将一些文本输入和按钮从 kivy 替换为 KivyMD。但是还没有接触到 Body() class。什么都没有改变
.py 文件
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
import sys, time, threading
from kivy.uix.screenmanager import ScreenManager, Screen
from datetime import datetime
import pandas_datareader.data as web
import pandas as pd
from kivymd.uix.screen import Screen
from kivymd.uix.list import MDList,ThreeLineListItem,ThreeLineAvatarIconListItem
from kivymd.uix.list import IconLeftWidget,ImageLeftWidget
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
import csv
from os import path
from kivy.uix.image import Image
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import NumericProperty, ListProperty, BooleanProperty, ObjectProperty, StringProperty
from kivy.uix.recycleview import RecycleView
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
import re
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
import pandas as pd
from kivy.clock import Clock
from functools import partial
from kivymd.uix.dialog import MDDialog
from kivymd.uix.textfield import MDTextField
from kivy.core.window import Window
# Window.size = (300,500)
username =''
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
txt_input = ObjectProperty(None)
stock_name = ObjectProperty(None)
stock_symbol = ObjectProperty(None)
purchase_price = ObjectProperty(None)
stop_loss = ObjectProperty(None)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, 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 is_selected:
# App.get_running_app().root.widget_1.ids.txt_input1.text = str(rv.data[index].get("text"))
xx =str(rv.data[index].get("text"))
if (xx.find('(NSI)') != -1):
x,y = xx.split(" (NSI)")
add_sym = '.NS'
else:
x,y = xx.split(" (BSE)")
add_sym = '.BO'
print(xx)
print(x)
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text = x
f = pd.read_csv("Stock Tickers.csv", encoding="ISO-8859-1", engine='python')
fl = len(f.index)
file = pd.DataFrame(f, columns=['Symbols', 'Name', 'Exchange'])
for i in range(fl):
for index in range(1):
columnSeriesObj_sym = file.iloc[:, 0]
columnSeriesObj1 = file.iloc[:, 1]
columnSeriesObj_ex = file.iloc[:, 2]
before_sym,b = columnSeriesObj_sym.values[i].split('.')
if columnSeriesObj1.values[i] == App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text:
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text = before_sym + add_sym
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
class DropDownWidget(GridLayout):
txt_input = ObjectProperty()
rv = ObjectProperty()
stock_name = ObjectProperty(None)
stock_symbol = ObjectProperty(None)
purchase_price = ObjectProperty(None)
stop_loss = ObjectProperty(None)
def btn_input(self):
end = datetime.today().date()
start = end
print("Stock Name:", App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text,
"Stock Symbol:", App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text)
print("Purchase Price:", App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text,
"Stop Loss(%):", App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text)
# write data to csv file
file_name = username + "_stoploss.csv"
if path.exists(file_name):
with open(file_name, "a+", newline='')as newFile:
fieldnames = ["Stock Name", "Stock Symbol", "Purchase Price", "Stop Loss(%)"]
newFileWriter = csv.DictWriter(newFile, fieldnames=fieldnames)
newFileWriter.writerow({"Stock Name": App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text,
"Stock Symbol": App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text,
"Purchase Price": App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text,
"Stop Loss(%)": App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text})
else:
myFile = open(file_name, 'w+',newline='')
myData = [["Stock Name", "Stock Symbol", "Purchase Price", "Stop Loss(%)"],
[App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text,
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text,
App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text,
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text]]
with myFile:
writer = csv.writer(myFile)
writer.writerows(myData)
df = web.DataReader(App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text, 'yahoo', start, end)
print(df.tail())
App.get_running_app().root.get_screen('body_screen').widget_1.ids.txt_input.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text = ""
class MyTextInput(TextInput):
txt_input = ObjectProperty(None)
flt_list = ObjectProperty()
word_list = ListProperty()
stock_name = ObjectProperty(None)
stock_symbol = ObjectProperty(None)
purchase_price = ObjectProperty(None)
stop_loss = ObjectProperty(None)
# this is the variable storing the number to which the look-up will start
starting_no = NumericProperty()
suggestion_text = ''
def __init__(self, **kwargs):
super(MyTextInput, self).__init__(**kwargs)
def on_text(self, instance, value):
# find all the occurrence of the word
self.parent.ids.rv.data = []
if len(value) != 0:
matches = [word for word in self.word_list if word.lower().find(value.lower()) != -1]
# display the data in the recycleview
display_data = []
for i in matches:
display_data.append({'text': i})
self.parent.ids.rv.data = display_data
# ensure the size is okay
if len(matches) <= 10:
self.parent.height = (50 + (len(matches) * 20))
else:
self.parent.height = 240
def keyboard_on_key_down(self, window, keycode, text, modifiers):
if self.suggestion_text and keycode[1] == 'tab':
self.insert_text(self.suggestion_text + ' ')
return True
return super(MyTextInput, self).keyboard_on_key_down(window, keycode, text, modifiers)
class Body(Screen):
def on_pre_enter (self, **kwargs):
super(Body, self).__init__(**kwargs)
f = pd.read_csv("Stock Tickers.csv", encoding = "ISO-8859-1", engine='python')
fl = len(f.index)
file = pd.DataFrame(f, columns=['Symbols', 'Name', 'Exchange'])
wl = []
for i in range(fl):
for index in range(1):
columnSeriesObj = file.iloc[:, 1]
self.columnSeriesObj_ex = file.iloc[:, 2]
wl.append(columnSeriesObj.values[i] + " (" + self.columnSeriesObj_ex.values[i] + ")")
tp = tuple(wl)
print("File is loaded")
self.widget_1 = DropDownWidget()
self.widget_1.ids.txt_input.word_list = wl
self.widget_1.ids.txt_input.starting_no = 3
self.add_widget(self.widget_1)
class Signin(Screen):
user_name = ObjectProperty(None)
def check(self,email):
regex = '^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'
if (re.search(regex, email)):
return True
else:
return False
def btn(self):
global username
un = self.user_name.text
class Option(Screen):
def back(self):
MDApp.get_running_app().root.transition.direction = 'right'
MDApp.get_running_app().root.current = 'signin_screen'
sm = ScreenManager()
sm.add_widget(Signin(name='signin_screen'))
sm.add_widget(Option(name='option_screen'))
sm.add_widget(Body(name='body_screen'))
class run1(MDApp):
def build(self):
kv = Builder.load_file("run1.kv")
return kv
if __name__ == "__main__":
run1().run()
.kv 文件
ScreenManager:
Signin:
Option:
Body:
ListApp:
<Body>:
name: 'body_screen'
canvas.before:
Color:
rgba: 188/255, 143/255, 145/255, 1
Rectangle:
pos: self.pos
size: self.size
<DropDownWidget>:
cols:2
id: DropDownWidget
canvas:
Color:
rgba:(1, 1, 1, 1)
size_hint:(0.5,1)
pos_hint :{'center_x':.43, 'center_y':.23}
row_force_default : True
row_default_height : 30
col_force_default : True
col_default_width : 250
spacing: '20dp'
stock_name: stock_name
stock_symbol: stock_symbol
purchase_price: purchase_price
stop_loss: stop_loss
txt_input: txt_input
rv: rv
MDToolbar:
title:'Add Stock'
type: "top"
pos_hint: {'top':1.0}
left_action_items: [["menu", lambda x: x]]
md_bg_color:98/255,0,238/255,1
elevation:8
MyTextInput:
id: txt_input
RV:
id: rv
size_hint: 5.0 ,.1
Label:
text: "Stock Name: "
TextInput:
id: stock_name
readonly: True
multiline:False
Label:
text: "Stock Symbol: "
TextInput:
id: stock_symbol
readonly: True
multiline:False
Label:
text: "Purchase Price: "
TextInput:
id: purchase_price
input_filter: 'int'
multiline:False
Label:
text: "Stop Loss(%): "
TextInput:
id: stop_loss
input_filter: 'int'
multiline:False
Button:
text:"Submit"
on_press: root.btn_input()
Button:
text:"Back"
on_press: root.parent.manager.current = 'option_screen'
<MyTextInput>:
id: MyTextInput
readonly: False
multiline: False
<SelectableLabel>:
id: SelectableLabel
# Draw a background to indicate selection
canvas.after:
Color:
rgba: (1, 1, 1, 1) if self.selected else (1, 0, 1, 1)
Rectangle:
# pos: self.pos
size: self.size
<RV>:
canvas:
Color:
rgba: 0,0,0,.2
Line:
rectangle: self.x +8 , self.y, self.width - 2, self.height -2
bar_width: 20
scroll_type:['bars']
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
cols:1
default_size: None, dp(20)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
multiselect: False
<Signin>:
name: 'signin_screen'
user_name: user_name
# canvas:
# Color:
# rgba: 255/255,105/255,180/255,1
# Rectangle:
# size: self.size
# pos: self.pos
# spacing: '15dp'
orientation:'vertical'
MDToolbar:
title:'SignIn'
type: "top"
pos_hint: {'top':1.0}
left_action_items: [["menu", lambda x: x]]
md_bg_color:98/255,0,238/255,1
elevation:8
MDTextField:
id: user_name
hint_text:'Email'
icon_right: "email"
mode:'rectangle'
required: True
helper_text_mode: "on_error"
helper_text: "Enter text"
# fill_color: 1, 1, 1, .5
halign:'center'
helper_text_mode:'on_focus'
pos_hint: {'center_x': .5, 'center_y': .5}
size_hint_x: None
width:300
# color_mode: 'custom'
# line_color_focus: 98/255,0,238/255,1
MDRoundFlatIconButton:
icon: "login"
text:"Submit"
font_size: 20
pos_hint: {'center_x': .5, 'center_y': .4}
halign:'center'
custom_color:0,0,0,1
# size_hint: .5 ,.3
background_normal: ''
# background_color: (255/255, 153/255, 71/255, 1)
on_press :
root.manager.transition.direction = 'left'
root.manager.current = 'option_screen'
<Option>:
canvas:
Color:
rgba: 138/255,43/255,226/255,.4
Rectangle:
size: self.size
pos: self.pos
name: 'option_screen'
orientation:'vertical'
spacing: '20dp'
MDToolbar:
title:'Choose Option'
type: "top"
md_bg_color:229/255,33/255,101/255,1
left_action_items: [["back button.png", lambda x: root.back()]]
pos_hint: {'top':1.0}
elevation:8
MDCard:
md_bg_color:138/255,43/255,226/255,1
orientation:'vertical'
size_hint:None,None
pos_hint: {'center_x': .5, 'center_y': .6}
height:150
width:150
elevation:8
MDIconButton:
md_bg_color:138/255,43/255,226/255,1
icon: "Add Stock.png"
user_font_size: "100dp"
halign:'center'
pos_hint: {'center_x': .5, 'center_y': .5}
on_press : root.manager.current = 'body_screen'
MDLabel:
text:'Add Stock'
font_size: "20dp"
halign:'center'
MDCard:
md_bg_color:138/255,43/255,226/255,1
orientation:'vertical'
size_hint:None,None
pos_hint: {'center_x': .5, 'center_y': .3}
height:150
width:150
elevation:8
MDIconButton:
icon: "Check Stoploss.png"
user_font_size: "100dp"
halign:'center'
pos_hint: {'center_x': .5, 'center_y': .5}
on_press : root.manager.current = 'Stoploss_ip'
MDLabel:
text:'Check Stoploss'
font_size: "20dp"
halign:'center'
显示如下错误..
错误
"D:\Stoploss Calculator\venv\Scripts\python.exe" "D:/Stoploss Calculator/run1.py"
[INFO ] [Logger ] Record log in C:\Users\Rushi Dada\.kivy\logs\kivy_20-12-25_64.txt
[INFO ] [deps ] Successfully imported "kivy_deps.angle" 0.3.0
[INFO ] [deps ] Successfully imported "kivy_deps.glew" 0.3.0
[INFO ] [deps ] Successfully imported "kivy_deps.sdl2" 0.3.1
[INFO ] [Kivy ] v2.0.0
[INFO ] [Kivy ] Installed at "D:\Stoploss Calculator\venv\lib\site-packages\kivy\__init__.py"
[INFO ] [Python ] v3.9.1 (tags/v3.9.1:1e5d33e, Dec 7 2020, 17:08:21) [MSC v.1927 64 bit (AMD64)]
[INFO ] [Python ] Interpreter at "D:\Stoploss Calculator\venv\Scripts\python.exe"
[INFO ] [Factory ] 186 symbols loaded
[INFO ] [KivyMD ] v0.104.1
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[INFO ] [Window ] Provider: sdl2
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] GLEW initialization succeeded
[INFO ] [GL ] Backend used <glew>
[INFO ] [GL ] OpenGL version <b'4.0.0 - Build 10.18.10.5161'>
[INFO ] [GL ] OpenGL vendor <b'Intel'>
[INFO ] [GL ] OpenGL renderer <b'Intel(R) HD Graphics 4000'>
[INFO ] [GL ] OpenGL parsed version: 4, 0
[INFO ] [GL ] Shading version <b'4.00 - Build 10.18.10.5161'>
[INFO ] [GL ] Texture max size <16384>
[INFO ] [GL ] Texture max units <16>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [Text ] Provider: sdl2
[INFO ] [GL ] NPOT texture support is available
[WARNING] [Lang ] The file D:\Stoploss Calculator\run1.kv is loaded multiples times, you might have unwanted behaviors.
[INFO ] [Base ] Start application main loop
File is loaded
[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "D:\Stoploss Calculator\run1.py", line 425, in <module>
run1().run()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\app.py", line 950, in run
runTouchApp()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 582, in runTouchApp
EventLoop.mainloop()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 347, in mainloop
self.idle()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 391, in idle
self.dispatch_input()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 342, in dispatch_input
post_dispatch_input(*pop(0))
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 248, in post_dispatch_input
listener.dispatch('on_motion', etype, me)
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\core\window\__init__.py", line 1412, in on_motion
self.dispatch('on_touch_down', me)
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\core\window\__init__.py", line 1428, in on_touch_down
if w.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\screenmanager.py", line 1198, in on_touch_down
return super(ScreenManager, self).on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\relativelayout.py", line 297, in on_touch_down
ret = super(RelativeLayout, self).on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\behaviors\button.py", line 138, in on_touch_down
if super(ButtonBehavior, self).on_touch_down(touch):
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivymd\uix\behaviors\ripplebehavior.py", line 231, in on_touch_down
return super().on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivymd\uix\button.py", line 961, in on_touch_down
return super().on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\behaviors\button.py", line 151, in on_touch_down
self.dispatch('on_press')
File "kivy\_event.pyx", line 705, in kivy._event.EventDispatcher.dispatch
File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1132, in kivy._event.EventObservers._dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 57, in custom_callback
exec(__kvlang__.co_value, idmap)
File "D:\Stoploss Calculator\run1.kv", line 240, in <module>
on_press : root.manager.current = 'body_screen'
File "kivy\properties.pyx", line 498, in kivy.properties.Property.__set__
File "kivy\properties.pyx", line 545, in kivy.properties.Property.set
File "kivy\properties.pyx", line 600, in kivy.properties.Property.dispatch
File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1154, in kivy._event.EventObservers._dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\screenmanager.py", line 1056, in on_current
self.transition.start(self)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\screenmanager.py", line 377, in start
self.screen_in.dispatch('on_pre_enter')
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\run1.py", line 243, in on_pre_enter
self.widget_1 = DropDownWidget()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\gridlayout.py", line 279, in __init__
super(GridLayout, self).__init__(**kwargs)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 359, in __init__
self.apply_class_lang_rules(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 463, in apply_class_lang_rules
Builder.apply(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 541, in apply
self._apply_rule(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 661, in _apply_rule
child.apply_class_lang_rules(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 463, in apply_class_lang_rules
Builder.apply(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 541, in apply
self._apply_rule(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 660, in _apply_rule
widget.add_widget(child)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\recycleview\__init__.py", line 466, in add_widget
super(RecycleView, self).add_widget(widget, *largs)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\scrollview.py", line 1152, in add_widget
raise Exception('ScrollView accept only one widget')
Exception: ScrollView accept only one widget
Process finished with exit code 1
我解决了这个问题。
问题出在 KV 文件名中。这是 运行 多次。并且多次加载同一个 kv 文件会导致意外结果,我看到了一个这样的结果
我的 .py 文件名为 run1.py,KV 文件名为 run1.kv 如您所见,我正在调用 kv = Builder.load_file("run1.kv ") 在 class run1(MDApp) 中。它正在调用 kv 文件,并且 kv 文件也被 .py 自动调用,因为它找到了相同的名称。
所以我用 stopl.kv 重命名了 kv 文件,它解决了问题..
每当我尝试 运行 Body() class 使用 Add Stock 按钮时,它会显示错误“异常:ScrollView 只接受一个小部件”,之前 运行 没问题,但现在我将一些文本输入和按钮从 kivy 替换为 KivyMD。但是还没有接触到 Body() class。什么都没有改变
.py 文件
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
import sys, time, threading
from kivy.uix.screenmanager import ScreenManager, Screen
from datetime import datetime
import pandas_datareader.data as web
import pandas as pd
from kivymd.uix.screen import Screen
from kivymd.uix.list import MDList,ThreeLineListItem,ThreeLineAvatarIconListItem
from kivymd.uix.list import IconLeftWidget,ImageLeftWidget
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
import csv
from os import path
from kivy.uix.image import Image
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import NumericProperty, ListProperty, BooleanProperty, ObjectProperty, StringProperty
from kivy.uix.recycleview import RecycleView
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
import re
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
import pandas as pd
from kivy.clock import Clock
from functools import partial
from kivymd.uix.dialog import MDDialog
from kivymd.uix.textfield import MDTextField
from kivy.core.window import Window
# Window.size = (300,500)
username =''
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
txt_input = ObjectProperty(None)
stock_name = ObjectProperty(None)
stock_symbol = ObjectProperty(None)
purchase_price = ObjectProperty(None)
stop_loss = ObjectProperty(None)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, 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 is_selected:
# App.get_running_app().root.widget_1.ids.txt_input1.text = str(rv.data[index].get("text"))
xx =str(rv.data[index].get("text"))
if (xx.find('(NSI)') != -1):
x,y = xx.split(" (NSI)")
add_sym = '.NS'
else:
x,y = xx.split(" (BSE)")
add_sym = '.BO'
print(xx)
print(x)
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text = x
f = pd.read_csv("Stock Tickers.csv", encoding="ISO-8859-1", engine='python')
fl = len(f.index)
file = pd.DataFrame(f, columns=['Symbols', 'Name', 'Exchange'])
for i in range(fl):
for index in range(1):
columnSeriesObj_sym = file.iloc[:, 0]
columnSeriesObj1 = file.iloc[:, 1]
columnSeriesObj_ex = file.iloc[:, 2]
before_sym,b = columnSeriesObj_sym.values[i].split('.')
if columnSeriesObj1.values[i] == App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text:
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text = before_sym + add_sym
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
class DropDownWidget(GridLayout):
txt_input = ObjectProperty()
rv = ObjectProperty()
stock_name = ObjectProperty(None)
stock_symbol = ObjectProperty(None)
purchase_price = ObjectProperty(None)
stop_loss = ObjectProperty(None)
def btn_input(self):
end = datetime.today().date()
start = end
print("Stock Name:", App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text,
"Stock Symbol:", App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text)
print("Purchase Price:", App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text,
"Stop Loss(%):", App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text)
# write data to csv file
file_name = username + "_stoploss.csv"
if path.exists(file_name):
with open(file_name, "a+", newline='')as newFile:
fieldnames = ["Stock Name", "Stock Symbol", "Purchase Price", "Stop Loss(%)"]
newFileWriter = csv.DictWriter(newFile, fieldnames=fieldnames)
newFileWriter.writerow({"Stock Name": App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text,
"Stock Symbol": App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text,
"Purchase Price": App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text,
"Stop Loss(%)": App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text})
else:
myFile = open(file_name, 'w+',newline='')
myData = [["Stock Name", "Stock Symbol", "Purchase Price", "Stop Loss(%)"],
[App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text,
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text,
App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text,
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text]]
with myFile:
writer = csv.writer(myFile)
writer.writerows(myData)
df = web.DataReader(App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text, 'yahoo', start, end)
print(df.tail())
App.get_running_app().root.get_screen('body_screen').widget_1.ids.txt_input.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_name.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stock_symbol.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.purchase_price.text = ""
App.get_running_app().root.get_screen('body_screen').widget_1.ids.stop_loss.text = ""
class MyTextInput(TextInput):
txt_input = ObjectProperty(None)
flt_list = ObjectProperty()
word_list = ListProperty()
stock_name = ObjectProperty(None)
stock_symbol = ObjectProperty(None)
purchase_price = ObjectProperty(None)
stop_loss = ObjectProperty(None)
# this is the variable storing the number to which the look-up will start
starting_no = NumericProperty()
suggestion_text = ''
def __init__(self, **kwargs):
super(MyTextInput, self).__init__(**kwargs)
def on_text(self, instance, value):
# find all the occurrence of the word
self.parent.ids.rv.data = []
if len(value) != 0:
matches = [word for word in self.word_list if word.lower().find(value.lower()) != -1]
# display the data in the recycleview
display_data = []
for i in matches:
display_data.append({'text': i})
self.parent.ids.rv.data = display_data
# ensure the size is okay
if len(matches) <= 10:
self.parent.height = (50 + (len(matches) * 20))
else:
self.parent.height = 240
def keyboard_on_key_down(self, window, keycode, text, modifiers):
if self.suggestion_text and keycode[1] == 'tab':
self.insert_text(self.suggestion_text + ' ')
return True
return super(MyTextInput, self).keyboard_on_key_down(window, keycode, text, modifiers)
class Body(Screen):
def on_pre_enter (self, **kwargs):
super(Body, self).__init__(**kwargs)
f = pd.read_csv("Stock Tickers.csv", encoding = "ISO-8859-1", engine='python')
fl = len(f.index)
file = pd.DataFrame(f, columns=['Symbols', 'Name', 'Exchange'])
wl = []
for i in range(fl):
for index in range(1):
columnSeriesObj = file.iloc[:, 1]
self.columnSeriesObj_ex = file.iloc[:, 2]
wl.append(columnSeriesObj.values[i] + " (" + self.columnSeriesObj_ex.values[i] + ")")
tp = tuple(wl)
print("File is loaded")
self.widget_1 = DropDownWidget()
self.widget_1.ids.txt_input.word_list = wl
self.widget_1.ids.txt_input.starting_no = 3
self.add_widget(self.widget_1)
class Signin(Screen):
user_name = ObjectProperty(None)
def check(self,email):
regex = '^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'
if (re.search(regex, email)):
return True
else:
return False
def btn(self):
global username
un = self.user_name.text
class Option(Screen):
def back(self):
MDApp.get_running_app().root.transition.direction = 'right'
MDApp.get_running_app().root.current = 'signin_screen'
sm = ScreenManager()
sm.add_widget(Signin(name='signin_screen'))
sm.add_widget(Option(name='option_screen'))
sm.add_widget(Body(name='body_screen'))
class run1(MDApp):
def build(self):
kv = Builder.load_file("run1.kv")
return kv
if __name__ == "__main__":
run1().run()
.kv 文件
ScreenManager:
Signin:
Option:
Body:
ListApp:
<Body>:
name: 'body_screen'
canvas.before:
Color:
rgba: 188/255, 143/255, 145/255, 1
Rectangle:
pos: self.pos
size: self.size
<DropDownWidget>:
cols:2
id: DropDownWidget
canvas:
Color:
rgba:(1, 1, 1, 1)
size_hint:(0.5,1)
pos_hint :{'center_x':.43, 'center_y':.23}
row_force_default : True
row_default_height : 30
col_force_default : True
col_default_width : 250
spacing: '20dp'
stock_name: stock_name
stock_symbol: stock_symbol
purchase_price: purchase_price
stop_loss: stop_loss
txt_input: txt_input
rv: rv
MDToolbar:
title:'Add Stock'
type: "top"
pos_hint: {'top':1.0}
left_action_items: [["menu", lambda x: x]]
md_bg_color:98/255,0,238/255,1
elevation:8
MyTextInput:
id: txt_input
RV:
id: rv
size_hint: 5.0 ,.1
Label:
text: "Stock Name: "
TextInput:
id: stock_name
readonly: True
multiline:False
Label:
text: "Stock Symbol: "
TextInput:
id: stock_symbol
readonly: True
multiline:False
Label:
text: "Purchase Price: "
TextInput:
id: purchase_price
input_filter: 'int'
multiline:False
Label:
text: "Stop Loss(%): "
TextInput:
id: stop_loss
input_filter: 'int'
multiline:False
Button:
text:"Submit"
on_press: root.btn_input()
Button:
text:"Back"
on_press: root.parent.manager.current = 'option_screen'
<MyTextInput>:
id: MyTextInput
readonly: False
multiline: False
<SelectableLabel>:
id: SelectableLabel
# Draw a background to indicate selection
canvas.after:
Color:
rgba: (1, 1, 1, 1) if self.selected else (1, 0, 1, 1)
Rectangle:
# pos: self.pos
size: self.size
<RV>:
canvas:
Color:
rgba: 0,0,0,.2
Line:
rectangle: self.x +8 , self.y, self.width - 2, self.height -2
bar_width: 20
scroll_type:['bars']
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
cols:1
default_size: None, dp(20)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
multiselect: False
<Signin>:
name: 'signin_screen'
user_name: user_name
# canvas:
# Color:
# rgba: 255/255,105/255,180/255,1
# Rectangle:
# size: self.size
# pos: self.pos
# spacing: '15dp'
orientation:'vertical'
MDToolbar:
title:'SignIn'
type: "top"
pos_hint: {'top':1.0}
left_action_items: [["menu", lambda x: x]]
md_bg_color:98/255,0,238/255,1
elevation:8
MDTextField:
id: user_name
hint_text:'Email'
icon_right: "email"
mode:'rectangle'
required: True
helper_text_mode: "on_error"
helper_text: "Enter text"
# fill_color: 1, 1, 1, .5
halign:'center'
helper_text_mode:'on_focus'
pos_hint: {'center_x': .5, 'center_y': .5}
size_hint_x: None
width:300
# color_mode: 'custom'
# line_color_focus: 98/255,0,238/255,1
MDRoundFlatIconButton:
icon: "login"
text:"Submit"
font_size: 20
pos_hint: {'center_x': .5, 'center_y': .4}
halign:'center'
custom_color:0,0,0,1
# size_hint: .5 ,.3
background_normal: ''
# background_color: (255/255, 153/255, 71/255, 1)
on_press :
root.manager.transition.direction = 'left'
root.manager.current = 'option_screen'
<Option>:
canvas:
Color:
rgba: 138/255,43/255,226/255,.4
Rectangle:
size: self.size
pos: self.pos
name: 'option_screen'
orientation:'vertical'
spacing: '20dp'
MDToolbar:
title:'Choose Option'
type: "top"
md_bg_color:229/255,33/255,101/255,1
left_action_items: [["back button.png", lambda x: root.back()]]
pos_hint: {'top':1.0}
elevation:8
MDCard:
md_bg_color:138/255,43/255,226/255,1
orientation:'vertical'
size_hint:None,None
pos_hint: {'center_x': .5, 'center_y': .6}
height:150
width:150
elevation:8
MDIconButton:
md_bg_color:138/255,43/255,226/255,1
icon: "Add Stock.png"
user_font_size: "100dp"
halign:'center'
pos_hint: {'center_x': .5, 'center_y': .5}
on_press : root.manager.current = 'body_screen'
MDLabel:
text:'Add Stock'
font_size: "20dp"
halign:'center'
MDCard:
md_bg_color:138/255,43/255,226/255,1
orientation:'vertical'
size_hint:None,None
pos_hint: {'center_x': .5, 'center_y': .3}
height:150
width:150
elevation:8
MDIconButton:
icon: "Check Stoploss.png"
user_font_size: "100dp"
halign:'center'
pos_hint: {'center_x': .5, 'center_y': .5}
on_press : root.manager.current = 'Stoploss_ip'
MDLabel:
text:'Check Stoploss'
font_size: "20dp"
halign:'center'
显示如下错误..
错误
"D:\Stoploss Calculator\venv\Scripts\python.exe" "D:/Stoploss Calculator/run1.py"
[INFO ] [Logger ] Record log in C:\Users\Rushi Dada\.kivy\logs\kivy_20-12-25_64.txt
[INFO ] [deps ] Successfully imported "kivy_deps.angle" 0.3.0
[INFO ] [deps ] Successfully imported "kivy_deps.glew" 0.3.0
[INFO ] [deps ] Successfully imported "kivy_deps.sdl2" 0.3.1
[INFO ] [Kivy ] v2.0.0
[INFO ] [Kivy ] Installed at "D:\Stoploss Calculator\venv\lib\site-packages\kivy\__init__.py"
[INFO ] [Python ] v3.9.1 (tags/v3.9.1:1e5d33e, Dec 7 2020, 17:08:21) [MSC v.1927 64 bit (AMD64)]
[INFO ] [Python ] Interpreter at "D:\Stoploss Calculator\venv\Scripts\python.exe"
[INFO ] [Factory ] 186 symbols loaded
[INFO ] [KivyMD ] v0.104.1
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[INFO ] [Window ] Provider: sdl2
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] GLEW initialization succeeded
[INFO ] [GL ] Backend used <glew>
[INFO ] [GL ] OpenGL version <b'4.0.0 - Build 10.18.10.5161'>
[INFO ] [GL ] OpenGL vendor <b'Intel'>
[INFO ] [GL ] OpenGL renderer <b'Intel(R) HD Graphics 4000'>
[INFO ] [GL ] OpenGL parsed version: 4, 0
[INFO ] [GL ] Shading version <b'4.00 - Build 10.18.10.5161'>
[INFO ] [GL ] Texture max size <16384>
[INFO ] [GL ] Texture max units <16>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [Text ] Provider: sdl2
[INFO ] [GL ] NPOT texture support is available
[WARNING] [Lang ] The file D:\Stoploss Calculator\run1.kv is loaded multiples times, you might have unwanted behaviors.
[INFO ] [Base ] Start application main loop
File is loaded
[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "D:\Stoploss Calculator\run1.py", line 425, in <module>
run1().run()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\app.py", line 950, in run
runTouchApp()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 582, in runTouchApp
EventLoop.mainloop()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 347, in mainloop
self.idle()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 391, in idle
self.dispatch_input()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 342, in dispatch_input
post_dispatch_input(*pop(0))
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\base.py", line 248, in post_dispatch_input
listener.dispatch('on_motion', etype, me)
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\core\window\__init__.py", line 1412, in on_motion
self.dispatch('on_touch_down', me)
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\core\window\__init__.py", line 1428, in on_touch_down
if w.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\screenmanager.py", line 1198, in on_touch_down
return super(ScreenManager, self).on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\relativelayout.py", line 297, in on_touch_down
ret = super(RelativeLayout, self).on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\behaviors\button.py", line 138, in on_touch_down
if super(ButtonBehavior, self).on_touch_down(touch):
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivymd\uix\behaviors\ripplebehavior.py", line 231, in on_touch_down
return super().on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivymd\uix\button.py", line 961, in on_touch_down
return super().on_touch_down(touch)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\behaviors\button.py", line 151, in on_touch_down
self.dispatch('on_press')
File "kivy\_event.pyx", line 705, in kivy._event.EventDispatcher.dispatch
File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1132, in kivy._event.EventObservers._dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 57, in custom_callback
exec(__kvlang__.co_value, idmap)
File "D:\Stoploss Calculator\run1.kv", line 240, in <module>
on_press : root.manager.current = 'body_screen'
File "kivy\properties.pyx", line 498, in kivy.properties.Property.__set__
File "kivy\properties.pyx", line 545, in kivy.properties.Property.set
File "kivy\properties.pyx", line 600, in kivy.properties.Property.dispatch
File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1154, in kivy._event.EventObservers._dispatch
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\screenmanager.py", line 1056, in on_current
self.transition.start(self)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\screenmanager.py", line 377, in start
self.screen_in.dispatch('on_pre_enter')
File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "D:\Stoploss Calculator\run1.py", line 243, in on_pre_enter
self.widget_1 = DropDownWidget()
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\gridlayout.py", line 279, in __init__
super(GridLayout, self).__init__(**kwargs)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 359, in __init__
self.apply_class_lang_rules(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 463, in apply_class_lang_rules
Builder.apply(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 541, in apply
self._apply_rule(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 661, in _apply_rule
child.apply_class_lang_rules(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\widget.py", line 463, in apply_class_lang_rules
Builder.apply(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 541, in apply
self._apply_rule(
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\lang\builder.py", line 660, in _apply_rule
widget.add_widget(child)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\recycleview\__init__.py", line 466, in add_widget
super(RecycleView, self).add_widget(widget, *largs)
File "D:\Stoploss Calculator\venv\lib\site-packages\kivy\uix\scrollview.py", line 1152, in add_widget
raise Exception('ScrollView accept only one widget')
Exception: ScrollView accept only one widget
Process finished with exit code 1
我解决了这个问题。 问题出在 KV 文件名中。这是 运行 多次。并且多次加载同一个 kv 文件会导致意外结果,我看到了一个这样的结果
我的 .py 文件名为 run1.py,KV 文件名为 run1.kv 如您所见,我正在调用 kv = Builder.load_file("run1.kv ") 在 class run1(MDApp) 中。它正在调用 kv 文件,并且 kv 文件也被 .py 自动调用,因为它找到了相同的名称。
所以我用 stopl.kv 重命名了 kv 文件,它解决了问题..