mirror of
				https://github.com/KevinMidboe/termForecast.git
				synced 2025-10-29 18:00:17 +00:00 
			
		
		
		
	
							
								
								
									
										101
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										101
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,99 @@ | |||||||
| .DS_Store | # Byte-compiled / optimized / DLL files | ||||||
| __pycache__ | __pycache__/ | ||||||
|  | *.py[cod] | ||||||
|  | *$py.class | ||||||
|  |  | ||||||
|  | # C extensions | ||||||
|  | *.so | ||||||
|  |  | ||||||
|  | # Distribution / packaging | ||||||
|  | .Python | ||||||
|  | build/ | ||||||
|  | develop-eggs/ | ||||||
|  | dist/ | ||||||
|  | downloads/ | ||||||
|  | eggs/ | ||||||
|  | .eggs/ | ||||||
|  | lib/ | ||||||
|  | lib64/ | ||||||
|  | parts/ | ||||||
|  | sdist/ | ||||||
|  | var/ | ||||||
|  | wheels/ | ||||||
|  | *.egg-info/ | ||||||
|  | .installed.cfg | ||||||
|  | *.egg | ||||||
|  |  | ||||||
|  | # PyInstaller | ||||||
|  | #  Usually these files are written by a python script from a template | ||||||
|  | #  before PyInstaller builds the exe, so as to inject date/other infos into it. | ||||||
|  | *.manifest | ||||||
|  | *.spec | ||||||
|  |  | ||||||
|  | # Installer logs | ||||||
|  | pip-log.txt | ||||||
|  | pip-delete-this-directory.txt | ||||||
|  |  | ||||||
|  | # Unit test / coverage reports | ||||||
|  | htmlcov/ | ||||||
|  | .tox/ | ||||||
|  | .coverage | ||||||
|  | .coverage.* | ||||||
|  | .cache | ||||||
|  | nosetests.xml | ||||||
|  | coverage.xml | ||||||
|  | *.cover | ||||||
|  | .hypothesis/ | ||||||
|  |  | ||||||
|  | # Translations | ||||||
|  | *.mo | ||||||
|  | *.pot | ||||||
|  |  | ||||||
|  | # Django stuff: | ||||||
|  | *.log | ||||||
|  | local_settings.py | ||||||
|  |  | ||||||
|  | # Flask stuff: | ||||||
|  | instance/ | ||||||
|  | .webassets-cache | ||||||
|  |  | ||||||
|  | # Scrapy stuff: | ||||||
|  | .scrapy | ||||||
|  |  | ||||||
|  | # Sphinx documentation | ||||||
|  | docs/_build/ | ||||||
|  |  | ||||||
|  | # PyBuilder | ||||||
|  | target/ | ||||||
|  |  | ||||||
|  | # Jupyter Notebook | ||||||
|  | .ipynb_checkpoints | ||||||
|  |  | ||||||
|  | # pyenv | ||||||
|  | .python-version | ||||||
|  |  | ||||||
|  | # celery beat schedule file | ||||||
|  | celerybeat-schedule | ||||||
|  |  | ||||||
|  | # SageMath parsed files | ||||||
|  | *.sage.py | ||||||
|  |  | ||||||
|  | # Environments | ||||||
|  | .env | ||||||
|  | .venv | ||||||
|  | env/ | ||||||
|  | venv/ | ||||||
|  | ENV/ | ||||||
|  |  | ||||||
|  | # Spyder project settings | ||||||
|  | .spyderproject | ||||||
|  | .spyproject | ||||||
|  |  | ||||||
|  | # Rope project settings | ||||||
|  | .ropeproject | ||||||
|  |  | ||||||
|  | # mkdocs documentation | ||||||
|  | /site | ||||||
|  |  | ||||||
|  | # mypy | ||||||
|  | .mypy_cache/ | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | MIT License | ||||||
|  |  | ||||||
|  | Copyright (c) 2017 Kevin | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
							
								
								
									
										2
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | include LICENSE | ||||||
|  | recursive-include docs *.gif | ||||||
							
								
								
									
										
											BIN
										
									
								
								docs/demo.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/demo.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 773 KiB | 
							
								
								
									
										4
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | fire==0.1.1 | ||||||
|  | geoip2==2.5.0 | ||||||
|  | fuzzywuzzy==0.15.1 | ||||||
|  | python-Levenshtein==0.12.0 | ||||||
							
								
								
									
										38
									
								
								setup.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								setup.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | from setuptools import setup | ||||||
|  |  | ||||||
|  | with open('requirements.txt') as f: | ||||||
|  |     requirements = f.read().splitlines() | ||||||
|  |  | ||||||
|  | setup( | ||||||
|  | 	name='term-forecast', | ||||||
|  | 	version='0.1.dev0', | ||||||
|  | 	author='Kevin Midboe', | ||||||
|  | 	author_email='support@kevinmidboe.com', | ||||||
|  | 	 | ||||||
|  | 	description='Terminal Forcast is a easily accessible terminal based weather forecaster', | ||||||
|  | 	url='https://github.com/KevinMidboe/termWeather/', | ||||||
|  | 	license='MIT', | ||||||
|  | 	 | ||||||
|  | 	packages=['term_forecast'], | ||||||
|  |  | ||||||
|  | 	classifiers = [ | ||||||
|  | 		"Environment :: Console", | ||||||
|  |         "Programming Language :: Python :: 3.6", | ||||||
|  | 		"Programming Language :: Python :: 3 :: Only", | ||||||
|  |         "License :: OSI Approved :: MIT License", | ||||||
|  |         "Development Status :: 4 - Beta", | ||||||
|  |  | ||||||
|  |         'Operating System :: OS Independent', | ||||||
|  |         'Operating System :: POSIX', | ||||||
|  |         'Operating System :: MacOS', | ||||||
|  |         'Operating System :: Unix', | ||||||
|  |     ], | ||||||
|  |  | ||||||
|  |     install_requires=requirements, | ||||||
|  |  | ||||||
|  |     entry_points={ | ||||||
|  |        'console_scripts': [ | ||||||
|  |            'forecast = term_forecast.term_weather:main', | ||||||
|  |        ], | ||||||
|  |     } | ||||||
|  | ) | ||||||
| Before Width: | Height: | Size: 51 MiB After Width: | Height: | Size: 51 MiB | 
							
								
								
									
										127
									
								
								termWeather/term_weather.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										127
									
								
								termWeather/term_weather.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | #!/usr/bin/env python3.6 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # @Author: KevinMidboe | ||||||
|  | # @Date:   2017-07-27 21:26:53 | ||||||
|  | # @Last Modified by:   KevinMidboe | ||||||
|  | # @Last Modified time: 2017-07-30 18:56:27 | ||||||
|  |  | ||||||
|  | # TODO LIST | ||||||
|  | # Get coordinates from IP ✔ | ||||||
|  | # Fetch coordinates from YR ✔ | ||||||
|  | # Convert coordinates to place name w/ google GeoCode api ✔ | ||||||
|  | # Parse return data | ||||||
|  | # Match weather description to icons ✔ | ||||||
|  | # Check internet connection in a strict way | ||||||
|  | # Add table for time periode | ||||||
|  | # Add cache for quicker location for same ip | ||||||
|  |  | ||||||
|  | import fire, json, geoip2.database, ssl, os | ||||||
|  | from yr.libyr import Yr | ||||||
|  | from requests import get | ||||||
|  | from pprint import pprint | ||||||
|  | from sys import stdout | ||||||
|  |  | ||||||
|  | from emojiParser import EmojiParser | ||||||
|  | from loadingAnimation import LoadingAnimation | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Location(object): | ||||||
|  | 	def __init__(self): | ||||||
|  |  | ||||||
|  | 		abspath = os.path.abspath(__file__) | ||||||
|  | 		dname = os.path.dirname(abspath) | ||||||
|  | 		os.chdir(dname) | ||||||
|  | 		self.reader = geoip2.database.Reader('conf/GeoLite2-City.mmdb') | ||||||
|  | 		self.getIP() | ||||||
|  |  | ||||||
|  | 	def getIP(self): | ||||||
|  | 		ip = get('https://api.ipify.org').text | ||||||
|  | 		self.ip = self.reader.city(ip) | ||||||
|  |  | ||||||
|  | 	def getCoordinates(self): | ||||||
|  | 		lat = self.ip.location.latitude | ||||||
|  | 		long = self.ip.location.longitude | ||||||
|  | 		return [lat, long] | ||||||
|  |  | ||||||
|  | 	def getAreaName(self): | ||||||
|  | 		lat, long = self.getCoordinates() | ||||||
|  | 		coordinates = ','.join([str(lat), str(long)]) | ||||||
|  | 		areaURL = 'https://maps.googleapis.com/maps/api/geocode/json?&latlng=' | ||||||
|  |  | ||||||
|  | 		areaAPIResponse = json.loads(get(areaURL + coordinates).text) | ||||||
|  | 		closestArea = areaAPIResponse['results'][0]['address_components'] | ||||||
|  |  | ||||||
|  | 		area = {} | ||||||
|  |  | ||||||
|  | 		for item in closestArea: | ||||||
|  | 			if 'route' in item['types']: | ||||||
|  | 				area['street'] = item['long_name'] | ||||||
|  |  | ||||||
|  | 			if 'locality' in item['types']: | ||||||
|  | 				area['town'] = item['long_name'] | ||||||
|  |  | ||||||
|  | 			if 'administrative_area_level_1' in item['types']: | ||||||
|  | 				area['municipality'] = item['long_name'] | ||||||
|  |  | ||||||
|  | 			if 'country' in item['types']: | ||||||
|  | 				area['country'] = item['long_name'] | ||||||
|  |  | ||||||
|  | 		return area | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class WeatherForecast(object): | ||||||
|  | 	def __init__(self, area=None): | ||||||
|  | 		# TODO search for area coordinates in a map | ||||||
|  | 		self.area = area | ||||||
|  |  | ||||||
|  | 		self.name = None | ||||||
|  | 		self.number = None | ||||||
|  | 		self.variable = None | ||||||
|  |  | ||||||
|  | 	def symbolVariables(self, symbol_obj): | ||||||
|  | 		self.name = symbol_obj['@name'] | ||||||
|  | 		self.number = symbol_obj['@number'] | ||||||
|  | 		self.variable = symbol_obj['@var'] | ||||||
|  |  | ||||||
|  | 	def parseYrTemperature(self, temperature_obj): | ||||||
|  | 		return temperature_obj['@value'] + ' ' + temperature_obj['@unit'] | ||||||
|  |  | ||||||
|  | 	def now(self): | ||||||
|  | 		location = Location() | ||||||
|  | 		self.area = location.getAreaName() | ||||||
|  |  | ||||||
|  | 		# Create seperate function for formatting | ||||||
|  | 		locationName = '/'.join([self.area['country'], self.area['municipality'], self.area['town'], self.area['street']]) | ||||||
|  |  | ||||||
|  | 		# Use the defined location name with yr API for location based weather information | ||||||
|  | 		weather = Yr(location_name=locationName) | ||||||
|  | 		now = json.loads(weather.now(as_json=True)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		temperature_output = self.parseYrTemperature(now['temperature']) | ||||||
|  |  | ||||||
|  | 		emojiParser = EmojiParser(now['symbol']['@name']) | ||||||
|  | 		weatherIcon_output = emojiParser.convertSematicsToEmoji() | ||||||
|  |  | ||||||
|  | 		return ('%s %s' % (temperature_output, weatherIcon_output)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TermWeather(object): | ||||||
|  | 	# Add now, forecast as args | ||||||
|  | 	def auto(self): | ||||||
|  | 		loadingAnimation = LoadingAnimation() | ||||||
|  | 		loadingAnimation.start() | ||||||
|  | 		weatherForecast = WeatherForecast() | ||||||
|  | 		forecast = weatherForecast.now() | ||||||
|  | 		loadingAnimation.stop() | ||||||
|  | 		stdout.write('\r%s          \n' % forecast) | ||||||
|  |  | ||||||
|  | 	def fetch(self, area=None): | ||||||
|  | 		weatherForecast = WeatherForcast(area) | ||||||
|  | 		weatherForecast.now() | ||||||
|  | 		 | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  | 	ssl._create_default_https_context = ssl._create_unverified_context | ||||||
|  | 	 | ||||||
|  | 	fire.Fire(TermWeather()) | ||||||
							
								
								
									
										0
									
								
								term_forecast/__init__.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										0
									
								
								term_forecast/__init__.py
									
									
									
									
									
										Executable file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								term_forecast/conf/GeoLite2-City.mmdb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								term_forecast/conf/GeoLite2-City.mmdb
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 51 MiB | 
							
								
								
									
										154
									
								
								term_forecast/emojiParser.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										154
									
								
								term_forecast/emojiParser.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,154 @@ | |||||||
|  | #!/usr/bin/env python3.6 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # @Author: KevinMidboe | ||||||
|  | # @Date:   2017-07-29 11:56:24 | ||||||
|  | # @Last Modified by:   KevinMidboe | ||||||
|  | # @Last Modified time: 2017-07-30 13:17:19 | ||||||
|  |  | ||||||
|  | from fuzzywuzzy import process | ||||||
|  |  | ||||||
|  | # Find the first word, if it is a noun or a adjective. ✔️ | ||||||
|  | # Remove the adjective and split if there is a AND ✔️ | ||||||
|  | # Then match the first noun to list and add that emoji ✔️ | ||||||
|  | # and then match the second to list and add that emoji ✔️ | ||||||
|  | # REGEX this bitch up  | ||||||
|  |  | ||||||
|  | symbol_table = { | ||||||
|  | 	'clear sky': '☀️', | ||||||
|  | 	'fair': '🌤', | ||||||
|  | 	'partly cloudy': '⛅️', | ||||||
|  | 	'cloudy': ' ☁️ ', | ||||||
|  | 	'thunder': '⚡️', | ||||||
|  | 	 | ||||||
|  | 	'rain showers': '🌦', | ||||||
|  | 	'rain': '🌧', | ||||||
|  | 	'sleet showers': '🌦 💦', | ||||||
|  | 	'sleet': '🌨 💦', | ||||||
|  | 	'snow showers': '⛅ ❄️', | ||||||
|  | 	'snow': '🌨', | ||||||
|  |  | ||||||
|  | 	'rain': '🌧', | ||||||
|  | 	'sleet': '🌧', | ||||||
|  | 	'snow': '🌨', | ||||||
|  |  | ||||||
|  | 	'showers': '🌤' | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | severity = { | ||||||
|  | 		'rain': ['', ' ☂️', ' ☔️'], | ||||||
|  | 		'sleet': [' 💦 ', ' 💧 ', ' 💧 💦 '], | ||||||
|  | 		'snow': [' ❄️ ', ' ❄️ ❄️ ', ' ❄️ ❄️ ❄️ '] | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | class EmojiParser(object): | ||||||
|  | 	def __init__(self, condition_text): | ||||||
|  | 		self.condition_expression = condition_text.lower() | ||||||
|  | 		self.severity = None | ||||||
|  | 		self.nouns = [] | ||||||
|  |  | ||||||
|  | 		self.weather_nouns = ['cleary sky', 'fair', 'cloudy', 'rain', 'rain showers', 'sleet', | ||||||
|  | 			'sleet showers', 'snow showers', 'thunder', 'snow'] | ||||||
|  | 		self.weather_adjectives = {'light': 0, 'normal': 1, 'heavy': 2} | ||||||
|  |  | ||||||
|  | 	def __str__(self): | ||||||
|  | 	 	return str([self.condition_expression, self.severity, self.nouns]) | ||||||
|  |  | ||||||
|  | 	# Splits and lowers the condition text for easier parsing | ||||||
|  | 	def splitCondition(self, condition): | ||||||
|  | 		return condition.split() | ||||||
|  |  | ||||||
|  | 	# Takes a input or uses condition_expression to find adjective in sentence | ||||||
|  | 	def findAdjective(self, sentence=None): | ||||||
|  | 		if sentence is None: | ||||||
|  | 			sentence = self.condition_expression | ||||||
|  |  | ||||||
|  | 		# Splits and iterates over each word in sentence | ||||||
|  | 		expression = self.splitCondition(sentence) | ||||||
|  | 		for word in expression: | ||||||
|  | 			if word in self.weather_adjectives: | ||||||
|  | 				# Return the word if matched with weather_adjectives | ||||||
|  | 				return word | ||||||
|  |  | ||||||
|  | 		return None | ||||||
|  |  | ||||||
|  | 	# Removes the first adjective in the a given sentence | ||||||
|  | 	def removeAdjective(self): | ||||||
|  | 		adjective = self.findAdjective() | ||||||
|  | 		if adjective:	# Adjective is not None | ||||||
|  | 			expression = self.splitCondition(self.condition_expression) | ||||||
|  | 			expression.remove(adjective) | ||||||
|  | 			return ' '.join(expression) | ||||||
|  | 		else: | ||||||
|  | 			return self.condition_expression | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	def severityValue(self): | ||||||
|  | 		adjective = self.findAdjective() | ||||||
|  |  | ||||||
|  | 		if adjective: | ||||||
|  | 			self.severity = self.weather_adjectives[adjective] | ||||||
|  | 		else: | ||||||
|  | 			self.severity = self.weather_adjectives['normal'] | ||||||
|  |  | ||||||
|  | 	def findWeatherTokens(self): | ||||||
|  | 		# If present removes the leading adjective | ||||||
|  | 		sentence = self.removeAdjective() | ||||||
|  | 		 | ||||||
|  | 		# If multiple tokens/weather_nouns split all between the 'and' | ||||||
|  | 		if 'and' in sentence: | ||||||
|  | 			self.nouns = sentence.split(' and ') | ||||||
|  | 		else: | ||||||
|  | 			self.nouns = [sentence] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	# Use the symbol_table to convert the forecast name to emoji | ||||||
|  | 	def emojify(self, noun): | ||||||
|  | 		return symbol_table[noun] | ||||||
|  |  | ||||||
|  | 	# Does as emojify above, but iterates over a list if multiple elements | ||||||
|  | 	def emojifyList(self, noun_list): | ||||||
|  | 		returnList = [] | ||||||
|  | 		 | ||||||
|  | 		# TODO use more like a map function? | ||||||
|  | 		for noun in noun_list: | ||||||
|  | 			returnList.append(self.emojify(noun)) | ||||||
|  |  | ||||||
|  | 		return '  '.join(returnList) | ||||||
|  |  | ||||||
|  | 	def findPrimaryForecast(self): | ||||||
|  | 		# Copies the contents not the refrence to the list | ||||||
|  | 		noun_list = list(self.nouns) | ||||||
|  | 		forecast = noun_list.pop(0) | ||||||
|  |  | ||||||
|  | 		# Translates to emoji once here instead of twice below | ||||||
|  | 		forecast_emoji = self.emojify(forecast) | ||||||
|  |  | ||||||
|  | 		if forecast in severity: | ||||||
|  | 			return ('%s %s' % (forecast_emoji, severity[forecast])) | ||||||
|  | 		else: | ||||||
|  | 			return forecast_emoji | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	# Trying to analyze the semantics of the condition text | ||||||
|  | 	def emojifyWeatherForecast(self): | ||||||
|  | 		# Finds the tokens/nouns of weather for the given input text and severity value | ||||||
|  | 		self.findWeatherTokens() | ||||||
|  | 		self.severityValue() | ||||||
|  |  | ||||||
|  | 		primary_forecast = self.findPrimaryForecast() | ||||||
|  | 		secondary_forecast = self.emojifyList(self.nouns[1:]) | ||||||
|  | 		 | ||||||
|  | 		return ('%s %s' % (primary_forecast, secondary_forecast)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	def convertSematicsToEmoji(self): | ||||||
|  | 		return self.emojifyWeatherForecast() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def main(): | ||||||
|  | 	emojiParser = EmojiParser('Cloudy') | ||||||
|  | 	print(emojiParser.convertSematicsToEmoji()) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  | 	main() | ||||||
							
								
								
									
										40
									
								
								term_forecast/loadingAnimation.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										40
									
								
								term_forecast/loadingAnimation.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | #!/usr/bin/env python3.6 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # @Author: KevinMidboe | ||||||
|  | # @Date:   2017-07-30 13:53:38 | ||||||
|  | # @Last Modified by:   KevinMidboe | ||||||
|  | # @Last Modified time: 2017-07-30 13:53:46 | ||||||
|  |  | ||||||
|  | import itertools | ||||||
|  | from threading import Thread | ||||||
|  | from time import sleep | ||||||
|  | from sys import stdout | ||||||
|  |  | ||||||
|  | class LoadingAnimation(object): | ||||||
|  | 	def __init__(self): | ||||||
|  | 		self.done = False | ||||||
|  |  | ||||||
|  | 	def start(self): | ||||||
|  | 		t = Thread(target=self.animate) | ||||||
|  | 		t.start() | ||||||
|  |  | ||||||
|  | 	def animate(self): | ||||||
|  | 	    for c in itertools.cycle(['|', '/', '-', '\\']): | ||||||
|  | 	        if self.done: | ||||||
|  | 	            break | ||||||
|  | 	        stdout.write('\rFetching ' + c) | ||||||
|  | 	        stdout.flush() | ||||||
|  | 	        sleep(0.1) | ||||||
|  |  | ||||||
|  | 	def stop(self): | ||||||
|  | 		self.done = True | ||||||
|  |  | ||||||
|  | def main(): | ||||||
|  | 	loadingAnimation = LoadingAnimation() | ||||||
|  | 	loadingAnimation.start() | ||||||
|  | 	sleep(2) | ||||||
|  | 	loadingAnimation.stop() | ||||||
|  | 	stdout.write('\rTemp          \n') | ||||||
|  | 	 | ||||||
|  | if __name__ == '__main__': | ||||||
|  | 	main() | ||||||
		Reference in New Issue
	
	Block a user