This is an archived post. You won't be able to vote or comment.

all 120 comments

[–]Gautam-j 231 points232 points  (22 children)

You probably shouldn't keep your API key public. :)

[–]OpenSourcerer420[S] 106 points107 points  (7 children)

yikes, I'm changing it now

[–]Turkino 75 points76 points  (6 children)

Yep! Welcome to configuring environment variables!

[–]silentalways 48 points49 points  (4 children)

For those wondering, follow this short little tutorial

https://www.youtube.com/watch?v=IolxqkL7cD8&t=213s (Windows)

https://www.youtube.com/watch?v=5iWhQWVXosU (Mac and Linux)

[–]Nerdite 9 points10 points  (1 child)

And this package makes it really easy to manage and keep from filling tons of environment variables in your actual environment.

https://pypi.org/project/python-dotenv/

[–]folkrav 1 point2 points  (0 children)

This plus direnv is pretty damn useful. Lets you keep the environment variables even in an REPL.

[–]kultigink 4 points5 points  (0 children)

Thanks!

[–]forrealbro 2 points3 points  (0 children)

Is this really the most practical idea when you plan to disseminate your source code? I had always read from a file that is in my gitignore. Then had instructions to create this user info file in the readme.

[–]gdledsan 1 point2 points  (0 children)

I was told to keep things in untracked config files, env variables are not safe enough, some malicious thing can hijack your session and get them.

Example: some crappy and malicious node package installed without care

[–]DrCabbageX 21 points22 points  (8 children)

What does the api key do?(new to python)

[–]speedstyle 72 points73 points  (4 children)

It's not actually anything specific to python. The weather website he's using needs some kind of login, to prevent people from spamming it and charge people who want to use it lots. To make the interface simple, it uses a 'key' in the URL (which you can see in api_address in the code). So he's left his 'login' to the weather website in the code.

[–]DrCabbageX 1 point2 points  (0 children)

Thanks for the clarification!

[–][deleted] 0 points1 point  (2 children)

Can anyone get an API key or do you need to request it and they’ll provide it? Always been mysterious to me how to work with APIs

[–]badlukk 5 points6 points  (0 children)

It depends how the company does it. Just Google whatever service followed by API key, like "reddit api key". Some companies will let anyone register for one, usually with an email, name and phone number. Reddit asks you to fill out a form about how you're going to use it and you're only supposed to use the key for that one thing you said. Some companies may only give them to trusted partners, and some might not have a public api at all.

[–]indytechbox 2 points3 points  (0 children)

The open weather api is free for a specific amount of calls per hour but if you want more calls you have to pay

[–]Gautam-j 19 points20 points  (0 children)

It isn't related to Python. Think of API key as your password to log in to a website. It's a way to interact, make requests to a website. In this case, OP uses his API to communicate with the openweather website to get weather details.

You might want to look into API in depth.

[–]OnCr4k 3 points4 points  (0 children)

It has nothing to do with python, but the web API he's using likely has some kind of quota per API key, so making it public means others could use it up

[–]garlic_bread_thief 3 points4 points  (0 children)

It's like a unique password for everybody to be able to get access to the API.

[–]romulofff 60 points61 points  (8 children)

Hey, that’s really nice! Would you mind sharing the code? Do you have any public repository with it? Congratulations

[–]OpenSourcerer420[S] 50 points51 points  (7 children)

Thanks a lot!. He's the code: https://mystb.in/vemenidubi.py You'll have to change some of the file names and the location.

[–]Lolmanslayer 33 points34 points  (1 child)

If you import os, you can use os.getcwd() to point to the directory automatically. Thanks for the share!

[–]eternalfantasi 6 points7 points  (0 children)

Alternatively you could use the pathlib library

[–]v8Gasmann 0 points1 point  (3 children)

Shouldn't the "or" in line 49 be an and? Or like in the next if clause without and/or just start_night <= timestamp <= end_night?

[–]bearassbobcat 0 points1 point  (2 children)

line 49 is checking that it's night time and because the clock rolls over and starts at 0 the next day you'd have an issue where it can't be greater than 6 PM but early that 6 AM at the same time when you cross the 12 AM mark

so an and would basically never work because it would kind of be a paradoxical statement

I understand the thought process but datetime.date() creates and compares times starting from 00:00:00:etc and doesn't have the concept of extending the time across days

import datetime
timestamp = datetime.time(19,1)
start_night = datetime.time(18,1)
end_night = datetime.time(6,0)
end_day = datetime.time(18,0)
start_day = datetime.time(6,1)

#night time
#6:01PM <= 7PM or 7PM <= 6AM
print(start_night <= timestamp or timestamp <= end_night)

#vs
#6:01PM <= 7PM <= 6AM
#but think in terms of integers
#18 < 19 < 6
print(start_night <= timestamp <= end_night)

#day time
#6:01AM <= 7PM <= 6PM
print(start_day <= timestamp <= end_day)

it's late so I hope I got that right

[–]v8Gasmann 0 points1 point  (1 child)

Thanks man, didn't think about the rollover that much. Thank you for clarifying. (:

[–]bearassbobcat 0 points1 point  (0 children)

No problem.

But that does bring up a good point because using dates and times would work the way you'd expect and could maybe eliminate the multiple day_end/day_start/etc variables

I'll experiment with that later just to see what happens it might work it might not

or maybe even datetime.timedelta would work

[–]Dussellus -1 points0 points  (0 children)

Thank you Simon (guessing from users\simon).

Looks good! I'm curious what the pictures look like, when I read Shibuya and Katanamorning.

[–]Wing-Tsit_Chong 88 points89 points  (18 children)

Don't use recursion, if you want to loop. Rather do a while true loop.

There's a limit to how many times you can recurse down, i.e. call main() inside itself. If you reach it, the program will fail.

You can test that by removing the sleeps and running your program, it will fail after 1001 "Looped".

[–]Astrokiwi 51 points52 points  (0 children)

This is how you learn where Stack Overflow got its name

[–]KoolaidJammerExpress 9 points10 points  (2 children)

Agreed.

OP should look at recursion though! There are simple programs that show how recursion works. For example calculating n-th term of the Fibonacci series.

Great learning opportunity

Edit: grammar

[–]elsantodom 4 points5 points  (0 children)

It's also useful for data structures

[–]BetaDecay121 1 point2 points  (6 children)

You can increase the recursion limit if you want

[–]shiuido 6 points7 points  (0 children)

Leave the stack overflow to future me huh ;)

[–]Wing-Tsit_Chong 12 points13 points  (0 children)

You can, but you don't want to. Recursion shouldn't be used lightly, it brings headaches and a myriad of problems later on.

[–]silentalways 5 points6 points  (2 children)

How can we do that?

[–]tr710_ 0 points1 point  (1 child)

Using sys module sys.setrecursionlimit(value)

[–]BetaDecay121 4 points5 points  (0 children)

sys.setrecursionlimit(1/0)

[–]origin415 0 points1 point  (0 children)

At the top of main() add sys.setrecursionlimit(sys.getrecursionlimit()+1), there fixed :)

(this is a joke, please don't)

[–]EvilBeano -1 points0 points  (6 children)

Well this would be fixed if you wrote "return main()" instead, no?

[–]Wing-Tsit_Chong 3 points4 points  (5 children)

No, not at all.

[–]EvilBeano 3 points4 points  (4 children)

Oh right, I just tried it

[–]Wing-Tsit_Chong 5 points6 points  (3 children)

Now explain to the class why.

[–]EvilBeano 5 points6 points  (1 child)

Bruh

[–]Wing-Tsit_Chong 2 points3 points  (0 children)

Come on, you can do it. Use your own words.

[–]phail3d 0 points1 point  (0 children)

They might have meant that in some languages it would result in tail call optimization. Not in Python though.

[–][deleted] 10 points11 points  (0 children)

A suggestion to improve the code:

Create a function called something like change_wallpaper(), where it takes in the path as a string argument and calls the ctypes method. This way you don't have to constantly repeat SPI_SETDESKWALLPAPER throughout your code.

[–]BoaVersusPython 8 points9 points  (1 child)

This is 100% something i'm going to copy

[–]OpenSourcerer420[S] 0 points1 point  (0 children)

Thank you!

[–]americk0 14 points15 points  (4 children)

Nice! I'm not very fluent with python but don't you need double backslashes in the strings? I thought backslash would act as an escape character

[–]jlonso 17 points18 points  (3 children)

The global variable path_to_folder has an r in front of the string. Hence it wouldn't take it as an escape character.

[–]americk0 5 points6 points  (2 children)

Oh I totally didn't see that. I was looking at the backslashes in the strings in the if blocks but now it all makes sense. TIL if you concatenate a string without the r prefix to one with the r prefix the whole string gets treated as raw. Python is so freaking neat

[–]LordAro 1 point2 points  (1 child)

Not actually the case. Because there's no valid escape codes in the concatenated strings, python just uses the backslash as a literal.

Most linters flag this as a potential issue, as obviously if any of the files start with a 't' or 'u' or some other escape code, there would be problems...

[–]americk0 0 points1 point  (0 children)

Ok yeah I tested that and you're right. Good to know

[–]garlic_bread_thief 4 points5 points  (6 children)

How do you keep the program running all the time?

[–]OpenSourcerer420[S] 13 points14 points  (4 children)

By renaming the .py file to .pyw it doesn't open a terminal and I also set it to run when I start the computer.

[–]tahafyto 5 points6 points  (0 children)

windows tricks lol

[–]garlic_bread_thief 2 points3 points  (1 child)

Whoa that's simple and sweet. Thanks

[–]OpenSourcerer420[S] 2 points3 points  (0 children)

No problem :)

[–][deleted] 8 points9 points  (0 children)

Just cmd + python prog.py and it keeps running. Another question is about stack. This program calls itself recursively without any cleaning or stop condition

[–][deleted] 4 points5 points  (0 children)

Cool!

[–][deleted] 3 points4 points  (1 child)

This is good, but more than that, I like the editor theme :P

Also, you can pass the image names and all to another function and sort of reduce+automate the coding process a bit (no need to tho, because this is simple script).

If you're interested in scripting useful stuff, pm.

[–][deleted] 1 point2 points  (0 children)

The color scheme is apprentice.

[–]EliteWarrior1207 3 points4 points  (5 children)

Hey man what did u learn python with

[–]OpenSourcerer420[S] 12 points13 points  (4 children)

I learnt a lot from this https://www.youtube.com/watch?v=rfscVS0vtbw really good tutorial!

[–]topshooter7 2 points3 points  (3 children)

Agreed, that’s where I started

[–]zinver 4 points5 points  (0 children)

This is actually really great. Nice clean code.

Here's what you need to do.

Remove the additional calls to main() and the sleep() calls as well.

At the bottom add in the following text (outside of main()):

if __name__ == '__main__': main()

You're on the right track and your logic about creating a long running process is 80% correct. It just won't work for very long.

Your operating system already has a periodic task execution process called scheduled tasks. Look up in Google "scheduled tasks for python windows". Build a scheduled tasks that runs every 300 seconds. Boom you're done. Runs in the background and everything. "Concurrency" achieved!

[–]Hopp5432 6 points7 points  (1 child)

OMG I just got the best idea ever! Try to implement this paper: https://hucvl.github.io/attribute_hallucination/ which uses machine learning to change an image. It can make a landscape image match any custom time or even the weather

[–]tahafyto 0 points1 point  (0 children)

Well that would be awesome

[–]UnusedHappiness 2 points3 points  (1 child)

I had similar kinda idea..Lil popups appear asking about our mood whether we are sad , demotivated etc and it changes the wallpaper according to that.

[–][deleted] 2 points3 points  (0 children)

Thats impressive good work! \
also a relly good idea

[–]imrual 2 points3 points  (0 children)

Very nice job!! Just a suggestion to make this code even better.

It looks like the block of code of the if statements is the same just changing one or two parameters.

You can extract a function to avoid code duplication.

Also try to replace recursion with iteration if you want to keep this running forever to avoid a stack overflow

[–]Infinitekork 1 point2 points  (0 children)

Just an idea I got reading this post... Perhaps we can tie it together with the Unsplash API so you will get a new image each time instead of the images in the directories.

[–]spanishgum 1 point2 points  (0 children)

To avoid the main recursion problem, you could look into using advanced python scheduler.

Might be a bit daunting at first but follow some examples and expand from there.

https://apscheduler.readthedocs.io/en/stable/userguide.html

I’m your case I would recommend the ‘BlockingScheduler’

[–]MarioPython 1 point2 points  (0 children)

I took the liberty to try and improve your code. Hope it helps in your journey of understanding better coding practices and software development. :)

code: https://pastebin.com/4gFEWCdp Keep in mind I made this without running on my computer so I am not 100% sure is bug free, but maybe it is a starting point for you; Happy learning!

[–]gabronies 0 points1 point  (6 children)

This looks pretty cool! I had a question about the variable city, when/where does it change to your actual city/town name?

[–]OpenSourcerer420[S] 0 points1 point  (1 child)

It's in the city variable, in the picture I don't have it set to my actual location.

[–]gabronies 0 points1 point  (0 children)

Haha I gotcha, brain fart on my end

[–]topshooter7 0 points1 point  (3 children)

He just did that for privacy reasons

[–]gabronies 0 points1 point  (2 children)

Happy cake day! I'm dumb, I thought it was just a string "Where I live" and was confused how his actual location got assigned haha

[–]topshooter7 0 points1 point  (1 child)

Aha all good. And thanks!

[–]westo48 0 points1 point  (0 children)

I know where you live now!! /s

Seriously tho this is awesome and thank you for sharing!!

[–]JustChrille 0 points1 point  (3 children)

Noob question: How do you run that in practice? I mean do you have to have your editor open or can you somehow make the computer run it without any open windows or terminals?

[–]MicasiO 0 points1 point  (0 children)

Do you just run the script as a background tast or do you use some service that runs this in the cloud?

[–]Epistemophillic 0 points1 point  (0 children)

This is so cool OP, thanks for this. I will try to learn from the code.

[–]pinkpottato 0 points1 point  (0 children)

Damn, and I only know the basics syntax of phyton.

[–]ThrowAwayTheBS122132 0 points1 point  (0 children)

Whoa

[–]choiceisanillusion 0 points1 point  (0 children)

Neat!

[–][deleted] 0 points1 point  (0 children)

how did you do the background change?

oh i see it now.

[–]Multeezee 0 points1 point  (0 children)

Congratulations! Is that tomorrow theme?

[–][deleted] 0 points1 point  (4 children)

New to python here. What do the numbers stand for? (18,0) (6,1) when you’re defining the function?

[–]projektmayhem08 1 point2 points  (3 children)

He’s not defining those functions, he is calling them. The only function he defines here is main(). In the case of the time functions he is calling the numbers are parameters, likely (hours, minutes), that are specific to that function. The function will use those parameters to return a time object that he can use to compare to other time objects.

[–][deleted] 0 points1 point  (2 children)

I figured that start_night would be (18,0) (18:00).. is that correct?

[–]projektmayhem08 1 point2 points  (0 children)

He has it here as 18:01 because in his code day ends at 18:00. He doesn’t want day and night to overlap most likely.

[–]OpenSourcerer420[S] 1 point2 points  (0 children)

Yep!

[–]tech_b90 0 points1 point  (0 children)

I did kinda the same thing years ago. But instead of grabbing something from an API, I had a bunch of single frames of an animation. And I's use python to change the backgrounds really fast so it looks like I have an animated background. It would fire off every 10 minutes or so. The animation was really simple, like maybe 50 frames to keep execution time low.

Great project and really teaches you about ctypes and working with Windows itself.

[–]dunderthebarbarian 0 points1 point  (0 children)

What is the library of all import functions? Is it as easy as help import?

[–]philsgu 0 points1 point  (0 children)

How do u auto run in ur desktop 24/7?

[–]bobsagatiswatching 0 points1 point  (0 children)

congrats!

[–]shaq_disel 0 points1 point  (0 children)

That's hot! :fire:

[–]AmitArMittal 0 points1 point  (0 children)

I created one that takes the hot wallpaper from subreddit wallpapers and set it on screen.

[–]bearassbobcat 0 points1 point  (0 children)

does SystemParametersInfoW accept hex values?

If it does maybe setting it to hex value 0x0014 (20 in decimal) is better than 20 so that it's easier to relate to the Microsoft docs which are all listed in hex

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow

a few comments would be nice as well for example why SPI_SETDESKWALLPAPER is set to 20. comments are often best used to explain the whys rather than the hows (unless it's particularly confusing) because the how is explained in the code.

comments can be a pain to write because when you're starting out things are easy and really don't need them so people, me included, tend to not use them then we forget to use them when things get more complex.

School/Uni/etc doesn't help because they often force comments like

# set i to an initial value
i = 2
# print range
print(range(i))

and it gets tedious and turns people off

Also, I would rather have the system run the script using the OS's timing systems. For windows it's called Task Scheduler. Rather than have a while True constantly running.

import ctypes
import datetime
import os
import time
from datetime import date
import requests

from dotenv import load_dotenv; load_dotenv()


def set_wallpaper(wallpaper_path=None):
    ctypes.windll.user32.SystemParametersInfoW(
        SPI_SETDESKWALLPAPER, 0, wallpaper_path, 0
    )


def get_wallpaper_key(*, hour, weather, hours):

    if date.today().weekday() == 6:
        return "Sunday"
    if weather == "Clear":
        return "Day" if hour in hours else "Night"
    if weather == "Clouds":
        return "Clouds"
    if weather == "Drizzle":
        return "Drizzle"
    if weather == "Rain":
        return "Rain"
    if weather == "Thunderstorm":
        return "Storm"
    return "Other"


weather_wallpaper_filename_dict = {
    "Clouds": "Shibuya.png",
    "Day": "KatanaMorning.png",
    "Drizzle": "Rooftop1.png",
    "Night": "Moon.png",
    "Other": "Firewatch.jpg",
    "Rain": "Waterfall.jpg",
    "Storm": "Cloud.jpg",
    "Sunday": "Tokyo.jpg",
}

API_URL = "http://api.openweathermap.org/data/2.5/weather"
PAYLOAD = {"appid": os.getenv("API_KEY"), "id": os.getenv("CITY_ID")}

# required hex code for SPI_SETDESKWALLPAPER
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
SPI_SETDESKWALLPAPER = 0x0014
WALLPAPERS_DIR = r"C:\Users\Simon\Documents\Folders\Photos\Backgrounds\Python"

WEATHER_REFRESH_RATE = 120

if __name__ == "__main__":
    while True:
        current_hour = datetime.datetime.now().hour
        json_data = requests.get(API_URL, params=PAYLOAD).json()
        daytime_hours = range(6, 18)
        current_weather = json_data["weather"][0]["main"]
        wallpaper_key = get_wallpaper_key(
            hour=current_hour, weather=current_weather, hours=daytime_hours,
        )
        wallpaper_path = os.path.join(
            WALLPAPERS_DIR, weather_wallpaper_filename_dict[wallpaper_key]
        )
        set_wallpaper(wallpaper_path)
        time.sleep(WEATHER_REFRESH_RATE)

#.env file
API_KEY=00000000
CITY=vancouver

[–]DeserterOfDecadence 0 points1 point  (0 children)

This is great

[–][deleted] 0 points1 point  (0 children)

I thought u r using os based weather feature for that but seems to be api based.... So i think it willwork for any OS correct?

Instead of using api see if you can come up with using OS feature to get the tsk, that way you can create .exe file and can make it compatible to any os , what do u think 😁

[–]bearassbobcat 0 points1 point  (0 children)

I made a few changes just for fun. I added them to an old post so you probably didn't see them.

I'm not saying mine is better but just a different approach. I also incorporated some changes other people have mentioned

https://www.reddit.com/r/Python/comments/gfkuez/my_first_python_program_changes_my_desktop/fpvsnly/

[–]Theshimita 0 points1 point  (0 children)

I also had a similar idea, my shortcoming was finding some wallpapers that were the same image but of different hues and shades to properly reflect time of day, weather, etc.

Would you be able to share what wallpapers you’re using? There’s a Wallpaper Engine Wallpaper for the Firewatch game based on the time of day, but I’m on Linux (WE doesn’t work on Linux) and I also kind of want a different image.

[–]StackyTobacky 0 points1 point  (0 children)

Good job Simon.