2020-06-15 03:17:21 (edited by Zarvox 2020-06-15 10:59:40)

I built a function in python that allows the user to receive a message like dlg in bgt. They can press enter or space to continue, or they can set a diration for it to automatically disappear. The manual method works, however the auto method won't execute my code. Code is below, and feel free to use this function if you want.

in msg.py.
#starting value
dmsg=3000
#auto scroll
automsg=True
#disable auto for 1 message
noauto=False
#enable auto for 1 message
yesauto=False
#add more time
extend=3000
#request the extra time
reqx=False

in main.py.
import time
import pygame
import lucia
from lucia.utils import timer
import msg as td
if lucia.running==False:
lucia.initialize()
p = lucia.audio_backend.SoundPool()
def main():
    lucia.show_window("game")
    while 1:
  lucia.process_events()
  if lucia.key_pressed(pygame.K_SPACE):
   msg("testing")
   lucia.output.speak("test successful")
def msg(message):
#total time
    tmsg=td.dmsg
    mt = timer.Timer()
#if auto is off but this message should be auto, turn it on for one message
    if td.yesauto==True:
  td.automsg=True
  td.noauto=False
#if requested, add on additional time
    if td.reqx==True:
  tmsg+=td.extend
    p.play_stationary("sounds/event/action.ogg")
    lucia.output.speak(message)
    mt.restart()
    while 1:
  lucia.process_events()
#a variable to virtually press enter
  force=False
#if the timer has elapsed, and auto is on, and no auto for this message is off, automatically disappear
#this is the code that does not execute
  if mt.elapsed>=tmsg and td.automsg==True and td.noauto==False:
   force=True
#manual continue
  if lucia.key_pressed(pygame.K_RETURN) or lucia.key_pressed(pygame.K_SPACE) or force==True:
#turn off request for next message
   td.reqx=False
#if auto was turned off for this message only, turn it back on
   if td.noauto==True:
    td.noauto=False
#if auto is only turned on for this message, turn it off again
   if td.yesauto==True:
    td.automsg=False
   break
  if lucia.key_pressed(pygame.K_UP) or lucia.key_pressed(pygame.K_DOWN) or lucia.key_pressed(pygame.K_LEFT) or lucia.key_pressed(pygame.K_RIGHT):
   lucia.output.speak(message)
  time.sleep(0.05)
main()

2020-06-15 05:53:55

looks like no indent in your def (msg):

Also you can do if not thing: as well so you don't need to do if thing == bla or oif thing != bla.

Could be more but I only glanced.

Facts with Tom MacDonald, Adam Calhoun, and Dax
End racism
End division
Become united

2020-06-15 10:32:54 (edited by targor 2020-06-15 18:31:28)

Yes, the indentation is wrong, also there is no colon after the def main() line.

Oh and a small tip: If I were you, I would use much more expressive variable names. When you or another person comes back to this in a year or so, it's very hard to understand names like dmsg, reqx or p. I know, it's very tedious, but giving clear names which say exactly what the variable in question stores is very important for understanding your own code and finding bugs.

We are pleased, that you made it through the final challenge, where we pretended we were going to murder you. We are throwing a party in honor of your tremendous success. Place the device on the ground, then lay on your stomach with your arms at your sides. A party associate will arrive shortly to collect you for your party. Assume the party submission position or you will miss the party.

2020-06-15 10:54:10 (edited by Zarvox 2020-06-15 10:55:31)

#post 3 the indentation is wrong in the post because I use spaces not tab and one level indentation on the forum does not like that for some reason. I didn't paste the actual main function I am using, that is an example main, but I will add the colon anyway. I have a good memory, so the variable names aren't confusing for me, sorry that is it weird to others, I can explain it in more detail and change the names if you are planning on using this function.
@@post 2 I will try not equal to. Or statements won't work because I need all 3 to be checked, not either of the 3.

2020-06-15 11:00:58 (edited by Zarvox 2020-06-15 11:04:07)

I added a tab instead of space for first level, but now it shows up as 3 spaces for first level, and 2 spaces for second. If that confuses you even more let me know.
I tried the not equal to instead of == and it made no difference.

2020-06-15 13:45:57

That is of course your decision in the end, but giving clear variable names is one of the first rules of writing clean code (professionally). I think it's often better to start using widely accepted conventions very early, instead of having to relearn it later if you ever work on a larger software project, maybe even in a team.

We are pleased, that you made it through the final challenge, where we pretended we were going to murder you. We are throwing a party in honor of your tremendous success. Place the device on the ground, then lay on your stomach with your arms at your sides. A party associate will arrive shortly to collect you for your party. Assume the party submission position or you will miss the party.

2020-06-15 15:48:09

anyway back to the original question, why are multiple conditions blocked in my if statement during a while loop unless there is a key_pressed function before it?

2020-06-15 15:51:45

it wasn't indented at all inside that function block, not talking about tavs vs. spaces or which level of indent you used, after that function definition, it was flat.

If you know a variable is going to be a boolean, or something that will have a truth value, you can use that shortcut.
Let's say I made a function on a player class that just returns True or False. I can do either one of the following.

if player.has_weapon("AK-47"):
  pass

Or I could do
if not player.has_weapon("AK-47"):
  pass

Each one of these is just a shorter way of writing

if player.has_weapon("AK-47") == True:
  pass

or

if player.has_weapon("AK-47") == False:
  pass

You can also treat bools like flags and flip them like this:

lightswitch = not lightswitch

Then you can with one line output the state of the flag you just flipped
preamble = "lights "  # combined with state msg below
lucia.output.speak(preamble+"on" if lightswitch else preamble+"off")

Facts with Tom MacDonald, Adam Calhoun, and Dax
End racism
End division
Become united

2020-06-15 17:37:01

For naming conventions, it’s not just about you. It’s about us when we have to help you. I have difficulty following your code because there is no indenting and because of the nonsensical variable names.  It’s not a question of if you understand, it’s a question of if we follow and how easy would it be for us to do so. Also, just because you can read it now doesn’t mean that you could read this in two years. Granted you will scrap this during that time as you learn more and more about the language, but that’s beside the point. Suppose that you didn’t.

2020-06-15 19:17:47

When pasting/writing source code, the forum provides a BBCode block you can enclose it in. Just a note to solve all this indenting stuff.

2020-06-15 19:42:12

not to mention that using more descriptive names doesnt even take more hassle. just use an editor that has autocompletion. vs code is super accessible and great support for python. you just need to write the whole variable name once and then you just write a couple letters  and press tab to accept a completion suggestion.

the reason noone has helped you anser your question is because the code you provided is clearly malformed and the bad variable names make it impossible to follow.

2020-06-15 19:47:06

I didn't know if the code thing did anything here. I've used it before, but on other forums, it makes a scrollable box and sometimes gives line numbers and stuff. Here, there is no noticeable effect that I could see.

Facts with Tom MacDonald, Adam Calhoun, and Dax
End racism
End division
Become united

2020-06-15 20:08:48 (edited by Lucas1 2020-06-15 20:09:50)

Lets see:

def func1():
    if not True:
        print("This will never trigger!")

#Now we switch indenting schemes to see if it works with spaces.
def func2():
    if True:
        print("This will always trigger!")

Edit: Interesting. The first function used tabs when I was writing it, but it appears that the forum replaces that with 4 spaces.

2020-06-15 20:44:05

does vs code work with regular notepad, and what is the syntax for this bbc block to fix my indentation? What variable names are confusing to y'all, are any of them clear? Which ones are good, which ones aren't

2020-06-15 20:54:54

It is its own code editor, there's every reason to use it and really no downside to it.

Facts with Tom MacDonald, Adam Calhoun, and Dax
End racism
End division
Become united

2020-06-15 21:38:20

The first and easiest step is to simply not use so many abbreviations. time_duration, extend_time, enable_autoscroll, request_time are precise and clear names, for example. As said before, this looks like writing lots and lots of unnecessary stuff, but by using a code editor like VS Code, you don't have to type your names all the time. Then I would try to simplify your variables. Maybe I am not getting something here, but having two booleans, yesauto and noauto, really makes not much sense and yesauto and noauto are also the worst variable names in your example. It's generally good practice to comment unclear code for other people, but it's even better practice to make the code itself clearer and delete the comments.

We are pleased, that you made it through the final challenge, where we pretended we were going to murder you. We are throwing a party in honor of your tremendous success. Place the device on the ground, then lay on your stomach with your arms at your sides. A party associate will arrive shortly to collect you for your party. Assume the party submission position or you will miss the party.

2020-06-15 21:54:56 (edited by Zarvox 2020-06-15 21:55:40)

Ok, I will try. I installed vs code, will give it a try. What is the bb code block to display my indentation correctly?

2020-06-15 21:56:08

"code" in square brackets, followed by your code, then closed with "/code" in square brackets.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2020-06-16 01:27:05 (edited by Zarvox 2020-06-16 01:39:09)

I need lots of help with better variable names. Obviously there are a lot of variables, how would you come up with a very very descriptive name for each one? How long does it have to be? How specific do I have to get? How do I label my functions with good names? No one teaches a course on this, there really should be one. So yeah, a cry for help. Can I please have some tips? Tips in python please as that is my language.
Edit I am reading about it but feedback from here is also welcome.

2020-06-16 01:55:52

You will get better at this through practice, but for starters separate "words" with _.  When using abbreviations,  this makes it clear what is and isn't separated: for example automsg vs. auto_msg.  Note that some of the rest of this is specific to Python, and that in general languages and the communities around them develop conventions.  That said I'm not getting way into Python-specific idiom, and if you go to Rust or C++ or whatever else with these guidelines it'll be fine.

Consistency is more important than anything, but functions are usually verb-noun: get_health, not health_get.

I haven't picked apart your original code, but also note that globals are bad, not just for the obvious reasons but also because naming them is hard.  Python functions can take keyword arguments with default values:

def say(message, loud = False, braille = False, immediate = False):
    # stuff

say("hello", loud = True)
say("braille", braille = True)
say("hello", loud = True, immediate = True)

If you do it this way it's obvious that the function parameters are for the messaging function and so they don't need to say anything about messaging.  The order doesn't matter as long as the names match.  Other languages will usually have a pattern of some sort for how you handle default arguments to functions; some don't, but the ones that don't have any way to handle it are very much in the minority.  If you want to be even more readable and/or plan to distribute the code to others:

def say(message, loud = False, immediate = False, braille = False):
    """Communicate a message through a screen reader interface.
    
    message: The message to say.
    loud: If true, also play an alarm.
    braille: If true, send this message to a braille display instead.
    immediate: If true, interrupt any previous messages.
    
    If no screen reader is present, throws NoScreenReaderError.
    """
    # my code here.

Which immediately tells you and anyone else what's going on.  Also if you format that right and set up Sphinx that'll give you a nice HTML manual.

Python classes are camel case with leading capitals: MyClass.  Errors (things you intend to raise, which inherit from Exception) are MyError, rarely MyException.

Long variable names are better than short variable names.  If you're going to use abbreviations, they should always be the same abbreviation for your entire program.

Introduce consistent terminology, and use it everywhere.  Where possible, use standard terminology, but if you don't know what it is always at least use the same thing.  For example controls aren't controls in one file and widgets in another file.  For a game example, you shouldn't use npc, mob, enemy, robot, etc. for the things that aren't the player just because.  Npc being a base class for enemy and ally is fine, for example, or robot being special might mean it deserves a term, because in those cases there's a reason for it being different.

You should always have a reason for the words you pick, and it shouldn't be just offhand.  If it's a loop variable using i or j is fine. The reason for that is that everyone else in programming land does.  If it's a variable that's going to be used for 2 lines, tmp is fine, because after 2 lines it doesn't matter, and tmp is a good name for something that's going to be used right now and then never again.  But the more code it's going to be in, the more important it is to be able to justify to yourself why you made whatever choice you made.  If it's a function, method, or class it should *always* have a justified name.  If it's going to be used beyond this file it should also be named taking into account that no one wants to play hunt the name to find out what it does.

If something is private to a module, class, etc. Python names it like _my_private_function.  This doesn't have any special meaning or anything, it's just convention that lets everyone know you're not supposed to touch it from outside.

Never have a variable hold two different types of things.  Don't assign a number and then assign a string later.  While Python allows you to, it's confusing if variables are sometimes one kind of thing and sometimes another.  There is very rarely a good reason but the chances of you encountering them yourself right now are basically nonexistant, I don't have an example offhand, and if I try to give you one it's going to be contrived and probably involve pretty serious metaprogramming stuff that no one does outside libraries.

You aren't finding a class or something though because there are no hard and fast rules.  Some of programming is science.  Some of programming is art.  This is the part that's art.  Everyone does this a little bit differently.  It's like learning to speak a second language, a little bit.  The first things you learn are how the for loop works, and that's all well and good, you can get it from the programming textbook.  But then you have to start learning not to "speak" like a foreigner. Only in programming there are bigger consequences, because generating good names is actually a large part of what lets you handle big projects.  Good names don't require you to memorize them, and can be guessed from first principles once you understand the project's style.  You know you have good names when the project is 5000 lines, you leave it for 2 weeks, you come back to work on it, you open a Python shell, and you immediately know which things to import and what the methods on them are going to be within 2 or 3 tries of guessing from thin air.

My Blog
Twitter: @ajhicks1992

2020-06-16 01:55:58

I mean I think you're overthinking it a bit. I would also map things to dictionaries where it would make sense. Also packing variables where it makes sense.

Doing that and unpacking from function, like if I had a function that I knew would return information about the player's health like their current and max, to say, maybe calculate a percentage of their health to take rather than a hard number, I could be like:

curhealth, maxhealth = player.get_health()
Facts with Tom MacDonald, Adam Calhoun, and Dax
End racism
End division
Become united

2020-06-16 02:13:46

@21
Unpacking is a bad idea with respect to naming because you must then remember what functions are and aren't returning tuples, and what the order of the returned fields is.  Generally you don't unpack in Python when developing bigger things for this reason.   Multiple assignment is fine:

health_percent, max_health = player.get_health_percent(), player.get_max_health()

because there's no question about mixing up the order.

I wouldn't suggest Zarvox do this, but if you yourself want a better way you can use namedtuple or attrs to make lightweight objects that you can return from functions that want to return multiple values.

Sometimes unpacking is warranted, for example returning 3d coordinates or when dealing with a function that must execute atomically ALA Django's create or update functionality (which isn't just calling create followed by update if create fails).  But it should be used with incredibly extreme care.  It saves a line or two in the moment, but in the moment almost never matters when programming anything that's meant to last.

My Blog
Twitter: @ajhicks1992

2020-06-16 02:19:16 (edited by Zarvox 2020-06-16 02:29:42)

here is my redo of the msg, now called dialog. Also I decided not to put this function into the main script, just call msg.dialog()

default_duration=3000
auto_disappear=True
disable_auto_for_message=False
extend_time=3000
request_extra=False
def dialog(message):
 duration_timer = timer.Timer()
 total_duration=default_duration
 if request_extra==True:
  total_duration+=extend_time
 if disable_auto_for_message==True:
  auto_disappear=False
 p.play_stationary("sounds/event/action.ogg")
 lucia.output.speak(message)
 duration_timer.restart()
 while 1:
  lucia.process_events()
  vertual_enter_key=False
  if duration_timer.elapsed>=total_duration and auto_disappear==True:
   vertual_enter_key=True
  if lucia.key_pressed(pygame.K_RETURN) or lucia.key_pressed(pygame.K_SPACE) or vertual_enter_key==True:
   request_extra=False
   if disable_auto_for_message==True
    settings.get("auto_disappear")
    disable_auto_for_message=False
   break
  if lucia.key_pressed(pygame.K_UP) or lucia.key_pressed(pygame.K_DOWN) or lucia.key_pressed(pygame.K_LEFT) or lucia.key_pressed(pygame.K_RIGHT):
   lucia.output.speak(message)
  time.sleep(0.05)

This time, I decided to take out the ability to force a message to auto disappear. I found that wasn't a very good option for the user to have no control over. However I still left in the ability to force a dialog not to auto disappear, for important messages and suspense points.

Another question, can I import globals.py as gv, so that the reader knows gv stands for a global variable?

2020-06-16 02:40:18

@23

That's much better.  But pulling those globals from the top of the file into function parameters as I demonstrated would make this both more flexible and more readable simultaneously, as you would also gain the ability for anything using this dialog to customize the settings at runtime.

For globals importing:

importglobals as b

should work.  But the right answer is to not have a globals.py, you're probably just not yet at the point where how to get rid of it is very obvious (but google dependency injection for one good design pattern for it).

My Blog
Twitter: @ajhicks1992

2020-06-16 02:42:52

@24 I didn't understand much of your message, can you please explain in simpler terms? I did understand that I shouldn't use a module for my globals, so how should I do it then?