2

I'm working on an program that is suppose to be an assistant for people that go to my local gym. Though I'm having trouble with a global variable difficult to change its string value. I want to be able to press any of the Buttons in main_Screen and have the string of the difficult change. Therefore in the BoulderingDirectory class it can preform the corresponding SQL for my database holding the exercises.

Note: I've cut off a majority of the code because there are multiple sections in the gym and I'm just focusing on this section because it applies exactly the same to other sections.

screen_manager = ScreenManager()

global difficult

difficult = "Beginner"

class ScreenManagerApp(App):
   def build(self):
       return screen_manager

class MyScreenManager(ScreenManager):
   pass

class main_Screen(Screen):
   pass

class urec_facilities(Screen):
   pass

class AdventureDirectory(Screen):
   pass


class BoulderingDirectory(Screen):
    connection = sqlite3.connect("workoutData.db")
    cursor = connection.cursor()

    if (difficult == "Beginner"):
        cursor.execute("SELECT * FROM workoutData WHERE Activity = 
                       'Bouldering' and Difficulty = 'Beginner'")
    elif(difficult == "Intermediate"):
        cursor.execute("SELECT * FROM workoutData WHERE Activity = 
                        'Bouldering' and Difficulty = 'Intermediate'")
    else:
        cursor.execute("SELECT * FROM workoutData WHERE Activity = 
                       'Bouldering' and Difficulty = 'Advanced'")

    rows = StringProperty(str(cursor.fetchall()))


root_widget = Builder.load_string("""


<main_Screen>:
    canvas.before:
        Rectangle:
            pos: self.pos
            size: self.size
            source: "mainScreenImage.png"

Label:
    text: "Select your dificulty"
    font_size: 50
    color: (0.9,0.8,0,1)
    background_color: (0,0,0,0)
    pos_hint: {"x": 0, "y": 0.20}


Button:
    text: 'Beginner'
    font_size: 30
    size_hint: 0.2,0.2
    color: (0.9,0.8,0,1)
    background_color: (0,0,0,0)
    pos_hint: {"x": 0.16, "y": 0.1}
    on_release:
        #set the difficult variable to a string "Beginner"
        app.root.current = 'urecFac'



Button:
    text: 'Intermediate'
    font_size: 30
    size_hint: 0.2,0.2
    color: (0.9,0.8,0,1)
    background_color: (0,0,0,0)
    pos_hint: {"x": 0.41, "y": 0.1}
    on_release:
        #set the difficult variable to a string "Intermediate"
        app.root.current = 'urecFac'


Button:
    text: 'Advanced'
    font_size: 30
    size_hint: 0.2,0.2
    color: (0.9,0.8,0,1)
    background_color: (0,0,0,0)
    pos_hint: {"x": 0.66, "y": 0.1}
    on_release:
        #set the difficult variable to a string "Advanced"
        app.root.current = 'urecFac'

<urec_facilities>:
 BoxLayout:
    orientation: "vertical"
    Button:
        text: "Adventure Center"
        font_size: 50
        color: (0.9,0.8,0,1)
        background_normal: "adventureCenterImage.jpg"
        on_release: app.root.current = "AdventureDirectory"
    Button:
        background_normal: "mainScreenImage.png"
        text: "Back"
        font_size: 50
        color: (0.9,0.8,0,1)
        on_release: app.root.current = "mainScreen"

<AdventureDirectory>:
BoxLayout:
    orientation: "vertical"
    Button:
        text: "Bouldering"
        font_size: 50
        color: (0.9,0.8,0,1)
        on_release:
            app.root.current = "BoulderingDirectory"

    Button:
        text: "Go back"
        font_size: 50
        color: (0.9,0.8,0,1)
        on_release:
            app.root.current = "urecFac"

<BoulderingDirectory>:
BoxLayout:
    orientation: "vertical"
    Label:
        text: root.rows
        text_size: (root.width - 175), None
    Button:
        text: "Go back"
        font_size: 50
        color: (0.9,0.8,0,1)
        background_color: (0,0,0,1)
        on_release:
            app.root.current = "AdventureDirectory"

""")



screen_manager.add_widget(main_Screen(name = "mainScreen"))
screen_manager.add_widget(urec_facilities(name = "urecFac"))
screen_manager.add_widget(AdventureDirectory(name = "AdventureDirectory"))
screen_manager.add_widget(BoulderingDirectory(name = "BoulderingDirectory"))


ScreenManagerApp().run()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
ShrooMsss
  • 25
  • 6

3 Answers3

1

It is not necessary or advisable to use global variables. It is not necessary because changing the value of a global variable does not notify the change to the copies and is not recommended because it is difficult to debug (for more information read Why are global variables evil?).

In this case the solution is to use a StringProperty that you can connect to a callback to update the data. On the other hand you must implement the logic of the request within a method so that it can be called several times. I have restructured your project considering the above and the solution is the following:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import StringProperty
import sqlite3

class Main_Screen(Screen):
   difficult = StringProperty()

class Urec_facilities(Screen):
   pass

class AdventureDirectory(Screen):
   pass

class BoulderingDirectory(Screen):
    rows = StringProperty("")

    def load_from_difficult(self, difficult):
        connection = sqlite3.connect("workoutData.db")
        cursor = connection.cursor()
        cursor.execute("SELECT * FROM workoutData WHERE Activity = 'Bouldering' and Difficulty = ?", (difficult,))
        self.rows = str(cursor.fetchall())

root_widget = Builder.load_string("""
ScreenManager:
    Main_Screen:
        name: 'mainScreen'
        on_difficult: bouldering.load_from_difficult(self.difficult)
    Urec_facilities:
        name: 'urecFac'
    AdventureDirectory:
        name: 'AdventureDirectory'
    BoulderingDirectory:
        id: bouldering
        difficult: 'Beginner'
        name: 'BoulderingDirectory'

<Main_Screen>:
    canvas.before:
        Rectangle:
            pos: self.pos
            size: self.size
            source: "mainScreenImage.png"

    Label:
        text: "Select your dificulty"
        font_size: 50
        color: (0.9,0.8,0,1)
        background_color: (0,0,0,0)
        pos_hint: {"x": 0, "y": 0.20}

    Button:
        text: 'Beginner'
        font_size: 30
        size_hint: 0.2,0.2
        color: (0.9,0.8,0,1)
        background_color: (0,0,0,0)
        pos_hint: {"x": 0.16, "y": 0.1}
        on_release:
            root.difficult =  "Beginner"
            app.root.current = 'urecFac'

    Button:
        text: 'Intermediate'
        font_size: 30
        size_hint: 0.2,0.2
        color: (0.9,0.8,0,1)
        background_color: (0,0,0,0)
        pos_hint: {"x": 0.41, "y": 0.1}
        on_release:
            root.difficult = "Intermediate"
            app.root.current = 'urecFac'

    Button:
        text: 'Advanced'
        font_size: 30
        size_hint: 0.2,0.2
        color: (0.9,0.8,0,1)
        background_color: (0,0,0,0)
        pos_hint: {"x": 0.66, "y": 0.1}
        on_release:
            root.difficult = "Advanced"
            app.root.current = 'urecFac'

<Urec_facilities>:
    BoxLayout:
        orientation: "vertical"
        Button:
            text: "Adventure Center"
            font_size: 50
            color: (0.9,0.8,0,1)
            background_normal: "adventureCenterImage.jpg"
            on_release: app.root.current = "AdventureDirectory"
        Button:
            background_normal: "mainScreenImage.png"
            text: "Back"
            font_size: 50
            color: (0.9,0.8,0,1)
            on_release: app.root.current = "mainScreen"

<AdventureDirectory>:
    BoxLayout:
        orientation: "vertical"
        Button:
            text: "Bouldering"
            font_size: 50
            color: (0.9,0.8,0,1)
            on_release:
                app.root.current = "BoulderingDirectory"

        Button:
            text: "Go back"
            font_size: 50
            color: (0.9,0.8,0,1)
            on_release:
                app.root.current = "urecFac"

<BoulderingDirectory>:
    BoxLayout:
        orientation: "vertical"
        Label:
            text: root.rows
            text_size: (root.width - 175), None
        Button:
            text: "Go back"
            font_size: 50
            color: (0.9,0.8,0,1)
            background_color: (0,0,0,1)
            on_release:
                app.root.current = "AdventureDirectory"

""")

class ScreenManagerApp(App):
    def build(self):
        return root_widget

if __name__ == '__main__':
    ScreenManagerApp().run()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thank you this seems to work very well for changing the variable but for some reason the data from my database is no longer displaying when I go to my BoulderingDirectory Screen, why could this issue arise? – ShrooMsss Dec 13 '18 at 05:02
  • @ShrooMsss You can share your .db, I have tested it and it works correctly. – eyllanesc Dec 13 '18 at 05:04
  • Nvm, I got it to work, Thank you so much. Very appreciative. – ShrooMsss Dec 13 '18 at 05:11
0

Before using a global variable in a class or a function, you have to declare that the variable you are referring to is the global variable or it is created as a local variable for that definition.

So, do

class BoulderingDirectory(Screen):
    global difficult
    ...
Weekday
  • 44
  • 3
  • Though I still dont know how to change the string of the variable so my SQL works. on_release: #set the difficult variable to a string "Intermediate" app.root.current = 'urecFac' – ShrooMsss Dec 13 '18 at 04:12
  • Have you tried declaring the global variable inside the said function. If yes, could you show us the function code? – Weekday Dec 13 '18 at 04:15
  • @Weekday Your solution will not work since it does not notify the change, besides it is not advisable to use global variables: [Why are global variables evil?](https://stackoverflow.com/questions/19158339/why-are-global-variables-evil) – eyllanesc Dec 13 '18 at 04:17
  • I've declared it in the function and it works but it doesnt change the string because the on_release call doesn't have a way to assign to the variable. – ShrooMsss Dec 13 '18 at 04:18
  • just simple assignment in the function after declaring global should work. – Weekday Dec 13 '18 at 04:30
  • @eyllanesc yes they are but for a simple app, these things can be forgone in my opinion. As Hettinger says "python is an adult language, we don't lock our doors" – Weekday Dec 13 '18 at 04:32
  • @Weekday You can be an adult, but even so, it's not good for you to shoot at your feet if it's not necessary. :-) – eyllanesc Dec 13 '18 at 04:33
  • @eyllanesc You are right. Just out of curiosity though do you think it is wrong for a programming language to advertise itself as being incredible callous to variable privacy? – Weekday Dec 13 '18 at 04:41
  • @Weekday No, just because we are adults you must have the maturity to know where to use it, liberty is not debauchery, that's why python has different scopes for variables, etc. – eyllanesc Dec 13 '18 at 04:43
  • @eyllanesc Thanks for patiently answering, as you can see I am a complete noob to programming and this is my first language ever. I feel I have so much to learn and any advice like this always helps. Again, thanks. – Weekday Dec 13 '18 at 04:50
  • @Weekday The scope of the variables is important but sometimes it is underestimated, bringing with it a program that consumes a lot of memory, difficulty to debug as there are collisions of variables, etc. The advisable thing is that the variables live until you are necessary after it should be eliminated. – eyllanesc Dec 13 '18 at 04:53
0

The global keyword allows you to create and modify variables at the global scope rather than the local scope. The syntax for it is global variableName. So just add global difficult to any place where you need to change and/or access the global variable.

You can change the beginning of your BoulderingDirectory class to this:

class BoulderingDirectory(Screen):
    global difficult

See here for more info on the global keyword.

Pika Supports Ukraine
  • 3,612
  • 10
  • 26
  • 42
  • I've declared it in the function and it works but it doesnt change the string because the on_release call doesn't have a way to assign to the variable. – ShrooMsss Dec 13 '18 at 04:19
  • @ShrooMsss what string isn't changing value? – Pika Supports Ukraine Dec 13 '18 at 04:21
  • the variable difficult is not changing its value because I dont know how to change it via the on_release call in main_Screen – ShrooMsss Dec 13 '18 at 04:24
  • 1
    @ShrooMsss I'm not a kivy expert, but I think you just have to define the `on_release` method and add `global difficult`, then edit the variable as needed. – Pika Supports Ukraine Dec 13 '18 at 04:27
  • so in my main_Screen class ive created a function called switch with a parameter of text. The function does difficulty = text. Then in the on_release for the button i call this function with the parameter being ("Intermediate") but I am getting an error of "takes 1 positional argument but 2 were given", do you have any idea why? – ShrooMsss Dec 13 '18 at 04:41
  • @ShrooMsss it means you passed 2 arguments to a function that only expected one. – Pika Supports Ukraine Dec 13 '18 at 16:30