From 99aa41e270df02ec36671e7b5da78e0cab0871d4 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 8 Jan 2025 17:59:06 -0500 Subject: [PATCH 1/9] Add slideshow function to playimage --- PyDOS.py | 2 +- README.md | 4 +- cpython/matrix.py | 1 + cpython/playimage.py | 225 ++++++++++++++++++++++++++++--------------- 4 files changed, 151 insertions(+), 81 deletions(-) diff --git a/PyDOS.py b/PyDOS.py index 5433e10..4b18ae1 100644 --- a/PyDOS.py +++ b/PyDOS.py @@ -73,7 +73,7 @@ def PyDOS(): global envVars if "envVars" not in globals().keys(): envVars = {} - _VER = "1.43" + _VER = "1.44" prmpVals = ['>','(',')','&','|','\x1b','\b','<','=',' ',_VER,'\n','$',''] print("Starting Py-DOS...") diff --git a/README.md b/README.md index 4857417..cdd1b4a 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,9 @@ down - the number of tiles connected down the matrix display If the parameters are omitted or not properly formatted, the program will prompt for each of the values. -**Playimage.py [filename]** - (Circuitpython only, requires the adafruit_imageload library installed in the /lib folder) program to display .bmp, .jpg, .gif or .png image files. If the program is loaded from PyDOS it attempts to determine the appropriate display configuration from the PyDOS environment, otherwise several display options are supported and selected depending on the existence of BOARD.Display or locally installed display libraries. +**Playimage.py [filename[,filename2,filename3,etc,[seconds_to_display]]]** - (Circuitpython only, requires the adafruit_imageload library installed in the /lib folder) program to display .bmp, .jpg, .gif (incl animaged) or .png image files. If multiple comma +seperated files are entered a continous slide show is displayed with each image being +displayed for `seconds_to_display` seconds. If the program is loaded from PyDOS it attempts to determine the appropriate display configuration from the PyDOS environment, otherwise several display options are supported and selected depending on the existence of BOARD.Display or locally installed display libraries. **reboot.py** - performs a soft reboot (Micropython requires a Ctrl-D to complete) diff --git a/cpython/matrix.py b/cpython/matrix.py index 4d22d19..7fbbf74 100644 --- a/cpython/matrix.py +++ b/cpython/matrix.py @@ -13,6 +13,7 @@ [base_width,base_height,bit_depth,chain_across,tile_down] = [int(p) for p in args] except: passedIn = "" + args = "" if len(args) != 5 or args[0] not in ['32','64'] or args[1] not in ['32','64'] or bit_depth < 1 or bit_depth > 8: try: diff --git a/cpython/playimage.py b/cpython/playimage.py index 8288f5c..e4c6f41 100644 --- a/cpython/playimage.py +++ b/cpython/playimage.py @@ -3,10 +3,10 @@ import gifio except: pass +import adafruit_ticks import adafruit_imageload import bitmaptools import displayio -import time from os import getenv try: from pydos_ui import Pydos_ui @@ -79,88 +79,78 @@ def playimage(passedIn=""): if passedIn != "": - fname = passedIn + flist = passedIn else: - fname = "" + flist = "" - if fname == "": - fname = input("Enter filename: ") + if flist == "": + flist = input("Enter filename: ") try: while Pydos_ui.virt_touched(): pass except: pass - if fname==passedIn: + if flist==passedIn: print('Press "q" to quit') else: input('Press "Enter" to continue, press "q" to quit') - if fname[-4:].upper() in [".BMP",".PNG",".JPG",".RLE"]: - - bitmap, palette = adafruit_imageload.load( \ - fname, bitmap=displayio.Bitmap, palette=displayio.Palette) - - scalefactor = display.width / bitmap.width - if display.height/bitmap.height < scalefactor: - scalefactor = display.height/bitmap.height + files = flist.split(',') + try: + dispseconds = int(files[-1]) + files = files[:-1] + except: + dispseconds = 15 - if scalefactor < 1: - print(f'scalefactor: {scalefactor}') - bitframe = displayio.Bitmap(display.width,display.height,2**bitmap.bits_per_value) - bitmaptools.rotozoom(bitframe,bitmap,scale=scalefactor) - facecc = displayio.TileGrid(bitframe,pixel_shader=palette) - # pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) - pwidth = bitframe.width - pheight = bitframe.height - else: - facecc = displayio.TileGrid(bitmap,pixel_shader=palette) - # pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) - pwidth = bitmap.width - pheight = bitmap.height - - print("bitmap (w,h): ",bitmap.width,bitmap.height) - print("scaled bitmap (w,h): ",pwidth,pheight) - print("facecc (w,h): ",facecc.width,facecc.height) - - if pwidth < display.width: - facecc.x = (display.width-pwidth)//2 - if pheight < display.height: - facecc.y = (display.height-pheight)//2 - splash = displayio.Group() - splash.append(facecc) - display.root_group = splash + singleimage = False + if len(files) == 1 and files[0][0] != '*': + singleimage = True - input('Press Enter to close') + fileindx = 0 + wildindx = 0 + while True: + fname = files[fileindx] - elif fname[-4:].upper() in [".GIF"]: + if fname[0] == '*': + wildlist = [f for f in os.listdir() if f[fname.find('.')-len(fname):] == fname[fname.find('.')-len(fname):]] + fname = wildlist[wildindx] + wildindx = (wildindx +1) % len(wildlist) + if wildindx == 0: + fileindx = (fileindx + 1) % len(files) + else: + fileindx = (fileindx + 1) % len(files) - odgcc = gifio.OnDiskGif(fname) - with odgcc as odg: - if getenv('PYDOS_DISPLAYIO_COLORSPACE',"").upper() == 'BGR565_SWAPPED': - colorspace = displayio.Colorspace.BGR565_SWAPPED - else: - colorspace = displayio.Colorspace.RGB565_SWAPPED + if fname[-4:].upper() in [".BMP",".PNG",".JPG",".RLE"]: - scalefactor = display.width / odg.width - if display.height/odg.height < scalefactor: - scalefactor = display.height/odg.height + bitmap, palette = adafruit_imageload.load( \ + fname, bitmap=displayio.Bitmap, palette=displayio.Palette) + + scalefactor = display.width / bitmap.width + if display.height/bitmap.height < scalefactor: + scalefactor = display.height/bitmap.height if scalefactor < 1: - print(f'scalefactor: {scalefactor}') - bitframe = displayio.Bitmap(display.width,display.height,2**odg.bitmap.bits_per_value) - bitmaptools.rotozoom(bitframe,odg.bitmap,scale=scalefactor) - facecc = displayio.TileGrid(bitframe, \ - pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) + if singleimage: + print(f'scalefactor: {scalefactor}') + bitframe = displayio.Bitmap(display.width,display.height,2**bitmap.bits_per_value) + bitmaptools.rotozoom(bitframe,bitmap,scale=scalefactor) + facecc = displayio.TileGrid(bitframe,pixel_shader=palette) + # pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) pwidth = bitframe.width pheight = bitframe.height else: - facecc = displayio.TileGrid(odg.bitmap, \ - pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) - pwidth = odg.bitmap.width - pheight = odg.bitmap.height + facecc = displayio.TileGrid(bitmap,pixel_shader=palette) + # pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) + pwidth = bitmap.width + pheight = bitmap.height + if singleimage: + print("bitmap (w,h): ",bitmap.width,bitmap.height) + print("scaled bitmap (w,h): ",pwidth,pheight) + print("facecc (w,h): ",facecc.width,facecc.height) + if pwidth < display.width: facecc.x = (display.width-pwidth)//2 if pheight < display.height: @@ -169,27 +159,104 @@ def playimage(passedIn=""): splash.append(facecc) display.root_group = splash - start = 0 - next_delay = -1 - cmnd = "" - # Display repeatedly. - while cmnd.upper() != "Q": - - if Pydos_ui.serial_bytes_available(): - cmnd = Pydos_ui.read_keyboard(1) - print(cmnd, end="", sep="") - if cmnd in "qQ": - break - while time.monotonic() > start and next_delay > time.monotonic()-start: - pass - next_delay = odg.next_frame() - start = time.monotonic() - if next_delay > 0: - if scalefactor < 1: - bitmaptools.rotozoom(bitframe,odg.bitmap,scale=scalefactor) + if singleimage: + input('Press Enter to close') + break + else: + cmnd = "" + stop = adafruit_ticks.ticks_add(adafruit_ticks.ticks_ms(),int(dispseconds*1000)) + while adafruit_ticks.ticks_less(adafruit_ticks.ticks_ms(),stop): + if Pydos_ui.serial_bytes_available(): + cmnd = Pydos_ui.read_keyboard(1) + print(cmnd, end="", sep="") + if cmnd.upper() == "Q": + break + if cmnd.upper() == "Q": + break - else: - print('Unknown filetype') + try: + splash.pop() + bitmap.deinit() + bitmap = None + facecc.bitmap.deinit() + facecc = None + if scalefactor < 1: + bitframe.deinit() + bitframe = None + except: + pass + + elif fname[-4:].upper() in [".GIF"]: + + odgcc = gifio.OnDiskGif(fname) + with odgcc as odg: + + if getenv('PYDOS_DISPLAYIO_COLORSPACE',"").upper() == 'BGR565_SWAPPED': + colorspace = displayio.Colorspace.BGR565_SWAPPED + else: + colorspace = displayio.Colorspace.RGB565_SWAPPED + + scalefactor = display.width / odg.width + if display.height/odg.height < scalefactor: + scalefactor = display.height/odg.height + + if scalefactor < 1: + if singleimage: + print(f'scalefactor: {scalefactor}') + bitframe = displayio.Bitmap(display.width,display.height,2**odg.bitmap.bits_per_value) + bitmaptools.rotozoom(bitframe,odg.bitmap,scale=scalefactor) + facecc = displayio.TileGrid(bitframe, \ + pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) + pwidth = bitframe.width + pheight = bitframe.height + else: + facecc = displayio.TileGrid(odg.bitmap, \ + pixel_shader=displayio.ColorConverter(input_colorspace=colorspace)) + pwidth = odg.bitmap.width + pheight = odg.bitmap.height + + if pwidth < display.width: + facecc.x = (display.width-pwidth)//2 + if pheight < display.height: + facecc.y = (display.height-pheight)//2 + splash = displayio.Group() + splash.append(facecc) + display.root_group = splash + + cmnd = "" + # Display repeatedly. + stop = adafruit_ticks.ticks_add(adafruit_ticks.ticks_ms(),int(dispseconds*1000)) + while adafruit_ticks.ticks_less(adafruit_ticks.ticks_ms(),stop) or dispseconds==-1: + + if Pydos_ui.serial_bytes_available(): + cmnd = Pydos_ui.read_keyboard(1) + print(cmnd, end="", sep="") + if cmnd.upper() == "Q": + break + start = adafruit_ticks.ticks_ms() + next_delay = odg.next_frame() + start = adafruit_ticks.ticks_add(start,int(next_delay*1000)) + if next_delay > 0: + if scalefactor < 1: + bitmaptools.rotozoom(bitframe,odg.bitmap,scale=scalefactor) + while adafruit_ticks.ticks_less(adafruit_ticks.ticks_ms(),start): + pass + if cmnd.upper() == "Q": + break + + try: + splash.pop() + odgcc = None + facecc.bitmap.deinit() + facecc = None + if scalefactor < 1: + bitframe.deinit() + bitframe = None + except: + pass + + else: + print('Unknown filetype') try: splash.pop() From 5718dcb14564a57f567d16556252e6f207232704 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Thu, 9 Jan 2025 01:16:55 -0500 Subject: [PATCH 2/9] animation bug when displaying just one file --- README.md | 3 ++- cpython/playimage.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cdd1b4a..0e1feb4 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,8 @@ If the parameters are omitted or not properly formatted, the program will prompt **Playimage.py [filename[,filename2,filename3,etc,[seconds_to_display]]]** - (Circuitpython only, requires the adafruit_imageload library installed in the /lib folder) program to display .bmp, .jpg, .gif (incl animaged) or .png image files. If multiple comma seperated files are entered a continous slide show is displayed with each image being -displayed for `seconds_to_display` seconds. If the program is loaded from PyDOS it attempts to determine the appropriate display configuration from the PyDOS environment, otherwise several display options are supported and selected depending on the existence of BOARD.Display or locally installed display libraries. +displayed for `seconds_to_display` seconds. Wildcard's in the format of *.xxx can be used +as an input filename. If the program is loaded from PyDOS it attempts to determine the appropriate display configuration from the PyDOS environment, otherwise several display options are supported and selected depending on the existence of BOARD.Display or locally installed display libraries. **reboot.py** - performs a soft reboot (Micropython requires a Ctrl-D to complete) diff --git a/cpython/playimage.py b/cpython/playimage.py index e4c6f41..db615fd 100644 --- a/cpython/playimage.py +++ b/cpython/playimage.py @@ -226,7 +226,7 @@ def playimage(passedIn=""): cmnd = "" # Display repeatedly. stop = adafruit_ticks.ticks_add(adafruit_ticks.ticks_ms(),int(dispseconds*1000)) - while adafruit_ticks.ticks_less(adafruit_ticks.ticks_ms(),stop) or dispseconds==-1: + while adafruit_ticks.ticks_less(adafruit_ticks.ticks_ms(),stop) or singleimage: if Pydos_ui.serial_bytes_available(): cmnd = Pydos_ui.read_keyboard(1) From 3e33434909cb19ac3f06bcbe43eda2ba23b6a81b Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 21 Jan 2025 01:45:31 -0500 Subject: [PATCH 3/9] count *nix/windows newline chars properly for PyBasic file I/O --- PyBasic/basicparser.py | 16 ++++++++++++++-- PyBasic/program.py | 36 ++++++++++++++++++++++++++++++++---- PyDOS.py | 2 +- README.md | 4 ++-- lib/pydos_bcfg.py | 2 +- lib/pydos_ui.py | 1 - 6 files changed, 50 insertions(+), 11 deletions(-) diff --git a/PyBasic/basicparser.py b/PyBasic/basicparser.py index 10b8f21..0c1a546 100644 --- a/PyBasic/basicparser.py +++ b/PyBasic/basicparser.py @@ -680,11 +680,23 @@ def __openstmt(self): raise RuntimeError('File '+filename+' could not be opened in line ' + str(self.__line_number)) if accessMode == "r+": + if hasattr(self.__file_handles,'newlines'): + try: + self.__file_handles[filenum].readline() + except: + pass + newlines = self.__file_handles.newlines + else: + newlines = None self.__file_handles[filenum].seek(0) filelen = 0 for lines in self.__file_handles[filenum]: - filelen += (len(lines)+(0 if uname()[0].upper() == 'LINUX' or \ - implementation.name.upper() in ['MICROPYTHON','CIRCUITPYTHON'] else 1)) + filelen += len(lines) + if newlines != None: + filelen += len(newlines)-1 + else: + filelen += (0 if uname()[0].upper() == 'LINUX' or \ + implementation.name.upper() in ['MICROPYTHON','CIRCUITPYTHON'] else 1) self.__file_handles[filenum].seek(filelen) diff --git a/PyBasic/program.py b/PyBasic/program.py index 0b5806a..a7e22e3 100644 --- a/PyBasic/program.py +++ b/PyBasic/program.py @@ -146,13 +146,26 @@ def load(self, file, tmpfile): try: infile = open(file, 'r') + if hasattr(infile,'newlines'): + try: + infile.readline() + except: + pass + newlines = infile.newlines + infile.seek(0) + else: + newlines = None fIndex = 0 fOffset = 0 pgmLoad = False if file.split(".")[-1].upper() == "PGM": pgmLoad = True for fileLine in infile: - fOffset += (len(fileLine) + (0 if self.__imp == 'X' else 1)) + fOffset += len(fileLine) + if newlines != None: + fOffset += len(newlines)-1 + elif self.__imp != 'X': + fOffset += 1 if len(fileLine) >= 9 and fileLine[0:9] == "-999,-999": break @@ -173,8 +186,11 @@ def load(self, file, tmpfile): if fileLine.strip().upper()[fileLine.strip().find(' '):].strip()[:4] == "DATA": self.__data.addData(line_number,fIndex) #self.add_stmt(Lexer().tokenize((fileLine.replace("\n","")).replace("\r","")),fIndex+fOffset,tmpfile) - fIndex += (len(fileLine) + (0 if self.__imp == 'X' else 1)) - + fIndex += len(fileLine) + if newlines != None: + fIndex += len(newlines)-1 + elif self.__imp != 'X': + fIndex += 1 except OSError: print("Could not read file") @@ -204,10 +220,22 @@ def add_stmt(self, tokenlist, fIndex, tmpfile): if tokenlist[1].lexeme == "DATA": self.__data.addData(line_number,fIndex) else: + if hasattr(tmpfile,'newlines'): + try: + tmpfile.readline() + except: + pass + newlines = tmpfile.newlines + else: + newlines = None tmpfile.seek(0) filelen = 0 for lines in tmpfile: - filelen += (len(lines)+(0 if self.__imp == 'X' else 1)) + filelen += len(lines) + if newlines != None: + filelen += len(newlines) - 1 + elif self.__imp != 'X': + filelen += 1 self.__program[line_number] = -(filelen+1) if tokenlist[1].lexeme == "DATA": diff --git a/PyDOS.py b/PyDOS.py index 4b18ae1..5d21c34 100644 --- a/PyDOS.py +++ b/PyDOS.py @@ -73,7 +73,7 @@ def PyDOS(): global envVars if "envVars" not in globals().keys(): envVars = {} - _VER = "1.44" + _VER = "1.45" prmpVals = ['>','(',')','&','|','\x1b','\b','<','=',' ',_VER,'\n','$',''] print("Starting Py-DOS...") diff --git a/README.md b/README.md index 0e1feb4..f14caba 100644 --- a/README.md +++ b/README.md @@ -200,10 +200,10 @@ down - the number of tiles connected down the matrix display If the parameters are omitted or not properly formatted, the program will prompt for each of the values. -**Playimage.py [filename[,filename2,filename3,etc,[seconds_to_display]]]** - (Circuitpython only, requires the adafruit_imageload library installed in the /lib folder) program to display .bmp, .jpg, .gif (incl animaged) or .png image files. If multiple comma +**Playimage.py [filename[,filename2,filename3,etc[],seconds_to_display]]]** - (Circuitpython only, requires the adafruit_imageload library installed in the /lib folder) program to display .bmp, .jpg, .gif (incl animated) or .png image files. If multiple comma seperated files are entered a continous slide show is displayed with each image being displayed for `seconds_to_display` seconds. Wildcard's in the format of *.xxx can be used -as an input filename. If the program is loaded from PyDOS it attempts to determine the appropriate display configuration from the PyDOS environment, otherwise several display options are supported and selected depending on the existence of BOARD.Display or locally installed display libraries. +as an input filename. If the program is loaded from PyDOS it attempts to determine the appropriate display configuration from the PyDOS environment, otherwise several display options are supported and selected depending on the existence of BOARD.DISPLAY or locally installed display libraries. **reboot.py** - performs a soft reboot (Micropython requires a Ctrl-D to complete) diff --git a/lib/pydos_bcfg.py b/lib/pydos_bcfg.py index 92ce74e..bf659c4 100644 --- a/lib/pydos_bcfg.py +++ b/lib/pydos_bcfg.py @@ -47,7 +47,7 @@ from machine import Pin try: led = "LED" - test = machine.Pin(led,Pin.OUT) + test = Pin(led,Pin.OUT) except: led = "D13" diff --git a/lib/pydos_ui.py b/lib/pydos_ui.py index 64e8211..75a9ff7 100644 --- a/lib/pydos_ui.py +++ b/lib/pydos_ui.py @@ -32,7 +32,6 @@ def serial_bytes_available(self,timeout=1): retval = 0 else: retval = 1 - retval = 1 if retval else 0 return retval From e9d6b6c35f6b4c359e0fed0e3cc3d6ea182d1b06 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 21 Jan 2025 03:25:02 -0500 Subject: [PATCH 4/9] count *nix/windows newline chars properly for PyBasic file I/O - bugfix --- PyBasic/basicparser.py | 4 ++-- PyDOS.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PyBasic/basicparser.py b/PyBasic/basicparser.py index 0c1a546..44d1bbf 100644 --- a/PyBasic/basicparser.py +++ b/PyBasic/basicparser.py @@ -680,12 +680,12 @@ def __openstmt(self): raise RuntimeError('File '+filename+' could not be opened in line ' + str(self.__line_number)) if accessMode == "r+": - if hasattr(self.__file_handles,'newlines'): + if hasattr(self.__file_handles[filenum],'newlines'): try: self.__file_handles[filenum].readline() except: pass - newlines = self.__file_handles.newlines + newlines = self.__file_handles[filenum].newlines else: newlines = None self.__file_handles[filenum].seek(0) diff --git a/PyDOS.py b/PyDOS.py index 5d21c34..882f61e 100644 --- a/PyDOS.py +++ b/PyDOS.py @@ -73,7 +73,7 @@ def PyDOS(): global envVars if "envVars" not in globals().keys(): envVars = {} - _VER = "1.45" + _VER = "1.46" prmpVals = ['>','(',')','&','|','\x1b','\b','<','=',' ',_VER,'\n','$',''] print("Starting Py-DOS...") From be1875f140d38f7e3fd7b6b98de8f15ca82d5f38 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 25 Jan 2025 01:32:39 -0500 Subject: [PATCH 5/9] Updated web scraping demo - wifi_finance.py --- PyDOS.py | 2 +- getdate.py | 1 + lib/pydos_wifi.py | 6 +- wifi_finance.py | 211 +++++++++++++++++++++++++++++----------------- 4 files changed, 139 insertions(+), 81 deletions(-) diff --git a/PyDOS.py b/PyDOS.py index 882f61e..6a2536f 100644 --- a/PyDOS.py +++ b/PyDOS.py @@ -73,7 +73,7 @@ def PyDOS(): global envVars if "envVars" not in globals().keys(): envVars = {} - _VER = "1.46" + _VER = "1.47" prmpVals = ['>','(',')','&','|','\x1b','\b','<','=',' ',_VER,'\n','$',''] print("Starting Py-DOS...") diff --git a/getdate.py b/getdate.py index 5f1b7a2..efc9433 100644 --- a/getdate.py +++ b/getdate.py @@ -29,6 +29,7 @@ def getdate(passedIn=""): try: print(" Using http worldtimeapi.org...",end="") + Pydos_wifi.timeout = 1000 response = Pydos_wifi.get("http://worldtimeapi.org/api/ip",None,True) time_data = Pydos_wifi.json() diff --git a/lib/pydos_wifi.py b/lib/pydos_wifi.py index 53a6f88..78db3c2 100644 --- a/lib/pydos_wifi.py +++ b/lib/pydos_wifi.py @@ -1,4 +1,4 @@ -PyDOS_wifi_VER = "1.40" +PyDOS_wifi_VER = "1.47" import os import time @@ -214,6 +214,10 @@ def get(self,text_url,headers=None,getJSON=False): return self.response + def post(self,text_url,data): + self.response = self._requests.post(text_url,data=data) + return self.response + def json(self): retVal = None if implementation.name.upper() == 'CIRCUITPYTHON': diff --git a/wifi_finance.py b/wifi_finance.py index e9b46f2..9b55fad 100644 --- a/wifi_finance.py +++ b/wifi_finance.py @@ -4,6 +4,7 @@ from sys import implementation from os import uname from pydos_wifi import Pydos_wifi +import time def wifi_finance(symbol): try: @@ -12,11 +13,12 @@ def wifi_finance(symbol): _scrWidth = 80 if not symbol: - symbol = "IXIC" + symbol = ".IXIC:INDEXNASDAQ" else: symbol = symbol.upper() - prt_sym = symbol - srch_sym = symbol + prt_sym = symbol[:symbol.find(':')] + srch_sym = symbol[:symbol.find(':')] + search_attempts = 5000 # Get wifi details and more from a .env file if Pydos_wifi.getenv('CIRCUITPY_WIFI_SSID') is None: @@ -24,8 +26,16 @@ def wifi_finance(symbol): print("Connecting to %s" % Pydos_wifi.getenv('CIRCUITPY_WIFI_SSID')) - if not Pydos_wifi.connect(Pydos_wifi.getenv('CIRCUITPY_WIFI_SSID'), Pydos_wifi.getenv('CIRCUITPY_WIFI_PASSWORD')): - raise Exception("Unable to connect to WiFi!") + res = False + for i in range(2): + try: + res = Pydos_wifi.connect(Pydos_wifi.getenv('CIRCUITPY_WIFI_SSID'), Pydos_wifi.getenv('CIRCUITPY_WIFI_PASSWORD')) + break + except: + print('Retrying....') + if not res: + if not Pydos_wifi.connect(Pydos_wifi.getenv('CIRCUITPY_WIFI_SSID'), Pydos_wifi.getenv('CIRCUITPY_WIFI_PASSWORD')): + raise Exception("Unable to connect to WiFi!") print("My IP address is", Pydos_wifi.ipaddress) @@ -36,16 +46,24 @@ def wifi_finance(symbol): #search_string = 'data-symbol="^IXIC" data-field="regularMarketChangePercent"' #TEXT_URL = "https://www.moneycontrol.com/us-markets" #search_string = 'Nasdaq' + #TEXT_URL = f"https://www.google.com/search?q={symbol.replace('&','%26')}+stock+price" + #search_string = symbol + #Id_Symbol = True + #price_ident = '%)' + #window_depth=4 - TEXT_URL = f"https://www.google.com/search?q={symbol.replace('&','%26')}+stock+price" + TEXT_URL = f"https://www.google.com/finance/quote/%7Bsymbol.replace('&','%26')}" search_string = symbol + Id_Symbol = False + price_ident = 'data-last-price' + window_depth = 5 #headers = {"user-agent": "RetiredWizard@"+implementation.name.lower()+uname()[2]} print("Fetching text from %s" % TEXT_URL) response = Pydos_wifi.get(TEXT_URL) response_window = [] - for _ in range(4): + for _ in range(window_depth): response_window.append(Pydos_wifi.next(256)) if len(response_window[-1]) != 256: break @@ -61,58 +79,65 @@ def wifi_finance(symbol): print(pline) print("-" * _scrWidth) - print("Identifying symbol",end="") - name_loc = -1 - iKount = 0 - while name_loc == -1 and iKount<800: - iKount +=1 - if iKount % 10 == 0: - print(".",end="") - - found_window = str(b''.join(response_window)) - - name_loc = found_window.find(' Inc. is') - if name_loc == -1: - name_loc = found_window.find(' Inc., commonly') - if name_loc == -1: - name_loc = found_window.find(' is a stock market ') - if name_loc != -1: - if found_window[:name_loc].rfind('or simply the ') != -1: - srch_sym = found_window[found_window[:name_loc].rfind('or simply the ')+14:name_loc] - elif found_window[:name_loc].rfind('>') != -1: - srch_sym = found_window[found_window[:name_loc].rfind('>')+1:name_loc] - srch_sym = srch_sym.replace(',','') - prt_sym = srch_sym.replace('&','&') - prt_sym = prt_sym.replace('amp;','') - if srch_sym[0:4].upper() == 'THE ': - srch_sym = srch_sym[4:] - print(f'* {search_string} * {srch_sym} * {prt_sym}',end="") - - if iKount<800: - for i in range(3): - response_window[i] = response_window[i+1] - try: - response_window[3] = Pydos_wifi.next(256) - if len(response_window[3]) != 256: - print('X',end="") - iKount=800 - except: - iKount=800 - print() - - print("Locating price data",end="") - response.close() - response = Pydos_wifi.get(TEXT_URL) - response_window = [] iKount = 0 - for _ in range(4): - response_window.append(Pydos_wifi.next(256)) - if len(response_window[-1]) != 256: - iKount = 799 - break + if Id_Symbol: + print("Identifying symbol",end="") + name_loc = -1 + while name_loc == -1 and iKount') != -1: + srch_sym = found_window[found_window[:name_loc].rfind('>')+1:name_loc] + srch_sym = srch_sym.replace(',','') + prt_sym = srch_sym.replace('&','&') + prt_sym = prt_sym.replace('amp;','') + if srch_sym[0:4].upper() == 'THE ': + srch_sym = srch_sym[4:] + print(f'* {search_string} * {srch_sym} * {prt_sym}',end="") + + if iKount')+1 - pctend = pct + found_window[pct:].find('<') - #print("Debug: %s\n" % found_window[nasdaq:pctend]) - pricest = found_window[:pctst-2].rfind('>')+1 - priceend = pricest + found_window[pricest:].find('<') - if nasdaq != -1: - print(f'{prt_sym}: {found_window[pricest:priceend]} {found_window[pctst:pctend].replace("<","")}\n') +# Final scrape logic +# Google Search +# pct = found_window.find(price_ident) +# pctst = found_window[:pct].rfind('>')+1 +# pctend = pct + found_window[pct:].find('<') +# print("Debug: %s\n" % found_window[nasdaq:pctend]) +# pricest = found_window[:pctst-2].rfind('>')+1 +# priceend = pricest + found_window[pricest:].find('<') + +# Google finance + pricest = found_window.find(price_ident)+len(price_ident)+2 + priceend = pricest + found_window[pricest:].find('"') + pctst = -1 + + #print(f'Debug: start loc: {pricest} end loc: {priceend}\n{found_window[nasdaq:]}') + + print(f'{prt_sym}: {found_window[pricest:priceend]}',end="") + if pctst != -1: + print(f' {found_window[pctst:pctend].replace("<","")}\n') + else: + print('\n') else: print(f"{prt_sym} symbol not found\n") @@ -176,4 +214,19 @@ def wifi_finance(symbol): wifi_finance(passedIn) else: print('Enter "wifi_finance.wifi_finance("symbol")" in the REPL or PEXEC command to run.') - print(' A null symbol ("") will default to the Nasdaq Index') \ No newline at end of file + print(' A null symbol ("") will default to the Nasdaq Index') + +print('\nDemonstration Web "scraping" program. The web sites being used in the') +print('demonstration will often change and break the algorithm used to locate a') +print('stock price. When that happens this program needs to be updated to work') +print('with the new web site or find a new one.\n') +print('The current web site being used is: https://www.google.com/finance\n') +print('With this site the symbol passed to wifi_finance must be formatted as') +print('follows: symbol:exchange. So for Apple Inc, you would enter AAPL:NASDAQ') +print('or for AT&T enter T:NYSE. To retrieve the price of an index format the') +print('symbol as follows: .indexsymbol:INDEXsymbol. For example Nasdaq:') +print('.IXIC:INDEXNASDAQ, Dow Jones: .DJI:INDEXDJX, S&P 500: .INX:INDEXSP. The') +print('index symbols can be retrieved by going to the www.google.com/finance page') +print("and selecting the index you're inerested in. The formatted symbol will be") +print('updated at the end of the URL (not the symbol displayed in the search box.') + From dfd29b44d44d7cc4707b2c21b3795529a9a6abd1 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sat, 25 Jan 2025 15:22:33 -0500 Subject: [PATCH 6/9] move web scraping demo information display before output screen --- wifi_finance.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wifi_finance.py b/wifi_finance.py index 9b55fad..1e49cdd 100644 --- a/wifi_finance.py +++ b/wifi_finance.py @@ -210,12 +210,6 @@ def wifi_finance(symbol): del response_window del found_window -if __name__ == "PyDOS": - wifi_finance(passedIn) -else: - print('Enter "wifi_finance.wifi_finance("symbol")" in the REPL or PEXEC command to run.') - print(' A null symbol ("") will default to the Nasdaq Index') - print('\nDemonstration Web "scraping" program. The web sites being used in the') print('demonstration will often change and break the algorithm used to locate a') print('stock price. When that happens this program needs to be updated to work') @@ -230,3 +224,9 @@ def wifi_finance(symbol): print("and selecting the index you're inerested in. The formatted symbol will be") print('updated at the end of the URL (not the symbol displayed in the search box.') +if __name__ == "PyDOS": + wifi_finance(passedIn) +else: + print('Enter "wifi_finance.wifi_finance("symbol")" in the REPL or PEXEC command to run.') + print(' A null symbol ("") will default to the Nasdaq Index') + From 729139277c76d184b87eeab9772b77ee9e8c835b Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 26 Jan 2025 22:08:11 -0500 Subject: [PATCH 7/9] getting time via http can be a problem esp on Lilygo T-Deck, default to NTP first --- PyDOS.py | 2 +- getdate.py | 55 +++++++++++++++++++++++++++--------------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/PyDOS.py b/PyDOS.py index 6a2536f..0993f33 100644 --- a/PyDOS.py +++ b/PyDOS.py @@ -73,7 +73,7 @@ def PyDOS(): global envVars if "envVars" not in globals().keys(): envVars = {} - _VER = "1.47" + _VER = "1.48" prmpVals = ['>','(',')','&','|','\x1b','\b','<','=',' ',_VER,'\n','$',''] print("Starting Py-DOS...") diff --git a/getdate.py b/getdate.py index efc9433..7e8a41a 100644 --- a/getdate.py +++ b/getdate.py @@ -28,33 +28,7 @@ def getdate(passedIn=""): print("Attempting to set Date/Time",end="") try: - print(" Using http worldtimeapi.org...",end="") - Pydos_wifi.timeout = 1000 - response = Pydos_wifi.get("http://worldtimeapi.org/api/ip",None,True) - time_data = Pydos_wifi.json() - - if passedIn == "": - tz_hour_offset = int(time_data['utc_offset'][0:3]) - tz_min_offset = int(time_data['utc_offset'][4:6]) - if (tz_hour_offset < 0): - tz_min_offset *= -1 - else: - tz_hour_offset = int(passedIn) - tz_min_offset = 0 - - unixtime = int(time_data['unixtime'] + (tz_hour_offset * 60 * 60)) + (tz_min_offset * 60) - ltime = time.localtime(unixtime) - - if sys.implementation.name.upper() == "MICROPYTHON": - machine.RTC().datetime(tuple([ltime[0]-(time.localtime(0)[0]-1970)]+[ltime[i] for i in [1,2,6,3,4,5,7]])) - else: - rtc.RTC().datetime = ltime - - print("\nTime and Date successfully set",end="") - envVars['errorlevel'] = '0' - - except: - print( " FAILED. Trying NTP...",end="") + print( "Trying NTP...",end="") if passedIn == "": tz_hour_offset = -4 @@ -117,6 +91,33 @@ def getdate(passedIn=""): print("\nTime and Date successfully set",end="") envVars['errorlevel'] = '0' + except: + + print("FAILED. Trying http worldtimeapi.org...",end="") + Pydos_wifi.timeout = 1000 + response = Pydos_wifi.get("http://worldtimeapi.org/api/ip",None,True) + time_data = Pydos_wifi.json() + + if passedIn == "": + tz_hour_offset = int(time_data['utc_offset'][0:3]) + tz_min_offset = int(time_data['utc_offset'][4:6]) + if (tz_hour_offset < 0): + tz_min_offset *= -1 + else: + tz_hour_offset = int(passedIn) + tz_min_offset = 0 + + unixtime = int(time_data['unixtime'] + (tz_hour_offset * 60 * 60)) + (tz_min_offset * 60) + ltime = time.localtime(unixtime) + + if sys.implementation.name.upper() == "MICROPYTHON": + machine.RTC().datetime(tuple([ltime[0]-(time.localtime(0)[0]-1970)]+[ltime[i] for i in [1,2,6,3,4,5,7]])) + else: + rtc.RTC().datetime = ltime + + print("\nTime and Date successfully set",end="") + envVars['errorlevel'] = '0' + print() Pydos_wifi.close() From 57a875c72dd299c8e529d2bc3dba04a53582a472 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 14 Feb 2025 00:54:12 -0500 Subject: [PATCH 8/9] add support for Circuitpython display auto-configure --- cpython/playimage.py | 23 ++++++++++++++--------- fileview.py | 2 +- lib/pydos_ui.py | 14 +++++++++++--- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/cpython/playimage.py b/cpython/playimage.py index db615fd..f86f110 100644 --- a/cpython/playimage.py +++ b/cpython/playimage.py @@ -8,6 +8,7 @@ import bitmaptools import displayio from os import getenv +from supervisor import runtime try: from pydos_ui import Pydos_ui from pydos_ui import input @@ -19,28 +20,32 @@ type(envVars) except: envVars = {} - + +display = None if '_display' in envVars.keys(): display = envVars['_display'] elif Pydos_display: display = Pydos_ui.display -elif 'DISPLAY' in dir(board): +elif bool(getattr(board,'DISPLAY',False)): display = board.DISPLAY +elif bool(getattr(runtime,'display',False)): + display = runtime.display else: try: - import matrix - display = matrix.envVars['_display'] + import framebufferio + import dotclockframebuffer except: try: - import framebufferio - import dotclockframebuffer + import adafruit_ili9341 except: try: - import adafruit_ili9341 - except: - import framebufferio + # import framebufferio import picodvi + except: + import matrix + display = matrix.envVars['_display'] + if display == None: displayio.release_displays() if 'TFT_PINS' in dir(board): diff --git a/fileview.py b/fileview.py index d51472c..f900443 100644 --- a/fileview.py +++ b/fileview.py @@ -67,7 +67,7 @@ def absolutePath(argPath,currDir): scrWidth = 80 if "_scrollable" in envVars.keys(): - scrollable = (envVars["_scrollable"] == True) + scrollable = (envVars["_scrollable"] == True) or (envVars["_scrollable"] == "True") else: try: scrollable = Pydos_ui.scrollable diff --git a/lib/pydos_ui.py b/lib/pydos_ui.py index 75a9ff7..0b88d0d 100644 --- a/lib/pydos_ui.py +++ b/lib/pydos_ui.py @@ -6,6 +6,7 @@ import select if implementation.name.upper() == "CIRCUITPYTHON": import board + from supervisor import runtime try: from displayio import CIRCUITPYTHON_TERMINAL as TERM from terminalio import FONT @@ -15,8 +16,15 @@ class PyDOS_UI: def __init__(self): - if implementation.name.upper() == "CIRCUITPYTHON" and 'DISPLAY' in dir(board): + if implementation.name.upper() == "CIRCUITPYTHON": + self.scrollable = False + if bool(getattr(board,'DISPLAY',False)): + self.display = board.DISPLAY + elif bool(getattr(runtime,'display',False)): + self.display = runtime.display + else: + self.scrollable = True else: self.scrollable = True @@ -45,8 +53,8 @@ def get_screensize(self,disp=None): dhigh = disp.height dwide = disp.width else: - dhigh = board.DISPLAY.height - dwide = board.DISPLAY.width + dhigh = self.display.height + dwide = self.display.width height = round(dhigh/(FONT.bitmap.height*TERM.scale))-1 width = round(dwide/((FONT.bitmap.width/95)*TERM.scale))-2 From 7c8c2bf6a127f67a3e74079ca157fe7ccb633c97 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Sun, 2 Mar 2025 13:13:59 -0500 Subject: [PATCH 9/9] misc fixes for terminalio display support & running external progams w/o PyDOS --- PyDOS.py | 2 +- bounce.py | 3 ++- cpython/matrix.py | 5 +++++ cpython/playimage.py | 16 ++++++++++++---- fileview.py | 39 +++++++++++++++++++++++++++++++++------ lib/pydos_ui.py | 2 +- 6 files changed, 54 insertions(+), 13 deletions(-) diff --git a/PyDOS.py b/PyDOS.py index 0993f33..8cdedc9 100644 --- a/PyDOS.py +++ b/PyDOS.py @@ -73,7 +73,7 @@ def PyDOS(): global envVars if "envVars" not in globals().keys(): envVars = {} - _VER = "1.48" + _VER = "1.49" prmpVals = ['>','(',')','&','|','\x1b','\b','<','=',' ',_VER,'\n','$',''] print("Starting Py-DOS...") diff --git a/bounce.py b/bounce.py index 2f06d3d..374d409 100644 --- a/bounce.py +++ b/bounce.py @@ -8,7 +8,8 @@ Pydos_ui = None x = 10; y = 10; d = 1; e = 1 -m='⬤' +#m='⬤' +m='O' try: width = int(envVars.get('_scrWidth',80)) height = int(envVars.get('_scrHeight',24)) diff --git a/cpython/matrix.py b/cpython/matrix.py index 7fbbf74..a7926c0 100644 --- a/cpython/matrix.py +++ b/cpython/matrix.py @@ -6,9 +6,14 @@ import framebufferio import rgbmatrix import supervisor +from os import getenv try: type(passedIn) +except: + passedIn = getenv('PYDOS_MATRIX_CONFIG') + +try: args = passedIn.split(',') [base_width,base_height,bit_depth,chain_across,tile_down] = [int(p) for p in args] except: diff --git a/cpython/playimage.py b/cpython/playimage.py index f86f110..19e6b99 100644 --- a/cpython/playimage.py +++ b/cpython/playimage.py @@ -11,6 +11,14 @@ from supervisor import runtime try: from pydos_ui import Pydos_ui + readkbd = Pydos_ui.read_keyboard + sba = Pydos_ui.serial_bytes_available +except: + Pydos_ui = [] + from sys import stdin + readkbd = stdin.read + sba = lambda : runtime.serial_bytes_available +try: from pydos_ui import input Pydos_display = ('display' in dir(Pydos_ui)) except: @@ -171,8 +179,8 @@ def playimage(passedIn=""): cmnd = "" stop = adafruit_ticks.ticks_add(adafruit_ticks.ticks_ms(),int(dispseconds*1000)) while adafruit_ticks.ticks_less(adafruit_ticks.ticks_ms(),stop): - if Pydos_ui.serial_bytes_available(): - cmnd = Pydos_ui.read_keyboard(1) + if sba(): + cmnd = readkbd(1) print(cmnd, end="", sep="") if cmnd.upper() == "Q": break @@ -233,8 +241,8 @@ def playimage(passedIn=""): stop = adafruit_ticks.ticks_add(adafruit_ticks.ticks_ms(),int(dispseconds*1000)) while adafruit_ticks.ticks_less(adafruit_ticks.ticks_ms(),stop) or singleimage: - if Pydos_ui.serial_bytes_available(): - cmnd = Pydos_ui.read_keyboard(1) + if sba(): + cmnd = readkbd(1) print(cmnd, end="", sep="") if cmnd.upper() == "Q": break diff --git a/fileview.py b/fileview.py index f900443..9c23e39 100644 --- a/fileview.py +++ b/fileview.py @@ -1,12 +1,21 @@ import os -from pydos_ui import Pydos_ui +import supervisor +from sys import implementation +try: + from pydos_ui import Pydos_ui +except: + Pydos_ui = None + from sys import stdin try: from pydos_ui import input except: pass #import uselect -def viewFile(args): +def viewFile(args,scrsiz=()): + # scrsiz can be used if running program from REPL to specify the screen dimensions + # first import launches with defaults but subsequent launches can use: + # fileview.viewFile("filename.txt",(height,width)) def chkPath(tstPath): validPath = True @@ -60,11 +69,22 @@ def absolutePath(argPath,currDir): scrLines = int(envVars["_scrHeight"]) scrWidth = int(envVars["_scrWidth"]) + elif scrsiz: + (scrLines,scrWidth) = scrsiz elif 'get_screensize' in dir(Pydos_ui): (scrLines,scrWidth) = Pydos_ui.get_screensize() else: - scrLines = 24 - scrWidth = 80 + if 'height' in dir(supervisor.runtime.display): + scrLines = supervisor.runtime.display.height + scrWidth = supervisor.runtime.display.width + else: + scrLines = 24 + scrWidth = 80 + + try: + type(envVars) + except: + envVars={} if "_scrollable" in envVars.keys(): scrollable = (envVars["_scrollable"] == True) or (envVars["_scrollable"] == "True") @@ -72,7 +92,11 @@ def absolutePath(argPath,currDir): try: scrollable = Pydos_ui.scrollable except: - scrollable = False + if implementation.name.upper() == 'CIRCUITPYTHON': + # Once CircuitPython 9.2.4 is stable this can be change to True + scrollable = False + else: + scrollable = False savDir = os.getcwd() args = absolutePath(args,savDir) @@ -108,7 +132,10 @@ def absolutePath(argPath,currDir): strtCol = 0 while cmnd.upper() != "Q": #cmnd = kbdInterrupt() - cmnd = Pydos_ui.read_keyboard(1) + if Pydos_ui: + cmnd = Pydos_ui.read_keyboard(1) + else: + cmnd = stdin.read(1) if ord(cmnd) == 27 and seqCnt == 0: seqCnt = 1 diff --git a/lib/pydos_ui.py b/lib/pydos_ui.py index 0b88d0d..638a1f0 100644 --- a/lib/pydos_ui.py +++ b/lib/pydos_ui.py @@ -24,7 +24,7 @@ def __init__(self): elif bool(getattr(runtime,'display',False)): self.display = runtime.display else: - self.scrollable = True + self.scrollable = True else: self.scrollable = True