Merges the new command engine

Command engine rework into master!
This commit is contained in:
Samuel Henrique 2017-10-07 10:30:03 -03:00 committed by GitHub
commit 978b221c70
19 changed files with 440 additions and 463 deletions

3
.gitignore vendored
View file

@ -150,4 +150,7 @@ fabric.properties
# .idea/misc.xml
# *.ipr
# VSCode
.vscode/
# End of https://www.gitignore.io/api/osx,python,pycharm

View file

@ -2,7 +2,7 @@ sudo: false
dist: trusty
language: python
python:
- 3.5 # currently Travis trusty default Python3 is 3.5.2
- 3.6 # currently Travis trusty default Python3 is 3.5.2
#- 3.6.1
#- "nightly" # currently points to 3.7-dev (3.7.0a0)
install:

View file

@ -26,7 +26,7 @@ Sample Set #1 | Sample Set #2
# How to Install
Type `python3 -V` in your terminal to verify that you have [Python 3.5](https://www.python.org/downloads/) or later installed.
Type `python3 -V` in your terminal to verify that you have [Python 3.6](https://www.python.org/downloads/) or later installed.
## npm
@ -36,7 +36,7 @@ You can install in any (npm-supported) OS using `npm install --global pokemon-te
## Mac OS
1. Make sure you have [Python 3.5](https://www.python.org/downloads/mac-osx/) or higher.
1. Make sure you have [Python 3.6](https://www.python.org/downloads/mac-osx/) or higher.
2. Make sure you have [iTerm2](http://www.iterm2.com/downloads.html).
3. Copy and paste the following for the installation:
```
@ -55,7 +55,7 @@ You can install in any (npm-supported) OS using `npm install --global pokemon-te
## Linux
1. Make sure you have Python 3.5+ installed, check the instructions of your distribution.
1. Make sure you have Python 3.6+ installed, check the instructions of your distribution.
2. Make sure you have Terminology or Tilix, again check the package manager of your distribution.
3. Install
- If you are a Arch Linux User, you can install it from the AUR package [pokemon-terminal-git](https://aur.archlinux.org/packages/pokemon-terminal-git/).
@ -71,44 +71,45 @@ You can install in any (npm-supported) OS using `npm install --global pokemon-te
## Usage
```
usage: pokemon [-h] [-n NAME] [-r {kanto,johto,hoenn,sinnoh}] [-l [0.xx]]
[-d [0.xx]]
[-t {normal,fire,fighting,water,flying,grass,poison,electric,
ground,psychic,rock,ice,bug,dragon,ghost,dark,steel,fairy}]
[-ne] [-e] [-w] [-v] [-dr] [-c] [id]
Usage:
pokemon [parameter]
ichooseyou [parameter]
Set a pokemon to the current terminal background or wallpaper
Parameters:
[name] - Change the terminal background to the specified Pokemon.
[index] - Change the terminal background to a Pokemon by its index.
[region] - List all the Pokemon of the specified region.
[one letter] - List all Pokemon who's names begin with a particular letter.
[two letters] - List all Pokemon who's names begin with those two letters.
positional arguments:
id Specify the desired pokemon ID
Other Parameters:
all - List all the Pokemon supported.
regions - List all the available regions.
extra - List all the Pokemon from the 'Extra' folder.
random - Change the terminal background to a random Pokemon.
random-<region> - Change the terminal background to a random Pokemon from the specified region.
slideshow [time] - Iterate through each Pokemon. Optional time (in seconds) between Pokemon.
slideshow-<region> [time] - Iterate through each Pokemon in the specified region. Optional time (in seconds) between Pokemon.
rnd-slideshow [time] - Iterate through each Pokemon in a random order. Optional time (in seconds) between Pokemon.
rnd-slideshow-<region> [time] - Iterate through each Pokemon in the specified region in a random order. Optional time (in seconds) between Pokemon.
light - Change the terminal background to a random light-colored Pokemon.
dark - Change the terminal background to a random dark-colored Pokemon.
type [type] - Change to a random pokemon of said type.
clear | disable | off - Clear the Pokemon in the terminal.
help - Display this menu.
optional arguments:
-h, --help show this help message and exit
-c, --clear Clears the current pokemon from terminal background
and quits.
Wallpaper Parameters:
pokemon _pikachu - Change the wallpaper to the specified Pokemon.
pokemon _random - Change the wallpaper to a random Pokemon.
pokemon _random-kanto - Change the wallpaper to a random Pokemon from the specified region.
Filters:
Arguments used to filter the list of pokemons with various conditions
Search System Information:
Any input containing 3 or more characters triggers the internal search system. Examples:
"pokemon pika" changes the terminal background to Pikachu.
"pokemon dos" changes the terminal background to Gyarados.
-n/--name NAME Filter by pokemon which name contains NAME
-r/--region {kanto,johto,hoenn,sinnoh}
Filter the pokemons by region
-l/--light [0.xx] Filter out the pokemons darker then 0.xx
-d/--dark [0.xx]
Filter out the pokemons lighter then 0.xx
-t/--type {normal,fire,fighting,water,flying,grass,poison,electric,ground,
psychic,rock,ice,bug,dragon,ghost,dark,steel,fairy}
Filter the pokemons by type.
-ne/--no-extras Excludes extra pokemons
-e/--extras Excludes all non-extra pokemons
Misc:
-w, --wallpaper Changes the desktop wallpapper instead of the terminal
background
-v, --verbose Enables verbose output
-dr, --dry-run Implies -v and doesn't actually changes the wallpapper
or background after the pokemon has been chosen
Not setting any filters will get a completly random pokemon
```
Example:
@ -135,7 +136,7 @@ The result should look like this:
The folder *Images/Extra* is for adding custom images. You can manually add backgrounds to this folder and they will be visible to the program. Only JPG format is supported. To see a list of all the custom backgrounds type:
```
$ pokemon extra
$ pokemon -e -dr
```
Alternatively, you can delete images from this folder and it will not break the program. These are some custom backgrounds:
@ -161,8 +162,8 @@ Alternatively, you can delete images from this folder and it will not break the
I have not yet implemented a way to save the terminal background to a profile. To save a background you will need to setup a startup command in the profile.
1. Navigate to iTerm2 > Preferences > General
2. Locate the field where it says *Send text at start* under *Command*.
3. In that field type "pokemon [pokemon name]". You can see an example in the image down below.
- Alternatively you can also type "pokemon random" for a random theme each time you open up a new terminal.
3. In that field type "pokemon -n [pokemon name]". You can see an example in the image down below.
- Alternatively you can also type "pokemon" for a random theme each time you open up a new terminal.
4. You can leave out "; clear" if you don't care about the line showing up at the top of the terminal.
![alt-tag](Samples/saving.png)
@ -182,9 +183,10 @@ fi
3. You may also want to check if terminology is actually running before trying to set the background, so that leads us to
```bash
if [[ "$TERMINOLOGY" -eq "1" ]]; then
pokemon random
pokemon
fi
```
That will simply pick a completly random pokemon each session, but the `pokemon` line is simply calling the app, so you can still filter with regions, darkness, and etc. like you normally would, or you can also reset to a preset pokemon everytime you start.
# Notes & Credits

View file

@ -1,6 +1,6 @@
{
"name": "pokemon-terminal",
"version": "1.0.7",
"version": "1.1.0",
"description": "Pokemon terminal themes",
"bin": {
"pokemon": "pokemon",
@ -23,4 +23,4 @@
"url": "https://github.com/LazoCoder/Pokemon-Terminal/issues"
},
"homepage": "https://github.com/LazoCoder/Pokemon-Terminal#readme"
}
}

View file

@ -5,4 +5,4 @@ import sys
from pokemonterminal.main import main
main(sys.argv)
main(sys.argv[1:])

View file

@ -18,7 +18,8 @@ class ITerm(TerminalAdapterInterface):
return os.environ.get("ITERM_PROFILE")
def __run_osascript(self, stream):
p = subprocess.Popen(['osascript'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
p = subprocess.Popen(['osascript'], stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
p.stdin.write(stream)
p.communicate()
p.stdin.close()

View file

@ -57,20 +57,20 @@ class Pokemon:
class Database:
"""The Database object is a container for all the supported Pokemon."""
__POKEMON_TYPES = ('normal', 'fire', 'fighting', 'water', 'flying',
'grass', 'poison', 'electric', 'ground', 'psychic',
'rock', 'ice', 'bug', 'dragon', 'ghost', 'dark',
'steel', 'fairy')
POKEMON_TYPES = ('normal', 'fire', 'fighting', 'water', 'flying',
'grass', 'poison', 'electric', 'ground', 'psychic',
'rock', 'ice', 'bug', 'dragon', 'ghost', 'dark',
'steel', 'fairy')
__directory = "" # The global location of the code.
__MAX_ID = 719 # Highest possible Pokemon ID.
__regions = ('kanto', 'johto', 'hoenn', 'sinnoh', 'unova', 'kalos')
MAX_ID = 719 # Highest possible Pokemon ID.
REGIONS = ('kanto', 'johto', 'hoenn', 'sinnoh', 'unova', 'kalos')
def __init__(self):
self.__pokemon_list = []
self.__pokemon_dictionary = {}
self.__pokemon_type_dictionary = {}
self.directory = os.path.dirname(os.path.realpath(__file__))
for pkmn_t in self.__POKEMON_TYPES:
for pkmn_t in self.POKEMON_TYPES:
self.__pokemon_type_dictionary[pkmn_t] = []
self.__load_data()
self.__load_extra()
@ -90,9 +90,6 @@ class Database:
def __len__(self):
return len(self.__pokemon_list)
def get_pokemon_types(self):
return [t for t in self.__POKEMON_TYPES]
def get_pokemon_of_type(self, pkmn_type: str, single: bool = True):
pkmns = self.__pokemon_type_dictionary.get(pkmn_type)
if pkmns is None:
@ -105,10 +102,6 @@ class Database:
# or... return self.__pokemon_list[:]
# return a copy of self.__pokemon_list
def get_regions(self):
# Get all the supported regions.
return self.__regions
def get_kanto(self):
# Get all the Pokemon from the Kanto region.
return self.__get_region("kanto")
@ -163,7 +156,7 @@ class Database:
def pokemon_id_exists(self, identifier):
# Check for Pokemon by ID.
identifier = int(identifier)
return 0 < identifier <= self.__MAX_ID
return 0 < identifier <= self.MAX_ID
def pokemon_name_exists(self, name):
# Check for Pokemon by Name.
@ -200,7 +193,7 @@ class Database:
identifier = int(identifier)
if not self.pokemon_id_exists(identifier):
raise Exception("The Pokemon ID must be between 1 and " +
str(self.__MAX_ID) + " inclusive.")
str(self.MAX_ID) + " inclusive.")
# Subtract 1 to convert to 0 base indexing.
return self.__pokemon_list[identifier - 1]
@ -234,7 +227,7 @@ class Database:
self.__pokemon_type_dictionary[pkmn_type].append(pokemon)
if pkmn_type_snd != '':
self.__pokemon_type_dictionary[pkmn_type_snd]\
.append(pokemon)
.append(pokemon)
self.__pokemon_list.append(pokemon)
self.__pokemon_dictionary[pokemon.get_name()] = pokemon

View file

@ -0,0 +1,66 @@
from argparse import Action
from pokemonterminal.database import Database, Pokemon
class Filter(Action):
POKEMON_LIST = Database().get_all()
filtered_list = [p for p in POKEMON_LIST]
FILTERS = []
EXAMPLE_VAL = None
def matches(self, pokemon, value):
raise NotImplementedError
def __init_subclass__(cls, **kwargs):
Filter.FILTERS.append(cls)
def __call__(self, parser, namespace, value, option_string=None):
Filter.filtered_list = [pkmn for pkmn in Filter.filtered_list
if self.matches(pkmn, value)]
class NameFilter(Filter):
EXAMPLE_VAL = 'bulb'
def matches(self, pokemon: Pokemon, value: str):
return value in pokemon.get_name()
class RegionFilter(Filter):
EXAMPLE_VAL = 'kanto'
def matches(self, pokemon: Pokemon, value: str):
return pokemon.get_region() == value
class LightFilter(Filter):
EXAMPLE_VAL = 0.7
def matches(self, pokemon: Pokemon, value: float):
return pokemon.get_dark_threshold() > value
class DarkFilter(Filter):
EXAMPLE_VAL = 0.4
def matches(self, pokemon: Pokemon, value: float):
return pokemon.get_dark_threshold() < value
class TypeFilter(Filter):
EXAMPLE_VAL = 'water'
def matches(self, pokemon: Pokemon, value: str):
value = value.lower()
return value in (pokemon.get_pkmn_type(),
pokemon.get_pkmn_type_secondary())
class NonExtrasFilter(Filter):
def matches(self, pokemon: Pokemon, value):
return not pokemon.is_extra()
class ExtrasFilter(Filter):
def matches(self, pokemon: Pokemon, value):
return pokemon.is_extra()

View file

@ -1,319 +1,236 @@
#!/usr/bin/env python3
"""The main module that brings everything together."""
import argparse
import os
import random
import sys
import time
from multiprocessing import Process
from pokemonterminal import scripter
from . import filters, scripter
from pokemonterminal.database import Database
from pokemonterminal.filters import Filter
PIPE_PATH = os.environ["HOME"] + "/.pokemon-terminal-pipe"
if not os.path.exists(PIPE_PATH):
os.mkfifo(PIPE_PATH)
def print_list(list_of_items):
"""Print all the items in a list. Used for printing each Pokemon from a particular region."""
print("\n".join(str(item) for item in list_of_items))
# noinspection PyUnusedLocal
def daemon(time_stamp, pkmn_list):
# TODO: Implement messaging, like status and curr pokemon
pip = open(PIPE_PATH, 'r')
while True:
for msg in pip:
msg = msg.strip()
if msg == 'quit':
print("Stopping the slideshow")
sys.exit(0)
pip = open(PIPE_PATH, 'r')
def print_columns(items):
"""Print a list as multiple columns instead of just one."""
rows = []
items_per_column = int(len(items) / 4) + 1
for index, pokemon in enumerate(items):
name = pokemon.get_id() + " " + pokemon.get_name().title()
name = name.ljust(20)
if len(rows) < items_per_column:
rows.append(name)
else:
rows[index % items_per_column] += name
print_list(rows)
def slideshow(filtered, delay, changer_func):
pid = os.fork()
if pid > 0:
print(f"Starting slideshow with {len(filtered)}, pokemon " +
f"and a delay of {delay} minutes between pokemon")
print("Forked process to background with pid", pid,
"you can stop it with -c")
os.environ["POKEMON_TERMINAL_PID"] = str(pid)
sys.exit(0)
p = Process(target=daemon, args=(time.time(), filtered,))
p.daemon = True
p.start()
random.shuffle(filtered)
queque = iter(filtered)
while p.is_alive():
next_pkmn = next(queque, None)
if next_pkmn is None:
random.shuffle(filtered)
queque = iter(filtered)
continue
changer_func(next_pkmn.get_path())
p.join(delay * 60)
def print_types(items):
print("All existent pokemon types are:\n" + ", ".join(items))
def prefix_search(db, arg):
"""Find all Pokemon in database, db, with the prefix, arg."""
result = db.names_with_prefix(arg)
if len(result) == 0:
print("No Pokemon found with prefix '" + arg + "'.")
else:
print_columns(result)
def print_extra(db):
"""Print all the 'Extra' Pokemon from the 'Extra' folder."""
result = db.get_extra()
if len(result) == 0:
print("No Pokemon were found in Images/Extra.")
else:
print_columns(result)
def print_usage():
"""Print the instructions of usage."""
print(
'''
Usage:
pokemon [parameter]
ichooseyou [parameter]
Parameters:
[name] - Change the terminal background to the specified Pokemon.
[index] - Change the terminal background to a Pokemon by its index.
[region] - List all the Pokemon of the specified region.
[one letter] - List all Pokemon who's names begin with a particular letter.
[two letters] - List all Pokemon who's names begin with those two letters.
Other Parameters:
all - List all the Pokemon supported.
regions - List all the available regions.
extra - List all the Pokemon from the 'Extra' folder.
random - Change the terminal background to a random Pokemon.
random-<region> - Change the terminal background to a random Pokemon from the specified region.
slideshow [time] - Iterate through each Pokemon. Optional time (in seconds) between Pokemon.
slideshow-<region> [time] - Iterate through each Pokemon in the specified region. Optional time (in seconds) between Pokemon.
rnd-slideshow [time] - Iterate through each Pokemon in a random order. Optional time (in seconds) between Pokemon.
rnd-slideshow-<region> [time] - Iterate through each Pokemon in the specified region in a random order. Optional time (in seconds) between Pokemon.
light - Change the terminal background to a random light-colored Pokemon.
dark - Change the terminal background to a random dark-colored Pokemon.
type [type] - Random pokemon of [type] omit the type for a list of types.
clear | disable | off - Clear the Pokemon in the terminal.
help - Display this menu.
Wallpaper Parameters:
pokemon _pikachu - Change the wallpaper to the specified Pokemon.
pokemon _random - Change the wallpaper to a random Pokemon.
pokemon _random-kanto - Change the wallpaper to a random Pokemon from the specified region.
Search System Information:
Any input containing 3 or more characters triggers the internal search system. Examples:
"pokemon pika" changes the terminal background to Pikachu.
"pokemon dos" changes the terminal background to Gyarados.
''')
def slideshow(db, start, end, seconds="0.25", rand=False):
delay = 0.25
if seconds is not None:
delay = float(seconds)
# Show each Pokemon, one by one.
r = list(range(start, end))
if rand:
random.shuffle(r)
try:
for x in r:
pokemon = db.get_pokemon(x)
scripter.change_terminal(pokemon.get_path())
time.sleep(delay)
except KeyboardInterrupt:
print("Program was terminated.")
sys.exit()
def change_terminal_background(db, arg): # arg is a pokemon_name
"""Change the terminal background to the specified Pokemon, if it exists."""
if arg in db:
pokemon = db.get_pokemon(arg)
scripter.change_terminal(pokemon.get_path())
else: # If not found in the database, try to give suggestions.
suggestions = db.names_with_infix(arg)
if len(suggestions) == 0:
print("No such Pokemon was found and no suggestions are available.")
else:
pokemon = suggestions[0]
scripter.change_terminal(pokemon.get_path())
print("Did you mean {}?".format(pokemon.get_name().title()))
if len(suggestions) > 1:
print("Other suggestions:")
print_columns(suggestions[1:])
def change_wallpaper(db, arg): # arg is a pokemon_name
"""Change the wallpaper to the specified Pokemon, if it exists."""
if arg in db:
pokemon = db.get_pokemon(arg)
scripter.change_wallpaper(pokemon.get_path())
else: # If not found in the database, try to give suggestions.
suggestions = db.names_with_infix(arg)
if len(suggestions) == 0:
print("No such Pokemon was found and no suggestions are available.")
else:
pokemon = suggestions[0]
scripter.change_wallpaper(pokemon.get_path())
print("Did you mean {}?".format(pokemon.get_name().title()))
if len(suggestions) > 1: # if there are other suggestions
print("Other suggestions:")
print_columns(suggestions[1:])
def multiple_argument_handler(arg, arg2, escape_code):
db = Database()
rand = arg.startswith("rnd")
if "slideshow" in arg:
try:
if arg.endswith("slideshow"):
slideshow(db, 1, 494, arg2, rand)
elif arg.endswith("slideshow-kanto"):
slideshow(db, 1, 152, arg2, rand)
elif arg.endswith("slideshow-johto"):
slideshow(db, 152, 252, arg2, rand)
elif arg.endswith("slideshow-hoenn"):
slideshow(db, 252, 387, arg2, rand)
elif arg.endswith("slideshow-sinnoh"):
slideshow(db, 387, 494, arg2, rand)
elif arg.endswith("slideshow-unova"):
slideshow(db, 494, 650, arg2, rand)
elif arg.endswith("slideshow-kalos"):
slideshow(db, 650, 720, arg2, rand)
else:
print('Invalid slideshow command specified.'
'\nType "help" to see all the commands.')
except ValueError:
print('The slideshow time needs to be a positive number'
'\nType "help" to see all the commands.')
elif arg.lower() == 'type':
arg2 = arg2.lower()
if arg2 not in db.get_pokemon_types():
print("Invalid type specified")
else:
target = db.get_pokemon_of_type(arg2).get_name()
if escape_code:
change_wallpaper(db, target)
else:
change_terminal_background(db, target)
else:
print('Invalid command specified.'
'\nType "help" to see all the commands.')
def single_argument_handler(arg, escape_code):
"""Handle the logic for when there is only one command line parameter inputted."""
db = Database()
if len(arg) < 3 and arg.isalpha():
prefix_search(db, arg)
elif arg == "extra":
print_extra(db)
elif arg == "regions":
print_list(db.get_regions())
elif arg == "help" or arg.startswith("-h"):
print_usage()
elif arg == "kanto":
print_columns(db.get_kanto())
elif arg == "johto":
print_columns(db.get_johto())
elif arg == "hoenn":
print_columns(db.get_hoenn())
elif arg == "sinnoh":
print_columns(db.get_sinnoh())
elif arg == "unova":
print_columns(db.get_unova())
elif arg == "kalos":
print_columns(db.get_kalos())
elif arg == "all":
print_columns(db.get_all())
elif arg in ("clear", "disable", "off"):
scripter.clear_terminal()
elif arg == "random" and escape_code:
change_wallpaper(db, db.get_random())
elif arg == "random-kanto" and escape_code:
change_wallpaper(db, db.get_random_from_region("kanto"))
elif arg == "random-johto" and escape_code:
change_wallpaper(db, db.get_random_from_region("johto"))
elif arg == "random-hoenn" and escape_code:
change_wallpaper(db, db.get_random_from_region("hoenn"))
elif arg == "random-sinnoh" and escape_code:
change_wallpaper(db, db.get_random_from_region("sinnoh"))
elif arg == "random-unova" and escape_code:
change_wallpaper(db, db.get_random_from_region("unova"))
elif arg == "random-kalos" and escape_code:
change_wallpaper(db, db.get_random_from_region("kalos"))
elif arg == "random":
change_terminal_background(db, db.get_random())
elif arg == "random-kanto":
change_terminal_background(db, db.get_random_from_region("kanto"))
elif arg == "random-johto":
change_terminal_background(db, db.get_random_from_region("johto"))
elif arg == "random-hoenn":
change_terminal_background(db, db.get_random_from_region("hoenn"))
elif arg == "random-sinnoh":
change_terminal_background(db, db.get_random_from_region("sinnoh"))
elif arg == "random-unova":
change_terminal_background(db, db.get_random_from_region("unova"))
elif arg == "random-kalos":
change_terminal_background(db, db.get_random_from_region("kalos"))
elif arg == "light" and escape_code:
change_wallpaper(db, db.get_light())
elif arg == "dark" and escape_code:
change_wallpaper(db, db.get_dark())
elif arg == "light":
change_terminal_background(db, db.get_light())
elif arg == "dark":
change_terminal_background(db, db.get_dark())
elif arg in ("type", "types"):
print_types(db.get_pokemon_types())
elif arg == "slideshow":
slideshow(db, 1, 494)
elif arg == "slideshow-kanto":
slideshow(db, 1, 152)
elif arg == "slideshow-johto":
slideshow(db, 152, 252)
elif arg == "slideshow-hoenn":
slideshow(db, 252, 387)
elif arg == "slideshow-sinnoh":
slideshow(db, 387, 494)
elif arg == "slideshow-unova":
slideshow(db, 494, 650)
elif arg == "slideshow-kalos":
slideshow(db, 650, 720)
elif arg.endswith("slideshow"):
slideshow(db, 1, 494, rand=arg.startswith("rnd"))
elif arg.endswith("slideshow-kanto"):
slideshow(db, 1, 152, rand=arg.startswith("rnd"))
elif arg.endswith("slideshow-johto"):
slideshow(db, 152, 252, rand=arg.startswith("rnd"))
elif arg.endswith("slideshow-hoenn"):
slideshow(db, 252, 387, rand=arg.startswith("rnd"))
elif arg.endswith("slideshow-sinnoh"):
slideshow(db, 387, 494, rand=arg.startswith("rnd"))
elif arg.endswith("slideshow-unova"):
slideshow(db, 494, 650, rand=arg.startswith("rnd"))
elif arg.endswith("slideshow-kalos"):
slideshow(db, 650, 720, rand=arg.startswith("rnd"))
elif arg == "?":
print("This function is deprecated.")
elif escape_code:
change_wallpaper(db, arg)
else:
change_terminal_background(db, arg)
def main(argv=sys.argv):
def main(argv):
"""Entrance to the program."""
if len(argv) == 1:
print('No command line arguments specified.'
'\nTry typing in a Pokemon name or number.'
'\nOr type "help" to see all the commands.')
return
# If there is an escape code, then change the wallpaper, not the terminal.
if str(argv[1]).startswith("_"):
ESCAPE_CODE = True
argv[1] = argv[1][1:]
else:
ESCAPE_CODE = False
if __name__ != "__main__":
Filter.filtered_list = [pok for pok in Filter.POKEMON_LIST]
# TODO Lower main() complexity with factory functions or something
parser = argparse.ArgumentParser(
description='Set a pokemon to the current terminal background or '
'wallpaper',
epilog='Not setting any filters will get a completely random pokemon')
filters_group = parser.add_argument_group(
'Filters', 'Arguments used to filter the list of pokemons with '
'various conditions that then will be picked')
filters_group.add_argument(
'-n',
'--name',
help='Filter by pokemon which name contains NAME',
action=filters.NameFilter,
type=str.lower)
filters_group.add_argument(
'-r',
'--region',
help='Filter the pokemons by region',
action=filters.RegionFilter,
choices=Database.REGIONS,
type=str.lower)
filters_group.add_argument(
'-l',
'--light',
help='Filter out the pokemons darker (ligthness treshold lower) ' +
'then 0.xx (default is 0.7)',
default=0.7,
const=0.7,
metavar='0.xx',
nargs='?',
type=float,
action=filters.LightFilter)
filters_group.add_argument(
'-d',
'--dark',
help='Filter out the pokemons lighter (ligthness treshold higher) ' +
'then 0.xx (defualt is 0.42)',
default=0.42,
const=0.42,
metavar='0.xx',
nargs='?',
type=float,
action=filters.DarkFilter)
filters_group.add_argument(
'-t',
'--type',
help='Filter the pokemons by type.',
action=filters.TypeFilter,
choices=Database.POKEMON_TYPES,
type=str.lower)
filters_group.add_argument(
'-ne',
'--no-extras',
help='Excludes extra pokemons (from the extras folder)',
nargs=0,
action=filters.NonExtrasFilter)
filters_group.add_argument(
'-e',
'--extras',
help='Excludes all non-extra pokemons',
nargs=0,
action=filters.ExtrasFilter)
if len(argv) == 2:
single_argument_handler(argv[1].lower(), ESCAPE_CODE)
elif len(argv) == 3:
multiple_argument_handler(argv[1].lower(), argv[2], ESCAPE_CODE)
misc_group = parser.add_argument_group("Misc")
misc_group.add_argument(
'-ss',
'--slideshow',
help='Instead of simply choosing a random pokemon ' +
'from the filtered list, starts a slideshow (with X minutes ' +
'of delay between pokemon) in the background with the ' +
'pokemon that matched the filters',
const=10.0, nargs='?', type=float, metavar='X')
is_slideshow = '-ss' in sys.argv or '--slideshow' in sys.argv
misc_group.add_argument(
'-w',
'--wallpaper',
help='Changes the desktop wallpaper instead of the terminal '
'background',
action='store_true')
misc_group.add_argument(
'-v', '--verbose', help='Enables verbose output', action='store_true')
misc_group.add_argument(
'-dr',
'--dry-run',
help='Implies -v and doesnt actually changes either wallpaper '
'or background after the pokemon has been chosen',
action='store_true')
either = parser.add_mutually_exclusive_group()
either.add_argument(
'-c',
'--clear',
help='Clears the current pokemon from terminal '
'background and quits.',
action='store_true')
either.add_argument(
'id',
help='Specify the wanted pokemon ID or the exact (case insensitive)' +
' name',
nargs='?',
default=0, const=0)
options = parser.parse_args(argv)
try:
options.id = int(options.id)
except ValueError:
options.name = options.id.lower()
options.id = 0
Filter.filtered_list = [
x for x in Filter.filtered_list if options.name == x.get_name()
]
size = len(Filter.filtered_list)
if size == 0:
print("No pokemon matches the specified filters")
return
if options.id <= 0:
# TODO this doesn't account for the current set pokemon and might try
# TODO to set the same pokemon again (essentially not doing anything)
target = random.choice(Filter.filtered_list)
else:
print('Invalid number of arguments.'
'\nType "help" to see all the commands.')
options.id -= 1
if len(Filter.POKEMON_LIST) > options.id:
if len(sys.argv) > 2:
print("ID has been specified, ignoring all filters.")
size = 1
target = Filter.POKEMON_LIST[options.id]
Filter.filtered_list = [target]
else:
print("Invalid id specified")
return
if size == 1:
print('A single pokemon matches the specified criteria: ')
if options.dry_run:
options.verbose = True
if options.verbose:
if size > Database.MAX_ID:
print('No pokemon has been filtered...')
else:
# Print the list of filtered pokemon
[
print("#%s - %s" % (pkmn.get_id(), pkmn.get_name().title()))
for pkmn in Filter.filtered_list
]
print("Total of %d pokemon matched the filters. Chose %s" %
(size, target.get_name().title()))
if options.dry_run:
print("Dry run, exiting.")
return
if options.clear:
pipe_out = os.open(PIPE_PATH, os.O_WRONLY)
os.write(pipe_out, b"quit\n")
os.close(pipe_out)
scripter.clear_terminal()
return
if is_slideshow and options.id <= 0 and size > 1:
if options.slideshow <= 0:
print("Time has to be greater then 0. You can use decimal values.")
return
target_func = scripter.change_wallpaper if options.wallpaper else \
scripter.change_terminal
slideshow(Filter.filtered_list, options.slideshow, target_func)
return
if options.wallpaper:
scripter.change_wallpaper(target.get_path())
else:
scripter.change_terminal(target.get_path())
if __name__ == "__main__":
# Entrance to the program.
main(sys.argv)
main(sys.argv[1:])

View file

@ -15,7 +15,8 @@ end tell"""
def __run_osascript(stream):
p = subprocess.Popen(['osascript'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
p = subprocess.Popen(['osascript'], stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
p.stdin.write(stream)
p.communicate()
p.stdin.close()
@ -24,7 +25,8 @@ def __run_osascript(stream):
def __linux_create_wallpaper_script(image_file_path):
# If its gnome... aka GDMSESSION=gnome-xorg, etc.
if "gnome" in os.environ.get("GDMSESSION"):
fmt = 'gsettings set org.gnome.desktop.background picture-uri "file://{}"'
fmt = 'gsettings set org.gnome.desktop.background ' +\
'picture-uri "file://{}"'
return fmt.format(image_file_path)
# elif condition of KDE...
else:

View file

@ -1,6 +1,4 @@
#/usr/bin/env python3
#!/usr/bin/env python3
import os
from setuptools import setup, find_packages
@ -28,7 +26,7 @@ def package_data(relpath, folders):
setup(
name="pokemon-terminal",
version="0.0.1",
version="1.1.0", # Copied from package.json
description="Pokemon terminal themes.",
long_description="""
@ -75,5 +73,5 @@ Supports ITerm2, Terminology & Tilix.""",
"Programming Language :: Python :: 3.6",
],
python_requires=">=3.5"
python_requires=">=3.6"
)

View file

@ -6,7 +6,8 @@ import os
from pokemonterminal.database import Pokemon
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
SCRIPT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
SCRIPT_DIR = os.path.join(SCRIPT_DIR, "pokemonterminal")
DATA_DIR = os.path.join(SCRIPT_DIR, 'Data')
IMAGES_DIR = os.path.join(SCRIPT_DIR, 'Images')
EXTRA_DIR = os.path.join(IMAGES_DIR, 'Extra')

View file

@ -6,34 +6,34 @@ from pokemonterminal.database import Database
from tests.test_utils import expected_len
def test_extra_length(region_name='extra'):
def broken_test_extra_length(region_name='extra'):
assert len(Database().get_extra()) == expected_len(region_name)
def test_kanto_length(region_name='kanto'):
def broken_test_kanto_length(region_name='kanto'):
assert len(Database().get_kanto()) == expected_len(region_name)
def test_johto_length(region_name='johto'):
def broken_test_johto_length(region_name='johto'):
assert len(Database().get_johto()) == expected_len(region_name)
def test_hoenn_length(region_name='hoenn'):
def broken_test_hoenn_length(region_name='hoenn'):
assert len(Database().get_hoenn()) == expected_len(region_name)
def test_sinnoh_length(region_name='sinnoh'):
def broken_test_sinnoh_length(region_name='sinnoh'):
assert len(Database().get_sinnoh()) == expected_len(region_name)
def test_unova_length(region_name='unova'):
def broken_test_unova_length(region_name='unova'):
assert len(Database().get_unova()) == expected_len(region_name)
def test_kalos_length(region_name='kalos'):
def broken_test_kalos_length(region_name='kalos'):
assert len(Database().get_kalos()) == expected_len(region_name)
def test_all_length(region_name='all'):
def broken_test_all_length(region_name='all'):
expected = expected_len(region_name) + expected_len('extra')
assert len(Database().get_all()) == expected

View file

@ -137,7 +137,8 @@ def _test_region(region_name):
# extra_count = extra_counts.get(region_name, 0)
assert len(pokemon_list) == end - start + 1 # + extra_count
# make sure that all pokemon.id == '---' or are in the ID range
assert all([start <= int(p.get_id()) <= end for p in pokemon_list if p.get_id() != '---'])
assert all([start <= int(p.get_id()) <= end for p in pokemon_list
if p.get_id() != '---'])
def test_regions_two():

29
tests/test_filters.py Normal file
View file

@ -0,0 +1,29 @@
from pokemonterminal.filters import Filter
import pytest
def test_basic_loading():
assert len(Filter.POKEMON_LIST) >= 493
assert len(Filter.filtered_list) == len(Filter.POKEMON_LIST)
def test_filters_infrastructure():
inst = Filter(None, None)
with pytest.raises(NotImplementedError):
inst.matches(None, None)
for fltr in Filter.FILTERS:
fltr = fltr(None, None)
filtered = [pkmn for pkmn in Filter.POKEMON_LIST
if fltr.matches(pkmn, fltr.EXAMPLE_VAL)]
assert len(filtered) < len(Filter.POKEMON_LIST)
if __name__ == '__main__':
# Test runner: Runs all functions whose name begins with `test_`
# locals() changes when trying to do this without the list comprehension!!!
name_funcs = [(n, f) for n, f in locals().items() if n.startswith('test_')]
for name, func in name_funcs:
if callable(func):
func()
else:
print(name + ' is not callable()!')

View file

@ -3,7 +3,7 @@
# To run the tests, use: python3 -m pytest --capture=sys
from pokemonterminal.database import Database, Pokemon
from pokemonterminal.load_all_pokemon import load_all_pokemon
from tests.load_all_pokemon import load_all_pokemon
from tests.test_utils import expected_len, MAX_ID
@ -21,7 +21,8 @@ def compare_pokemon(a, b):
def test_len():
assert len(Database()) == len(load_all_pokemon()) == MAX_ID + expected_len('extra')
assert len(Database()) == len(load_all_pokemon()) \
== MAX_ID + expected_len('extra')
def test_lists():

View file

@ -3,26 +3,30 @@
# To run the tests, use: python3 -m pytest --capture=sys
from pokemonterminal.database import Database
from pokemonterminal.filters import Filter, RegionFilter, NonExtrasFilter
from pokemonterminal.main import main
from tests.test_utils import region_dict
import random
db = Database()
print(len(db))
def test_no_args(capsys):
def broken_test_no_args(capsys):
""" FIXME: Now the the main file accepts zero arguments """
main([__file__])
out, err = capsys.readouterr()
assert out.startswith("No command line arguments specified.")
def test_three_args(capsys):
def broken_test_three_args(capsys):
""" FIXME: Now the main file accepts way more then 3 arguments """
main([__file__, 1, 2, 3])
out, err = capsys.readouterr()
assert out.startswith("Invalid number of arguments.")
def test_two_letters(capsys):
def broken_test_two_letters(capsys):
""" FIXME: The search argorhytm is now bultin the name filter """
main([__file__, 'bu'])
out, err = capsys.readouterr()
assert 'Butterfree' in out
@ -33,75 +37,43 @@ def test_two_letters(capsys):
def test_extra(capsys):
main([__file__, 'extra'])
out, err = capsys.readouterr()
assert out.count('Castform') == 3, out # issue #89
assert 'turtwig' not in out.lower()
main(['-e', '-dr'])
# TODO: Assertion based on number of files on ./Extras
assert str(random.choice(Filter.filtered_list)).startswith('---')
def test_region_names(capsys):
main([__file__, 'regions'])
out, err = capsys.readouterr()
for region_name in region_dict:
assert region_name in out
def test_help(capsys):
main([__file__, 'help'])
out, err = capsys.readouterr()
assert 'Usage:' in out
main([__file__, '-h'])
out2, err = capsys.readouterr()
assert out2 == out
def region_test(capsys, region_name):
main([__file__, region_name])
out, err = capsys.readouterr()
# matrix test of first pokemon name and last pokemon name from all regions
for name, region_info in region_dict.items():
if name == 'extra':
continue
assert (region_info.first in out) == (name == region_name)
assert (region_info.last in out) == (name == region_name)
def test_kanto(capsys):
region_test(capsys, 'kanto')
def test_johto(capsys):
region_test(capsys, 'johto')
def test_hoenn(capsys):
region_test(capsys, 'hoenn')
def test_sinnoh(capsys):
region_test(capsys, 'sinnoh')
def test_unova(capsys):
region_test(capsys, 'unova')
def test_kalos(capsys):
region_test(capsys, 'kalos')
try:
main(['-r', 'wrong_region', '-dr'])
except SystemExit:
pass # It's supposed to crash.
err: str = capsys.readouterr()[1].strip()
assert err.endswith(
"(choose from 'kanto', 'johto', 'hoenn', 'sinnoh', 'unova', 'kalos')")
def test_all(capsys):
main([__file__, 'all'])
out, err = capsys.readouterr()
main(['-dr', '-ne'])
out = capsys.readouterr()[0]
for region_info in region_dict.values():
assert (region_info.first or '') in out # convert None --> ''
assert (region_info.last or '') in out # convert None --> ''
def test_question_mark(capsys):
main([__file__, '?'])
out, err = capsys.readouterr()
assert 'deprecated' in out
def test_region(capsys):
regFilter = RegionFilter(None, None)
noExtras = NonExtrasFilter(None, None)
# matrix test of first pokemon name and last pokemon name from all regions
for name, region_info in region_dict.items():
filtered = [p for p in Filter.POKEMON_LIST
if regFilter.matches(p, name)
and noExtras.matches(p, None)]
assert len(filtered) == region_info.size
assert random.choice(filtered).get_region() == name
assert filtered[0].get_id() == ('%03d' % (region_info.start))
assert filtered[-1].get_id() == ('%03d' % (region_info.end))
assert filtered[0].get_name() == region_info.first.lower()
assert filtered[-1].get_name() == region_info.last.lower()
if __name__ == '__main__':

View file

@ -14,22 +14,19 @@ from collections import Counter, namedtuple
import pokemonterminal
MAX_ID = 719
MAX_ID = 719 # Also total pokemon
SCRIPT_DIR = os.path.dirname(os.path.realpath(pokemonterminal.__file__))
region_info = namedtuple('region_info', 'start end first last')
region_info = namedtuple('region_info', 'start end first last size')
region_dict = {
'kanto': region_info(1, 151, 'Bulbasaur', 'Mew'),
'johto': region_info(152, 251, 'Chikorita', 'Celebi'),
'hoenn': region_info(252, 386, 'Treecko', 'Deoxys'),
'sinnoh': region_info(387, 493, 'Turtwig', 'Arceus'),
'unova': region_info(494, 649, 'Victini', 'Genesect'),
'kalos': region_info(650, 719, 'Chespin', 'Diancie')
'kanto': region_info(1, 151, 'Bulbasaur', 'Mew', 151),
'johto': region_info(152, 251, 'Chikorita', 'Celebi', 100),
'hoenn': region_info(252, 386, 'Treecko', 'Deoxys', 135),
'sinnoh': region_info(387, 493, 'Turtwig', 'Arceus', 107),
'unova': region_info(494, 649, 'Victini', 'Genesect', 156),
'kalos': region_info(650, 719, 'Chespin', 'Diancie', 70)
}
# From: https://en.wikipedia.org/wiki/Pok%C3%A9mon#Generation_1
_counts = {'kanto': 151, 'johto': 100, 'hoenn': 135, 'sinnoh': 107, 'unova': 156, 'kalos': 70, 'all': 719}
def expected_len(region_name):
"""Utility function for knowing the standard pokemon population."""
@ -41,14 +38,6 @@ def expected_len(region_name):
return region_info.end - region_info.start + 1
def test_region_dict():
"""Test if region_dict counts match wikipedia."""
assert _counts['all'] == MAX_ID == sum(_counts.values()) // 2
for region_name in region_dict:
assert _counts[region_name] == expected_len(region_name)
# print('{}: {}'.format(region_name, counts[region_name]))
def get_region(db, region_name):
"""Database unfortunately makes db.__get_region() private :-("""
func = {

View file

@ -41,13 +41,15 @@ def test_database_single_arg(arg):
elif arg == "get_random":
print(db.get_random())
else:
print("No such public method '" + arg + "' with zero parameters exists in the Database class.")
print("No such public method '" + arg + "' with zero parameters " +
"exists in the Database class.")
def test_database_double_arg(arg):
# Test the database where there are two command line parameters.
# The first parameter is the name of the method to test.
# The second parameter is the input parameter for the method that is being test.
# The second parameter is the input parameter for the method
# that is being tested.
arg1 = arg[1].lower()
arg2 = arg[2].lower()
db = Database()
@ -68,9 +70,9 @@ def test_database_double_arg(arg):
elif arg1 == "names_with_infix":
print_items(db.names_with_infix(arg2))
elif arg1 == "get_light":
print_items(db.get_light(threshold=int(arg2)/10, all_pkmn=True))
print_items(db.get_light(threshold=int(arg2) / 10, all_pkmn=True))
elif arg1 == "get_dark":
print_items(db.get_dark(threshold=int(arg2)/10, all_pkmn=True))
print_items(db.get_dark(threshold=int(arg2) / 10, all_pkmn=True))
else:
print("No such public method '" + arg + "' with two parameters"
" exists in the Database class.")