I have a label which when you click on it prints its value. It should also change the label on the first screen. Changing that label works as it should, but when I use the same method from outside the main class it does not work. Because of that I think the problem haws something to do with making new instances of classes instead of updating the ones that are already there. My knowledge of OOP is not good and because of that I can't figure this out. Any help and/or tips would be appreciated.
I tried resolving the problem with solutions from : Instantiate a class but don't call its __init__ method , but could not make it work in my case
The problem starts at line 93/94, then goes to line 45 from then it's clear
from kivy.app import *
from kivy.uix.button import *
from kivy.graphics import *
from kivy.uix.widget import *
from kivy.uix.label import *
from kivy.uix.behaviors import *
from kivy.uix.floatlayout import *
from kivy.uix.boxlayout import *
from kivy.uix.scrollview import ScrollView
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
brojPjesmi = 50
def dobijJenegaArtista(kega):
f = "Hi"+ str(x[0])
return f
x = [0]
#---------------------------------------------------------------------------------
class Name(Widget):
def __init__(self, **kwargs):
super(Name, self).__init__(**kwargs)
def changeName(self):
self.nam = "yeah"
self.ids.nameOfSong.text = self.nam[0]
self.ids.artistOfSong.text = str(dobijJenegaArtista(x[0]))
#---------------------------------------------------------------------------------
class FirstWindow(Screen):
def __init__(self, **kwargs):
super(FirstWindow, self).__init__(**kwargs)
self.pos = (0, 0)
self.size = (1,1)
self.z = Name()
self.add_widget(self.z)
def next(self):
if x[0] == brojPjesmi:
x[0] = 0
else:
x[0] = x[0] + 1
self.z.changeName()
def selected(self,p):
x[0] = p - 1 # it puts the value clicked to my counter x, because
# I will send it to next which will add 1 i subtract 1
print(x[0]) # check to see if it is correct
self.next()
def last(self):
if x[0] == 0:
x[0] = brojPjesmi
else:
x[0] = x[0] - 1
self.z.changeName()
#---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------
class SecondWindow(Screen,BoxLayout):
def __init__(self, **kwargs):
super(SecondWindow, self).__init__(**kwargs)
self.d = MyRow()
self.add_widget(self.d)
#---------------------------------------------------------------------------------
g = []
class MyRow(ScrollView):
def __init__(self, **kwargs):
super(MyRow, self).__init__(**kwargs)
self.size_hint=(1,None)
self.size=(Window.width,Window.height)
layout = GridLayout(cols = 1,spacing= 10,size_hint_y = None)
layout.bind(minimum_height = layout.setter('height'))
self.butt = Button(text="gmm",height=40,size_hint_y = None,on_press = lambda x:self.change_screen())
layout.add_widget(self.butt)
for i in range(brojPjesmi):
self.z = OneRow()
u = self.z.make()
layout.add_widget(u)
self.add_widget(layout)
def change_screen(self):
App.get_running_app().root.transition.direction = "right"
App.get_running_app().root.current = 'main'
i = [0]
#---------------------------------------------------------------------------------
class OneRow(ButtonBehavior,GridLayout):
def mm(self):
print(self.i)
self.z = MyRow
self.z.change_screen(self)
self.man = FirstWindow()
self.man.selected(self.i)
def make(self):
i[0] = i[0] + 1
self.i = i[0]
self.cols = 3
self.rows = 1
self.height = 25
self.size_hint_y = None
self.on_release = self.mm
self.Treci = Label(text = str(""),size_hint_y= None,size_hint_x =.1,height=25)
self.prviDio = Label(text = str(self.i),size_hint_y= None,height=25)
self.drugiDio = Label(text = str(self.i*2),size_hint_y= None,height=25)
self.add_widget(self.Treci)
self.add_widget(self.prviDio)
self.add_widget(self.drugiDio)
return self
#---------------------------------------------------------------------------------
class langApp(App):
def build(self):
return kv
#---------------------------------------------------------------------------------
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("nu.kv")
if __name__ == '__main__':
langApp().run()
.kv file
#:kivy 1.11.1
WindowManager:
FirstWindow:
SecondWindow:
<FirstWindow>:
name: "main"
FloatLayout:
pos: 0,0
size: root.width,root.height
Button:
on_press: root.last()
text: 'Last'
pos_hint: {'x':.25,'y':.1}
size_hint: .1,.1
Button:
on_release:
text: 'Stop'
pos_hint: {'x':.45,'y':.1}
size_hint: .1,.1
Button:
on_press: root.next()
text: 'Next'
pos_hint: {'x':.65,'y':.1}
size_hint: .1,.1
Button:
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
text: 'songs'
pos_hint: {'x':.8,'y':.8}
size_hint: .05,.05
<Name>:
FloatLayout:
pos: 0,0
size: root.width,root.height
pos_hint_x: None
pos_hint_y: None
Label:
id: nameOfSong
text: "Choose a song"
font_size: 20
size_hint: None, None
pos_hint: {'x': 0.435, 'y': 0.25}
Label:
id: artistOfSong
text: ""
font_size: 40
size_hint: None, None
pos_hint: {'x': 0.435, 'y': 0.20}
<SecondWindow>:
name: "second"
PS: The code should: when you press next or last it should update the counter and the screen with the hi+counter(it's just a placeholder), after you click the button in the upper right corner it should list the numbers(also a placeholder usually it's songs and artists) and when you click on a number it should take you to the first screen(works until this point) and then change to label to the number you clicked
The number is also printed for debugging purposes
Related
I have a recycleview of several thousand songs. I want to force a selection when I shuffle songs. I can force scrolling to the selected song by setting scroll_y but I cannot seem to get the item in the recycleview to display as selected as with the basic kivy recycleview example where you click on an item and it changes background color.
I have a basic example. If you click it should start the random item selection every 0.5 sec and print out the rv.data showing that the items have 'selected': True.
I've tried setting key_selection: 'selected' and I've also set 'selected': True in the recycleview.data.
MPROSRVbug.kv
<Manachan>:
id: manachanID
SelectionScreen:
id: selectscreenID
name: 'selectscreen'
manager: 'manachanID'
<SelectionScreen>:
id: selectionscreenid
BoxLayout:
orientation: 'horizontal'
Video:
id: previewVideo
SongList:
id: songlistid
<SongList>
cols: 1
size_hint_x: 1
BoxLayout:
size_hint: (1,.1)
orientation: 'vertical'
Label:
id: songtitle
canvas.before:
Color:
rgba: (.3, .3, .3, 1)
Rectangle:
pos: self.pos
size: self.size
RV:
id: recycle_grid1
size_hint_x: 1
<RV>:
viewclass: 'SongLinkButton'
SelectableRecycleBoxLayout:
id: recycle_grid2
default_size: None, dp(120)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<SongLinkButton>:
id: songbutton
orientation: 'horizontal'
height: '80dp'
# 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: textinfo
text_size: self.width, None
MPROSRVbug.py
#MUSIC PLAYER RANDOM ORDERED SORT
import os, sys
from random import *
songDict = {}
songVal = {}
i = 0
for mp3file in ["1","9","7k","r","j","i","7","g","a","2",]:
songDict[i] = mp3file
songVal[str(mp3file)] = 0
i += 1
if i > 7:
break
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.recycleview import RecycleView
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview.views import RecycleKVIDsDataViewBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import *
from kivy.core.audio import SoundLoader
from kivy.uix.scrollview import ScrollView
from kivy.clock import Clock
import traceback
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
#set self.data:
self.data = [{'textinfo.text': x, 'selected': False} for x in [songDict[y] for y in songDict]]
print("self.data item?", self.data[0], len(self.data))
def scroll_to_index(self, index):
box = self.children[0]
pos_index = (box.default_size[1] + box.spacing) * index
scroll = self.convert_distance_to_scroll(
0, pos_index - (self.height * 0.5))[1]
if scroll > 1.0:
scroll = 1.0
elif scroll < 0.0:
scroll = 0.0
self.scroll_y = 1.0 - scroll
def NextSong(self, *args):
rvRef = App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"]
randPickInt = randint(0,len(rvRef.data)-1)
for d in rvRef.data:
if d['textinfo.text'] == songDict[randPickInt]:
d['selected'] = True
dictElement = d
target = os.path.normpath(rvRef.data[randPickInt]['textinfo.text'])
App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["songtitle"].text = target
targettextOG = rvRef.data[randPickInt]['textinfo.text']
#PLAN: apply_selection always triggers when reycycleview refreshes, so set a global var of the selected guy. within the widget, if the name is the same as the selected guy, make yourself selected
global curSelectedSong
curSelectedSong = rvRef.data[randPickInt]['textinfo.text']
counter = 0
for d in rvRef.data:
#remove all previously selected
if d['selected']:
d['selected'] = False
#if we found the song, change to selected
if d['textinfo.text'] == targettextOG:
#print("VERSING", d['textinfo.text'], targettextOG, d['textinfo.text'] == targettextOG)
d['selected'] = True
#print("so has d updated?", d)
rvRef.data[counter] = d
print("updated rvRef.data", rvRef.data[counter])
counter += 1
print("WHAT IS RV DATA?", rvRef.data)
App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"].scroll_to_index(randPickInt)
rvRef.refresh_from_data()
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
def __init__(self, **kwargs):
super(SelectableRecycleBoxLayout, self).__init__(**kwargs)
def WeightedChoice():
pass
class SongLinkButton(RecycleKVIDsDataViewBehavior,BoxLayout,Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def __init__(self, **kwargs):
super(SongLinkButton, self).__init__(**kwargs)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SongLinkButton, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SongLinkButton, 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):
global curSelectedSong
try:
if curSelectedSong == rv.data[index]["textinfo.text"]:
self.selected == True
except:
pass
global soundObj
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected and App.get_running_app().root.current == 'selectscreen':
target = os.path.normpath(rv.data[index]['textinfo.text'])
Clock.schedule_interval(App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"].NextSong, 1)
App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["songtitle"].text = target
else:
pass
class Manachan(ScreenManager):
pass
class SongList(GridLayout):
pass
class SelectionScreen(Screen):
pass
class MyApp(App):
def build(self):
self.load_kv('MPROSRVbug.kv')
self.title = "Music Player Random Ordered Sort by Pengindoramu"
return Manachan()
if __name__ == '__main__':
MyApp().run()
I know this is probably an easy answer, and I've been digging around trying to figure this out but can't quite seem to understand it. Anytime I run this, when I click the button to call the method, I always get the same problem. im trying my best to understand this stuff but its getting the best of me right now.
AttributeError: 'NoneType' object has no attribute 'text'
import kivymd
from kivy.app import App
from kivymd.app import MDApp
from kivy.uix.boxlayout import BoxLayout
from kivymd.uix.textfield import MDTextField
from kivy.properties import ObjectProperty
class MainApp(MDApp):
height_feet = ObjectProperty(None)
height_feet = height_feet
height_inches = ObjectProperty(None)
weight = ObjectProperty(None)
bmiout = ObjectProperty(None)
def calculate(self):
height_feet = float(self.height_feet.text)
height_feet = float(height_feet * 12)
height_inches = float(self.height_inches.text)
height = float(height_feet + height_inches)
weight = float(self.weight.text)
bmi = int(weight / (height * height) * 703)
percent = str("%")
self.bmiout.text = "{}{}".format(bmi, percent)
self.height_feet.text = ""
self.height_inches.text = ""
self.weight.text = ""
def __init__(self, **kwargs):
self.title = "BMI"
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Blue"
super().__init__(**kwargs)
MainApp().run()
'KV'
#:import MDTextField kivymd.uix.textfield.MDTextField
#:import MDFillRoundFlatIconButton kivymd.uix.button.MDFillRoundFlatIconButton
BoxLayout:
id:layout
size_hint: .8, .8
pos_hint: {"center_x": .5, "center_y": .5}
spacing: dp(50)
orientation: 'vertical'
height_feet: height_feet
height_inches: height_inches
weight: weight
bmiout: bmiout
Label:
text: "BMI"
font_size: 100
MDTextField:
id: height_feet
hint_text: "Enter your height in feet"
require: True
max_text_length: 1
halign: 'center'
MDTextField:
id: height_inches
hint_text: "Enter your height in inches"
require: True
max_text_length: 2
halign: "center"
MDTextField:
id: weight
hint_text: "Enter your weight"
require: True
max_text_length: 3
halign: "center"
Label:
text: "Your BMI is"
font_size: 60
Label:
id: bmiout
font_size: 60
MDFillRoundFlatIconButton:
text: "Calculate"
icon: "calculator"
pos_hint:{ "center_x": .5, "center_y": .5}
width: dp(25)
on_release:
app.calculate()
Well, you should connect your object properties to widgets. Until it's done it is None.
from kivy.clock import Clock
...
class MainApp(MDApp):
# isn't it more readable when init is at the start?
def __init__(self, **kwargs):
self.title = "BMI"
# run function with delay
Clock.schedule_once(self.connectwidgets)
super().__init__(**kwargs)
# KivyMD can break when theme manager is in the init method
# so I put it here, just in case
theme_cls.theme_style = "Dark"
theme_cls.primary_palette = "Blue"
height_feet = ObjectProperty(None)
height_inches = ObjectProperty(None)
weight = ObjectProperty(None)
bmiout = ObjectProperty(None)
# As I said you should connect properties with widgets with using it's IDs
# but we have to delay running this function because instead Kivy run it before getting the widgets from kv file and won't work. Again.
def connectwidgets(self, *args):
self.height_feet = self.ids.height_feet
self.height_inches = self.ids.height_inches
self.weight = self.ids.weight
self.bmiout = self.ids.bmiout
def calculate(self):
height_feet = float(self.height_feet.text)
height_feet = float(height_feet * 12)
height_inches = float(self.height_inches.text)
height = float(height_feet + height_inches)
weight = float(self.weight.text)
bmi = int(weight / (height * height) * 703)
percent = str("%")
self.bmiout.text = "{}{}".format(bmi, percent)
self.height_feet.text = ""
self.height_inches.text = ""
self.weight.text = ""
MainApp().run()
PS. I don't know if that works, because I never put widgets to App class, but try it.
I need to make something like a spinner or dropdown list but with the ability to input the value manually when there is no right value on the list.
I'm trying to find some properties of spinner but no one seems to fit. Is there any posibility in kv language?
Here's a basic implementation using DropDown widget.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
kv = """
<CustomDropdown>:
size_hint_y: None
pos_hint: {"top": 1}
height: 56.0
orientation: "horizontal"
TextInput:
id: txt_input
size_hint_x: 0.5
Button:
id: show_drop
size_hint_x: 0.2
text: "drop"
on_release: root.show_dropdown()
Button:
id: submit_btn
size_hint_x: 0.3
text: "Submit"
on_press: root.validate_txt()
"""
class CustomDropdown(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.dropdown = DropDown()
self.fakeValues = ["First", "Second", "Third"]
for val in self.fakeValues:
btn = Button(text=val, size_hint=[0.5, None])
btn.height = 56.0
btn.bind(on_press=self.change_value)
self.dropdown.add_widget(btn)
def change_value(self, *args):
self.ids.txt_input.text = args[0].text
self.hide_dropdown()
def add_options(self, *args):
btn = Button(text=args[0], size_hint=[0.5, None])
btn.height = 56.0
btn.bind(on_press=self.change_value)
self.dropdown.add_widget(btn)
def validate_txt(self, *args):
curText = self.ids.txt_input.text
if ((curText in self.fakeValues) or (curText == None) or (curText == "")):
return
self.fakeValues.append(curText)
self.add_options(curText)
self.hide_dropdown()
def hide_dropdown(self):
self.dropdown.dismiss()
def show_dropdown(self, *args):
if self.dropdown.parent:
self.hide_dropdown()
else:
self.dropdown.open(self.ids.txt_input)
class TestApp(App):
def build(self):
Builder.load_string(kv)
return CustomDropdown()
TestApp().run()
Before doing anything:
Showing DropDown:
Typing some custom words and submitting it:
Showing DropDown again:
Concerning the following code, which is a subset of my calculation game app, I have two questions:
1) Why is dummy_series not plotted, although the axes (with ticks and labels are drawn)
2) How do I get the quit button to properly exit the app (does it have to remove all widgets from the root widget? Is there an oposite to AppObject.run(), that stops the app? - SOLVED: App.get_running_app().stop()
For the first question, the relevant part of the code is found in the StatisticScreen and the PlotScreen classes. The first one creates the dummy_series, initializes the creation of the graph widget and changes the screen to the PlotScreen. In the PlotScreen class, there is showPlot methods, which is basically copied from the github README.
So far I tried to change the overall background color to white. Both by "canvas before" a white rectangle, and by really changing the background color of the window. Both had no effect (exept the axes and labels were hidden, because they are also white). Then I tried to color the graph differently each time I create it (taken from the same github repo, there is a TestApp in if __name__ == '__main__':). But there is still no graph.
For the second question please consider the changeScreen-method of CalculationRoot. If it is called with quit as the argument, currently it just empties the screen_list and returns False. The idea was to call the callback of the "Back"-Button (key=27,1000). Since closing the App with the "Back"-Button actually works, given the screen_list is empty, I thought I could use this existing process. Also scheduling the keyHandler-method of the app object CalculationApp does not have the desired effect of closing the app.
# Python build-in Modules
import os
import operator # better handling of +, #, *, etc.
import webbrowser # access homepages via the about section
import random # create random math questions
import datetime # for the timer
import itertools # eg for cycling colors
from functools import partial # schedule callback functions that take arguments different from 'dt'
# Kivy
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.core.window import Window
from kivy.utils import platform
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty, NumericProperty, StringProperty
from kivy.storage.dictstore import DictStore
from kivy.utils import get_color_from_hex as rgb
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.logger import Logger
from kivy.garden.graph import Graph, MeshLinePlot, SmoothLinePlot
# Non-standard python
import numpy as np
###############################################################################
# Constants
###############################################################################
BACKGROUND_COLOR = [0,0,0,0]
TEXT_COLOR = [1,1,1,1]
kv_string = """
#:import Factory kivy.factory.Factory
#:set color_button (0.784, 0.4, 0.216, 1) # brown
#:set color_button_pressed (0.659, 0.3, 0.431, 1) # darker brown
#:set color_background_down '(0.4, 0.4, 0.4, 1)' # purple
<WrappedLabel#Label>:
size_hint_y: None
height: self.texture_size[1] + self.texture_size[1]/2
markup: True
<GridLabel#Label>:
font_size: min(root.width, root.height) * .3
<StatisticsSpinner#Spinner>:
background_color: color_button if self.state == 'normal' else color_button_pressed
background_down: color_background_down
option_cls: Factory.get("SpinnerLabel")
<SpinnerLabel#SpinnerOption>:
background_color: color_button if self.state == 'down' else color_button_pressed
background_down: color_background_down
<CalculationRoot>:
orientation: 'vertical'
cg_screen_manager: cg_screen_manager
statistic_screen: statistic_screen
plot_screen: plot_screen
ScreenManager:
id: cg_screen_manager
StartScreen:
name: 'StartScreen'
StatisticScreen:
id: statistic_screen
name: 'StatisticScreen'
PlotScreen:
id: plot_screen
name: 'PlotScreen'
<StartScreen#Screen>:
BoxLayout:
orientation: 'vertical'
padding: root.width * .02, root.height * .02
spacing: min(root.width, root.height) * .02
WrappedLabel:
text: '[b] Calculation Game [/b]'
font_size: min(root.width, root.height) * .1
Button:
text: 'Statistic'
on_release: app.root.changeScreen(self.text.lower())
Button:
text: 'Quit'
<StatisticScreen#Screen>:
stats_operation_spinner: stats_operation_spinner
stats_difficulty_spinner: stats_difficulty_spinner
stats_num_questions_button: stats_num_questions_button
BoxLayout:
orientation: 'vertical'
padding: root.width * .02, root.height * .02
spacing: min(root.width, root.height) * .02
WrappedLabel:
text: '[b] Statistics [/b]'
halign: 'center'
font_size: min(root.width, root.height) * .1
GridLayout:
size_hint: .9,.4
cols: 2
pos_hint: {'center_x': .5}
GridLabel:
text: 'Operation Type'
StatisticsSpinner:
id: stats_operation_spinner
text: '+'
values: ['+', '-', '*', ':', '%']
GridLabel:
text: 'Difficulty'
StatisticsSpinner:
id: stats_difficulty_spinner
text: '1'
values: ['1','2','3','4']
GridLabel:
text: 'Number of Questions'
Button:
id: stats_num_questions_button
text: '8'
on_release: app.root.statistic_screen.switchNumQuestions(self, self.text)
# on_release: self.text = '16' if self.text == '8' else self.text = '8'
background_color: color_button if self.state == 'normal' else color_button_pressed
Button:
size_hint: 1, .2
text: 'Plot'
on_release: app.root.statistic_screen.showPlot()
font_size: min(root.width, root.height) * .1
<PlotScreen#Screen>:
"""
###############################################################################
# Widgets
###############################################################################
class StatisticScreen(Screen):
""" Selection screen, where you can fix the parameters for
a pot of your statistics.
"""
def __init__(self, *args, **kwargs):
super(StatisticScreen, self).__init__(*args, **kwargs)
def showPlot(self):
""" 'onPlotButtonPress'
callback for the 'plot'-Button on the bottom of StatisticScreen
"""
dummy_series = np.random.randint(1, 10, (12,))
App.get_running_app().root.ids.plot_screen.createKivyPlot(dummy_series)
App.get_running_app().root.changeScreen('plot')
def switchNumQuestions(self, instance, text):
""" 'onNumQuestionsButtonPress'
callback for the 'choose number of questions'-button.
"""
if int(text) == 16:
instance.text = '8'
else:
instance.text = '16'
class PlotScreen(Screen):
def __init__(self, *args, **kwargs):
super(PlotScreen, self).__init__(*args, **kwargs)
self.series = None
self.graph_figure = None # error if uncommented... why? (referred to former name self.canvas)
self.colors = itertools.cycle([rgb('7dac9f'), rgb('dc7062'), rgb('66a8d4'), rgb('e5b060')])
def createKivyPlot(self, series=np.array(range(12))):
graph_theme = {
'label_options': {
'color': rgb('444444'), # color of tick labels and titles
'bold': True},
'background_color': rgb('f8f8f2'), # back ground color of canvas
'tick_color': rgb('808080'), # ticks and grid
'border_color': rgb('808080')} # border drawn around each graph
self.graph_figure = Graph(xlabel='Last 12 games', ylabel='Average Response Time', x_ticks_minor=1,
x_ticks_major=5, y_ticks_major=1,
y_grid_label=True, x_grid_label=True, padding=10,
x_grid=True, y_grid=True, xmin=0, xmax=len(series), ymin=0, ymax=int(1.5*max(series)), **graph_theme)
plot = SmoothLinePlot(mode='line_strip', color=next(self.colors))
plot.points = [(x, series[x]) for x in range(0, len(series))]
self.graph_figure.add_plot(plot)
self.add_widget(self.graph_figure)
def destroy(self):
self.remove_widget(self.graph_figure)
self.graph_figure = None
###############################################################################
# Root Widget
###############################################################################
class CalculationRoot(BoxLayout):
""" Root of all widgets
"""
calculation_screen = ObjectProperty(None)
def __init__(self, *args, **kwargs):
super(CalculationRoot, self).__init__(*args, **kwargs)
self.screen_list = [] # previously visited screens
def changeScreen(self, next_screen):
if self.screen_list == [] or self.ids.cg_screen_manager.current != self.screen_list[-1]:
self.screen_list.append(self.ids.cg_screen_manager.current)
if next_screen == 'start':
self.ids.cg_screen_manager.current = 'StartScreen'
elif next_screen == 'statistic':
self.ids.cg_screen_manager.current = 'StatisticScreen'
elif next_screen == 'plot':
self.ids.cg_screen_manager.current = 'PlotScreen'
elif next_screen == 'quit':
self.screen_list == []
#self.onBackBtnPress() # not working
#Clock.schedule_once(partial(App.get_running_app().keyHandler(27)), 0) # not working
return False
def onBackBtnPress(self):
if self.screen_list:
self.ids.cg_screen_manager.current = self.screen_list.pop()
return True
return False
###############################################################################
# App Object
###############################################################################
class CalculationApp(App):
""" App object
"""
def __init__(self, *args, **kwargs):
super(CalculationApp, self).__init__(*args, **kwargs)
self.use_kivy_settings = False
def keyHandler(self, *args):
key = args[1]
print(key)
if key in (1000, 27):
return self.root.onBackBtnPress()
def post_build_init(self, ev):
if platform == 'android':
pass
win = self._app_window
win.bind(on_keyboard = self.keyHandler)
def build(self):
Builder.load_string(kv_string)
self.bind(on_start=self.post_build_init)
return CalculationRoot()
if __name__ in ('__main__', '__android__'):
CalculationApp().run()
Apparently, there has been a problem with using kivy.garden.graph inside a ScreenManager for some time. According to this issue report, it has been fixed in kivy version v1.10.1.dev0. However, I think you can get around it by adding _with_stencilbuffer=False to your call to Graph().
And to stop the app, you can modify your kv_string in the StartScreen section to include :
Button:
text: 'Quit'
on_release: app.stop()
ScrollView:
id: historyscroll
size_hint: 1, 0.925
pos_hint: {"x": 0, "top": 1}
Label:
id: historybox
text: "start"
size_hint: 1, None
size_hint_y: None
height: self.texture_size[1]
Issue:
Text is not displayed. Adding new text to the label causes the word pause to get displayed with different font sizes.
Issue2:
TextInput:
text: "localhost:"
size_hint: 1, 0.06
pos_hint: {"x": 0, "y": 0}
id: cmdbox
multiline: False
text_validate_unfocus:False
font_size: "20sp"
Upon typing something in the text box, sometimes some wierd image gets displayed. Also text_validate_unfocus: False does not prevent text box from unfocusing when enter key is pressed
Edit:
Whole code:
main.py:
#-*-coding:utf8;-*-
#qpy:3
#qpy:kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.clock import Clock
from kivy.core.window import Window
from game import Game
class Sm(ScreenManager):
pass
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.game = Game()
self.add_widget(self.game)
self.ids.cmdbox.bind(on_text_validate=self.cmdreturn)
self.ids.cmdbox.bind(focus=self.cmdfocus)
self.ids.historybox.text=" "*(Window.width*75/1048)
#Clock.schedule_interval(self.render, 0.5)
def cmdreturn(self, args):
self.cmd = self.ids.cmdbox.text
#self.ids.historybox.insert_text("\n"+self.cmd)
self.ids.historybox.text += "\n" + self.cmd
self.cmdexecute(self.cmd.split(str(self.game.current_)+":")[1])
self.ids.cmdbox.text = str(self.game.current_) + ":"
def cmdexecute(self, cmd):
print(cmd)
if cmd == "fill":
self.ids.historybox.text+="\nfill"*30
if cmd == "clear":
self.ids.historybox.text= " "*(Window.width*75/1048)
if cmd == "ls":
self.ids.historybox.text= "\n"+str(self.game.current.folders.keys())
def cmdfocus(self, instance, value):
if value:
self.ids.cmdbox.pos_hint={"x":0, "y":0.45}
self.ids.historyscroll.size_hint=(1, 0.475)
else:
self.ids.cmdbox.pos_hint={"x":0, "y":0}
self.ids.historyscroll.size_hint=(1, 0.925)
class MenuScreen(Screen):
pass
class PauseScreen(Screen):
pass
class OptionsScreen(Screen):
pass
class GameApp(App):
def build(self):
sm = Sm()
sm.add_widget(MenuScreen(name="menu"))
sm.add_widget(GameScreen(name="game"))
sm.add_widget(PauseScreen(name="pause"))
sm.add_widget(OptionsScreen(name="options"))
sm.transition = NoTransition()
return sm
GameApp().run()
game.kv:
#kivy 1.10.0
<GameScreen>:
id:gscreen
FloatLayout:
Button:
text:"pause"
size_hint:(0.15, 0.05)
pos_hint:{"right":0.98, "top":0.98}
on_press:root.manager.current="pause"
ScrollView:
id: historyscroll
size_hint: 1, 0.925
pos_hint: {"x": 0, "top": 1}
Label:
id: historybox
#readonly: True
text: "start"
size_hint: 1, None
size_hint_y: None
height: self.texture_size[1]
#height: max(self.minimum_height, historyscroll.height)
#multiline: True
#foreground_color: (1,1,1,1)
#background_color: (255,255,255,0)
#font_size: "17sp"
#halign: "left"
#text_size:(self.width, "None")
TextInput:
text: "localhost:"
size_hint: 1, 0.06
pos_hint: {"x": 0, "y": 0}
id: cmdbox
multiline: False
text_validate_unfocus:False
font_size: "20sp"
<MenuScreen>:
FloatLayout:
Button:
text:"start"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.61}
on_press:root.manager.current="game"
Button:
text:"options"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.5}
on_press:root.manager.current="options"
Button:
text:"exit"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.39}
on_press:quit
<OptionsScreen>:
FloatLayout:
Button:
text:"back"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.5}
on_press:root.manager.current="menu"
<PauseScreen>:
FloatLayout:
Button:
text:"back"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.495}
on_press:root.manager.current="game"
Button:
text:"exit"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.605}
on_press:root.manager.current="menu"
game.py:
from kivy.uix.widget import Widget
from random import randint
class Game(Widget):
def __init__(self, **kwargs):
super(Game, self).__init__(**kwargs)
self.localhost = "5255.7611"
self.internet = Internet(self.localhost)
self.current_ = "localhost"
self.current = self.internet.links[self.localhost.split(".")[0]].links[self.localhost.split(".")[1]]
class Internet:
def __init__(self, localhost):
self.links = {}
self.links[str(localhost)[:4]] = Router(str(localhost)[:4], self)
self.links[str(localhost)[:4]].islocal(localhost)
def Crouter(self):
tmp = str(randint(1000, 9999))
if not str(tmp) in self.links:
self.links[str(tmp)] = Router(tmp, self)
else: self.Crouter
class Computer:
def __init__(self, ip, router):
self.ip = ip
self.router = router
self.islocal = False
self.folders = {"programs":Folder("programs",[], {}),
"downloads":Folder("downloads",[], {})}
class Folder:
def __init__(self, name, content, data):
self.content = content
self.data = data
class File:
def __init__(self, content, data):
self.content = content
self.data = data
class Router:
def __init__(self, ip, internet):
self.ip = ip
self.internet = internet
self.links = {}
def Ccomputer(self):
tmp = str(randint(1000, 9999))
if not str(tmp) in self.links:
self.links[str(tmp)] = Computer(str(tmp)+self.ip, self)
else: self.Ccomputer
def islocal(self, localhost):
self.links[str(localhost)[5:]] = Computer(str(localhost), self)
self.links[str(localhost)[5:]].islocal = True
(Btw, I am using qpython-kivy)
Issue in brief:
The text input(id:cmdbox) on editing sometimes displays wierd images in place of text.
Also, the label(id:historybox) does not display the correct text(it only displays "pause" in different font size each time)
Edit2:
Finally got some images.
https://www.dropbox.com/sh/i6t192ujys2hivz/AACPR5Sgb72Mv8M7gB3DiGmNa?dl=0
For the first issue, in the __init__ of your screen you are replacing the text of your label
For the second, I don't know if that property exists in kivy If you want to keep the focus in your TextInput Try this:
...
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.game = Game()
self.add_widget(self.game)
self.ids.cmdbox.bind(on_text_validate=self.cmdreturn)
self.ids.cmdbox.bind(focus=self.cmdfocus)
#self.ids.historybox.text=" "*(Window.width*75/1048)
#Clock.schedule_interval(self.render, 0.5)
def cmdreturn(self, args):
self.cmd = self.ids.cmdbox.text
#self.ids.historybox.insert_text("\n"+self.cmd)
self.ids.historybox.text += "\n" + self.cmd
self.cmdexecute(self.cmd.split(str(self.game.current_)+":")[1])
self.ids.cmdbox.text = str(self.game.current_) + ":"
Clock.schedule_once(self.refocus)
def refocus(self, *args):
self.ids.cmdbox.focus = True
...
And I can't say anything about the weird images because I don't get them