7
\$\begingroup\$

This is a life counter that I am using together with my friends for card games (Magic the Gathering).

For those who know the game, it is actually designed for 1vs1 Commander. It provides you with the possibility to take notes about life points, commander damage, and also to roll a dice to decide who has to start the game.

Interface

I would like to know if it is well written (in a "Pythonic" way) or if there is something that I can learn from the community to improve my sintax and my coding style: in fact, I am new to Python and to coding (to give you an idea of my level, I have started writing this app 1 month ago, and actually I code 2-3 hours a week).

Please note that the functions set_20, set_30, set_40 are not actually called from the interface: in a future version there will be a "settings" section (that is why there is also a Settings screen at the end of the builder) but I am still working on it.

Despite what is written in the above paragraph, the app works correctly: I am writing it following the Agile methodology, so I am adding pieces of functions/code when I have an idea without compromising the functionality of the app.

If you wish, you could also follow the project on Github: the code below is in the v2 branch, the master contains the first version of the app.

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
import random

Builder.load_string('''
<LifeCounter>:
    ScreenManager:
        size_hint: 20, 1
        id: _screen_manager
        Screen:
            name: 'MainScreen'
            BoxLayout: 
                orientation: 'vertical'
                padding: 50
                FloatLayout:
                    orientation: 'horizontal'
                    Button:
                        text: "Me"
                        font_size: '20sp'
                        pos_hint: {'x':.0, 'y':.3}
                        size_hint: .4, .6
                        on_release: root.change_name(self)
                    Button:
                        text: "Opponent"
                        font_size: '20sp'
                        pos_hint: {'x':.55, 'y':.3}
                        size_hint: .4, .6
                        on_release: root.change_name(self)
                FloatLayout:
                    orientation: 'horizontal'
                    Label:
                        id: l_label
                        font_size: '40sp'
                        pos_hint: {'x':.0, 'y':.3}
                        size_hint: .4, .8
                        text: str(root.count)
                        canvas.before: 
                            Color: 
                                rgb: .6, .6, .6 
                            Rectangle: 
                                pos: self.pos 
                                size: self.size 
                    Label:
                        id: l_label2
                        font_size: '40sp'
                        pos_hint: {'x':.55, 'y':.3}
                        size_hint: .4, .8
                        text: str(root.count2)
                        canvas.before: 
                            Color: 
                                rgb: .6, .6, .6 
                            Rectangle: 
                                pos: self.pos 
                                size: self.size 
                FloatLayout:
                    orientation: 'horizontal'
                    Button:
                        text: "-"
                        font_size: '30sp'
                        pos_hint: {'x':.0, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.decr() 
                    Button:
                        text: "+"
                        font_size: '30sp'
                        pos_hint: {'x':.20, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.incr()     
                    Button:
                        text: "-"
                        font_size: '30sp'
                        pos_hint: {'x':.55, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.decr2()
                    Button:
                        text: "+"
                        font_size: '30sp'
                        pos_hint: {'x':.75, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.incr2()
                FloatLayout:
                    orientation: 'horizontal'
                    Button:
                        text: "Commander A"
                        font_size: '20sp'
                        pos_hint: {'x':.0, 'y':.3}
                        size_hint: .4, .8
                        on_release: root.change_name(self)
                    Button:
                        text: "Commander B"
                        font_size: '20sp'
                        pos_hint: {'x':.55, 'y':.3}
                        size_hint: .4, .8
                        on_release: root.change_name(self)
                FloatLayout:
                    orientation: 'horizontal'
                    Label:
                        id: l_label
                        font_size: '40sp'
                        pos_hint: {'x':.0, 'y':.3}
                        size_hint: .4, .8
                        text: str(root.cmdcount)
                        canvas.before: 
                            Color: 
                                rgb: .6, .6, .6 
                            Rectangle: 
                                pos: self.pos 
                                size: self.size 
                    Label:
                        id: l_label2
                        font_size: '40sp'
                        pos_hint: {'x':.55, 'y':.3}
                        size_hint: .4, .8
                        text: str(root.cmdcount2)
                        canvas.before: 
                            Color: 
                                rgb: .6, .6, .6 
                            Rectangle: 
                                pos: self.pos 
                                size: self.size 
                FloatLayout:
                    orientation: 'horizontal'
                    Button:
                        text: "-"
                        font_size: '30sp'
                        pos_hint: {'x':.0, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.decrcmd()  
                    Button:
                        text: "+"
                        font_size: '30sp'
                        pos_hint: {'x':.20, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.incrcmd()      
                    Button:
                        text: "-"
                        font_size: '30sp'
                        pos_hint: {'x':.55, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.decrcmd2()
                    Button:
                        text: "+"
                        font_size: '30sp'
                        pos_hint: {'x':.75, 'y':.3}
                        size_hint: .2, .8
                        on_release: root.incrcmd2()
                FloatLayout:
                    orientation: 'horizontal'
                    Button:
                        text: "Roll a dice"
                        pos_hint: {'x':.0, 'y':.005}
                        size_hint: .4, .8
                        font_size: '20sp'
                        on_release: root.roll_dice()
                    Label:
                        text: str(root.dice)
                        pos_hint: {'x':.55, 'y':.005}
                        size_hint: .4, .8
                        font_size: '20sp'
                        canvas.before: 
                            Color: 
                                rgb: .6, .6, .6 
                            Rectangle: 
                                pos: self.pos 
                                size: self.size
                FloatLayout:
                    orientation: 'horizontal'
                    Button:
                        text: "-->"
                        pos_hint: {'x':.65, 'y': .0}
                        size_hint: .3, .8
                        on_release: pass
        Screen:
            name: 'Settings'
            BoxLayout: 
                orientation: 'vertical'
                padding: 50
                FloatLayout:
                    orientation: 'horizontal'
                    Button:
                        text: "20"
                        pos_hint: {'x':.12, 'y':.6}
                        size_hint: .2, .5
                        font_size: '20sp'
                        on_release: root.set_20()
                    Button:
                        text: "30"
                        pos_hint: {'x':.40, 'y':.6}
                        size_hint: .2, .5
                        font_size: '20sp'
                        on_release: root.set_30()
                    Button:
                        text: "40"
                        pos_hint: {'x':.68, 'y':.6}
                        size_hint: .2, .5
                        font_size: '20sp'
                        on_release: root.set_40()
''')

class LifeCounter(BoxLayout):
    count = NumericProperty(30)
    count2 = NumericProperty(30)
    cmdcount = NumericProperty(0)
    cmdcount2 = NumericProperty(0)
    dice = NumericProperty(0)

    def __init__(self, **kwargs):
        super(LifeCounter, self).__init__(**kwargs)
        self.hide_input = TextInput(size_hint=(None, None),
                                    size=(0, 0),
                                    multiline=False)
        self.hide_input_bind = None

    # The functions below update the names of the players and of the commanders.
    def change_name(self, instance):
        if self.hide_input_bind:
            self.hide_input.unbind_uid('text',
                                       self.hide_input_bind)
        self.hide_input.text = instance.text
        self.hide_input.focus = True
        self.hide_input_bind = self.hide_input.fbind('text',
                                                     self._update_text,
                                                     instance)

    def _update_text(self, button,
                     instance, value):
        button.text = value

    # The functions below update the labels of the player's life points.
    def incr(self):
        self.count += 1

    def decr(self):
        self.count -= 1

    def incr2(self):
        self.count2 += 1

    def decr2(self):
        self.count2 -= 1

    # The functions below update the labels of the commander damage.
    def incrcmd(self):
        self.cmdcount += 1

    def decrcmd(self):
        self.cmdcount -= 1

    def incrcmd2(self):
        self.cmdcount2 += 1

    def decrcmd2(self):
        self.cmdcount2 -= 1

    # The functions below set the lifepoints to 20, 30, 40 according to user's needs.
    def set_20(self):
        self.count = 20
        self.count2 = 20

    def set_30(self):
        self.count = 30
        self.count2 = 30

    def set_40(self):
        self.count = 40
        self.count2 = 40

    # The function below rolls the dice
    def roll_dice(self):
        self.min = 1
        self.max = 20
        self.dice = random.randint(self.min,
                                   self.max)

class MtgLifeCounter(App):
    def build(self):
        return LifeCounter()

if __name__ == '__main__':
    MtgLifeCounter().run()
\$\endgroup\$
3
\$\begingroup\$

Don't use fields unnecessarily

The self.min and self.max values are not used anywhere else in the posted code outside of this function:

def roll_dice(self):
    self.min = 1
    self.max = 20
    self.dice = random.randint(self.min,
                               self.max)

So they should not be fields, local variables would be enough:

def roll_dice(self):
    min, max = 1, 20
    self.dice = random.randint(min, max)

And why use local variables at all when you can simply write:

def roll_dice(self):
    self.dice = random.randint(1, 20)

Don't repeat yourself

These functions have the same behavior, just use different numbers:

def set_20(self):
    self.count = 20
    self.count2 = 20

def set_30(self):
    self.count = 30
    self.count2 = 30

It would be better to add a helper function and reuse that:

def set_20(self):
    self.set(20)

def set_30(self):
    self.set(30)

def _set(self, value):
    self.count = value
    self.count2 = value

Formatting

Why write like this:

def _update_text(self, button,
                 instance, value):
    button.text = value

When you can write more naturally as:

def _update_text(self, button, instance, value):
    button.text = value
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.