Compare commits

...

18 Commits

Author SHA1 Message Date
markjfine eaeed80311
Added debug to MusicBrainz search error 2023-07-22 09:39:09 -04:00
markjfine dab697f082
Updated several package versions 2023-07-20 13:56:15 -04:00
Mark J. Fine 49cf532fdc renamed code 511 per spec, added official mime types, modified LOT regex to acount for expiry, added regex for Navteq/HERE stream/packet info, added place holder for Navteq/HERE images. 2023-07-10 20:26:19 -04:00
Mark J. Fine 76b7348943 fixed typo in python-dateutil 2023-02-24 17:02:32 -05:00
Mark J. Fine a27f2969d4 Added requirements.txt instructions 2023-01-12 07:57:20 -05:00
Mark J. Fine 47d2c85abd Added pygobject 2023-01-11 13:19:31 -05:00
markjfine aa7eca98ee
Merge pull request #33 from andylhxu/patch-1
Add requirements.txt
2023-01-11 11:21:14 -05:00
Andy Lianghua Xu a3a6ac2a41
Update requirements.txt 2023-01-11 01:27:49 -05:00
Andy Lianghua Xu 5f2e736d4b
Add requirements.txt
To make it easier to pip install the required packages, rather than relying on exceptions to figure out the missing ones.
2023-01-11 01:25:41 -05:00
Mark J. Fine fd5ba2796b Fill some of the dead space in wide mode with station messages/alerts 2022-12-22 16:08:42 -05:00
Mark J. Fine 31b4d262ed Fixed typo in set_sensitive() 2022-12-22 14:25:13 -05:00
Mark J. Fine 3f77d35962 Added warning before restarting 2022-12-22 08:33:43 -05:00
Mark J. Fine 69057f967a Added way to change application display from narrow to wide layout 2022-12-21 22:28:29 -05:00
Mark J. Fine 1679eda1c5 Changed version number to 2.2.2. 2022-12-19 05:27:39 -05:00
Mark J. Fine d83c934b95 Corrected the way gain was being set for SDRPlay. 2022-12-18 13:50:20 -05:00
Mark J. Fine ec61b85afc Removed quotes from SDRPlay Antenna switch and added Auto option 2022-12-17 16:35:45 -05:00
Mark J. Fine b945e3611a Increased RTL_TCP address size to allow for port 2022-12-14 21:12:07 -05:00
Mark J. Fine 6069f6c503 Use Resmapling.LANCZOS only on PIL > v8, include RTL -H and -d switches only when using RTL. 2022-12-11 20:27:38 -05:00
6 changed files with 5222 additions and 102 deletions

View File

@ -21,7 +21,7 @@ The following programs are required to run NRSC5-DUI
It is also assumed you have a fully operational Gtk3 environment installed from [Homebrew](https://brew.sh/), if running on macOS.
# Setup
1. Install the latest version of Python 3.9, PyGObject, Pillow, and other python dependencies.
1. Install the latest version of Python 3.9, PyGObject, Pillow, and other python dependencies. Once Python is installed, you may install the dependencies by giving the command `pip install -r <path_to requirements.txt>`
2. Compile and install nrsc5. If using an SDRPlay, you must compile and install the version provided by [fventuri](https://github.com/fventuri/nrsc5).
3. Install nrsc5-dui files in a directory where you have write permissions.

View File

@ -24,7 +24,14 @@ import os, pty, select, sys, shutil, re, json, datetime, numpy, glob, time, plat
from subprocess import Popen, PIPE
from threading import Timer, Thread
from dateutil import tz
from PIL import Image, ImageFont, ImageDraw
from PIL import Image, ImageFont, ImageDraw, __version__
print('Using Pillow v'+__version__)
if (int(__version__[0]) < 9):
imgLANCZOS = Image.LANCZOS
else:
imgLANCZOS = Image.Resampling.LANCZOS
import gi
gi.require_version("Gtk", "3.0")
@ -51,7 +58,8 @@ cfgDir = os.path.join(runtimeDir, "cfg") # config file directory
class NRSC5_DUI(object):
def __init__(self):
global runtimeDir, resDir
global runtimeDir, resDir, imgLANCZOS
self.windowsOS = False # save our determination as a var in case we change how we determine.
self.getControls() # get controls and windows
@ -76,7 +84,7 @@ class NRSC5_DUI(object):
self.debugLog("OS Determination: Windows = {}".format(self.windowsOS))
self.app_name = "NRSC5-DUI"
self.version = "2.2.0"
self.version = "2.2.2"
self.web_addr = "https://github.com/markjfine/nrsc5-dui"
self.copyright = "Copyright © 2017-2019 Cody Nybo & Clayton Smith, 2019 zefie, 2021-2022 Mark J. Fine"
musicbrainzngs.set_useragent(self.app_name,self.version,self.web_addr)
@ -162,7 +170,7 @@ class NRSC5_DUI(object):
263 : "Service Maintenance",
264 : "HD Radio System Services",
265 : "Audio-Related Objects",
511 : "Test_Str_E"
511 : "Reserved for Special Tests"
}
self.ProgramType = {
@ -200,6 +208,25 @@ class NRSC5_DUI(object):
76 : "Special Reading Services"
}
self.MIMETypes = {
0x1E653E9C : "JPEG",
0x2D42AC3E : "NavTeq",
0x4F328CA0 : "PNG",
0x4DC66C5A : "HDC",
0x4EB03469 : "TTN TPEG 2",
0x52103469 : "TTN TPEG 3",
0x82F03DFC : "HERE TPEG",
0xB39EBEB2 : "TTN TPEG 1",
0xB7F03DFC : "HERE Image",
0xB81FFAA8 : "Unknown Test",
0xBB492AAC : "Text",
0xBE4B7536 : "Primary Image",
0xD9C72536 : "Station Logo",
0xEECB55B6 : "HD TMC",
0xEF042E96 : "TTN STM Weather",
0xFF8422D7 : "TTN STM Traffic"
}
self.pointer_cursor = Gdk.Cursor(Gdk.CursorType.LEFT_PTR)
self.hand_cursor = Gdk.Cursor(Gdk.CursorType.HAND2)
@ -256,7 +283,7 @@ class NRSC5_DUI(object):
re.compile("^[0-9\:]{8,8} Title: (.*)$"), # 4 match title
re.compile("^[0-9\:]{8,8} Artist: (.*)$"), # 5 match artist
re.compile("^[0-9\:]{8,8} Album: (.*)$"), # 6 match album
re.compile("^[0-9\:]{8,8} LOT file: port=([\d]+) lot=([\d]+) name=(.*\.(?:jpg|jpeg|png|txt)) size=([\d]+) mime=([\w]+)$"), # 7 match file (album art, maps, weather info)
re.compile("^[0-9\:]{8,8} LOT file: port=([\d]+) lot=([\d]+) name=(.*\.(?:jpg|jpeg|png|txt)) size=([\d]+) mime=([\w]+) .*$"), # 7 match file (album art, maps, weather info)
re.compile("^[0-9\:]{8,8} MER: (-?[\d]+\.[\d]+) dB \(lower\), (-?[\d]+\.[\d]+) dB \(upper\)$"), # 8 match MER
re.compile("^[0-9\:]{8,8} BER: (0\.[\d]+), avg: (0\.[\d]+), min: (0\.[\d]+), max: (0\.[\d]+)$"), # 9 match BER
re.compile("^[0-9\:]{8,8} Best gain: (.*) dB,.*$"), # 10 match gain
@ -271,7 +298,9 @@ class NRSC5_DUI(object):
re.compile("^[0-9\:]{8,8} Synchronized$"), # 19 synchronized
re.compile("^[0-9\:]{8,8} Lost synchronization$"), # 20 lost synch
re.compile("^[0-9\:]{8,8} Lost device$"), # 21 lost device
re.compile("^[0-9\:]{8,8} Open device failed.$") # 22 No device
re.compile("^[0-9\:]{8,8} Open device failed.$"), # 22 No device
re.compile("^[0-9\:]{8,8} Stream data: port=([\d]+).* mime=([\w]+) size=([\d]+)$"), # 23 Navteq/HERE stream info
re.compile("^[0-9\:]{8,8} Packet data: port=([\d]+).* mime=([\w]+) size=([\d]+)$") # 24 Navteq/HERE packet info
]
self.loadSettings()
@ -302,6 +331,30 @@ class NRSC5_DUI(object):
self.lblExtend.set_sensitive(dlCoversSet)
self.cbExtend.set_sensitive(dlCoversSet)
def restart_program(self):
python = sys.executable
os.execl(python, python, *sys.argv)
def confirm_dialog(self, title, message):
dialog = Gtk.MessageDialog(parent=self.mainWindow, flags=0, message_type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.YES_NO, text=title)
dialog.format_secondary_text(message)
dialog.set_default_response(Gtk.ResponseType.YES)
response = dialog.run()
dialog.destroy()
return (response == Gtk.ResponseType.YES)
def on_cbxAspect_changed(self, btn):
screenAspect = self.cbxAspect.get_active_text()
if (screenAspect == "narrow") or (screenAspect == "wide"):
mainFile = os.path.join(resDir, "mainForm.glade")
gladeFile = os.path.join(resDir, "mainForm-"+screenAspect+".glade")
if (os.path.isfile(gladeFile)):
shutil.copy(gladeFile,mainFile)
title = "Aspect Changed"
message = "You have change the display layout to "+screenAspect+". This change will not happen until the application is restarted. Would you like to restart it now?"
if (self.confirm_dialog(title,message)):
self.restart_program()
def on_cbxSDRRadio_changed(self, btn):
useSDRPlay = (self.cbxSDRRadio.get_active_text() == "SDRPlay")
self.lblSdrPlaySer.set_visible(useSDRPlay)
@ -337,7 +390,7 @@ class NRSC5_DUI(object):
return result
def on_cover_resize(self, container):
global mapDir
global mapDir, imgLANCZOS
if (self.did_resize()):
self.showArtwork(self.coverImage)
@ -345,13 +398,13 @@ class NRSC5_DUI(object):
if (self.mapData["mapMode"] == 0):
map_file = os.path.join(mapDir, "TrafficMap.png")
if os.path.isfile(map_file):
map_img = Image.open(map_file).resize((img_size, img_size), Image.Resampling.LANCZOS)
map_img = Image.open(map_file).resize((img_size, img_size), imgLANCZOS)
self.imgMap.set_from_pixbuf(self.img_to_pixbuf(map_img))
else:
self.imgMap.set_from_icon_name("MISSING_IMAGE", Gtk.IconSize.DIALOG)
elif (self.mapData["mapMode"] == 1):
if os.path.isfile(self.mapData["weatherNow"]):
map_img = Image.open(self.mapData["weatherNow"]).resize((img_size, img_size), Image.Resampling.LANCZOS)
map_img = Image.open(self.mapData["weatherNow"]).resize((img_size, img_size), imgLANCZOS)
self.imgMap.set_from_pixbuf(self.img_to_pixbuf(map_img))
else:
self.imgMap.set_from_icon_name("MISSING_IMAGE", Gtk.IconSize.DIALOG)
@ -467,6 +520,13 @@ class NRSC5_DUI(object):
result = musicbrainzngs.search_recordings(strict=setStrict, artist=searchArtist, recording=newTitle, type=setType, status=setStatus)
except:
print("MusicBrainz recording search error")
print("iteration =",i,".")
print("imgSaved =",imgSaved,".")
print("strict =",setStrict,".")
print("artist =",searchArtist,".")
print("recording =",newTitle,".")
print("type =",setType,".")
print("status =",setStatus,".")
if (result is not None) and ('recording-list' in result) and (len(result['recording-list']) != 0):
# loop through the list until you get a match
@ -596,13 +656,13 @@ class NRSC5_DUI(object):
self.nrsc5Args.append(aasDir)
# set IP address if rtl_tcp is used
if (self.cbDevIP.get_active()):
if (not(useSDRPlay)) and (self.cbDevIP.get_active()):
self.nrsc5Args.append("-H")
self.nrsc5Args.append(self.txtDevIP.get_text())
# set gain if auto gain is not selected
if (not self.cbAutoGain.get_active()):
self.streamInfo["Gain"] = self.spinGain.get_value()
self.streamInfo["Gain"] = round(self.spinGain.get_value(),2)
self.nrsc5Args.append("-g")
self.nrsc5Args.append(str(self.streamInfo["Gain"]))
@ -612,7 +672,7 @@ class NRSC5_DUI(object):
self.nrsc5Args.append(str(int(self.spinPPM.get_value())))
# set rtl device number if not zero
if (self.spinRTL.get_value() != 0):
if (not(useSDRPlay)) and (self.spinRTL.get_value() != 0):
self.nrsc5Args.append("-d")
self.nrsc5Args.append(str(int(self.spinRTL.get_value())))
@ -630,8 +690,9 @@ class NRSC5_DUI(object):
# set SDRPlay antenna if not blank
#if (self.cbSDRPlay.get_active()) and (self.cbxSDRPlayAnt.get_active_text() != ""):
if (useSDRPlay) and (self.cbxSDRPlayAnt.get_active_text() != ""):
self.nrsc5Args.append("-A")
self.nrsc5Args.append("\"Antenna "+self.cbxSDRPlayAnt.get_active_text()+"\"")
if self.cbxSDRPlayAnt.get_active_text() != "Auto":
self.nrsc5Args.append("-A")
self.nrsc5Args.append("Antenna "+self.cbxSDRPlayAnt.get_active_text())
# set frequency and stream
self.nrsc5Args.append(str(self.spinFreq.get_value()))
@ -645,6 +706,7 @@ class NRSC5_DUI(object):
# disable the controls
self.spinFreq.set_sensitive(False)
self.cbxAspect.set_sensitive(False)
self.cbxSDRRadio.set_sensitive(False)
self.spinGain.set_sensitive(False)
self.spinPPM.set_sensitive(False)
@ -709,6 +771,7 @@ class NRSC5_DUI(object):
if (not self.cbAutoGain.get_active()):
self.spinGain.set_sensitive(True)
self.spinFreq.set_sensitive(True)
self.cbxAspect.set_sensitive(True)
self.cbxSDRRadio.set_sensitive(True)
self.spinPPM.set_sensitive(True)
self.spinRTL.set_sensitive(True)
@ -786,7 +849,7 @@ class NRSC5_DUI(object):
authors = [
"Cody Nybo <cmnybo@gmail.com>",
"Clayton Smith <argilo@gmail.com>",
"nefie <zefie@zefie.net>",
"zefie <zefie@zefie.net>",
"Mark J. Fine <mark.fine@fineware-swl.com>"
]
@ -916,7 +979,7 @@ class NRSC5_DUI(object):
self.btnDelete.set_sensitive(iter is not None)
def on_radMap_toggled(self, btn):
global mapDir
global mapDir, imgLANCZOS
if (btn.get_active()):
img_size = min(self.alignmentMap.get_allocated_height(), self.alignmentMap.get_allocated_width()) - 12
if (img_size < 200):
@ -925,7 +988,7 @@ class NRSC5_DUI(object):
self.mapData["mapMode"] = 0
mapFile = os.path.join(mapDir, "TrafficMap.png")
if (os.path.isfile(mapFile)): # check if map exists
mapImg = Image.open(mapFile).resize((img_size, img_size), Image.Resampling.LANCZOS) # scale map to fit window
mapImg = Image.open(mapFile).resize((img_size, img_size), imgLANCZOS) # scale map to fit window
self.imgMap.set_from_pixbuf(imgToPixbuf(mapImg)) # convert image to pixbuf and display
else:
self.imgMap.set_from_icon_name("MISSING_IMAGE", Gtk.IconSize.DIALOG) # display missing image if file is not found
@ -933,7 +996,7 @@ class NRSC5_DUI(object):
elif (btn == self.radMapWeather):
self.mapData["mapMode"] = 1
if (os.path.isfile(self.mapData["weatherNow"])):
mapImg = Image.open(self.mapData["weatherNow"]).resize((img_size, img_size), Image.Resampling.LANCZOS) # scale map to fit window
mapImg = Image.open(self.mapData["weatherNow"]).resize((img_size, img_size), imgLANCZOS) # scale map to fit window
self.imgMap.set_from_pixbuf(imgToPixbuf(mapImg)) # convert image to pixbuf and display
else:
self.imgMap.set_from_icon_name("MISSING_IMAGE", Gtk.IconSize.DIALOG) # display missing image if file is not found
@ -1067,8 +1130,14 @@ class NRSC5_DUI(object):
self.lblSlogan.set_tooltip_text(self.streamInfo["Slogan"])
self.lblMessage.set_label(self.streamInfo["Message"])
self.lblMessage.set_tooltip_text(self.streamInfo["Message"])
if (self.txtMessage2):
self.txtMessage2.set_label(self.streamInfo["Message"])
self.txtMessage2.set_tooltip_text(self.streamInfo["Message"])
self.lblAlert.set_label(self.streamInfo["Alert"])
self.lblAlert.set_tooltip_text(self.streamInfo["Alert"])
if (self.txtAlert2):
self.txtAlert2.set_label(self.streamInfo["Alert"])
self.txtAlert2.set_tooltip_text(self.streamInfo["Alert"])
self.set_button_name(self.btnAudioPrgs0,self.btnAudioLbl0,0)
self.set_button_name(self.btnAudioPrgs1,self.btnAudioLbl1,1)
self.set_button_name(self.btnAudioPrgs2,self.btnAudioLbl2,2)
@ -1110,7 +1179,7 @@ class NRSC5_DUI(object):
if (self.cbAutoGain.get_active()):
self.spinGain.set_value(self.streamInfo["Gain"])
self.lblGain.set_label("{:2.1f}dB".format(self.streamInfo["Gain"]))
self.lblGain.set_label("{:2.2f}dB".format(self.streamInfo["Gain"]))
# second param is lot id, if -1, show cover, otherwise show cover
# technically we should show the file with the matching lot id
@ -1148,7 +1217,7 @@ class NRSC5_DUI(object):
self.statusTimer.start()
def processTrafficMap(self, fileName):
global aasDir, mapDir
global aasDir, mapDir, imgLANCZOS
r = re.compile("^[\d]+_TMT_.*_([1-3])_([1-3])_([\d]{4})([\d]{2})([\d]{2})_([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})_([0-9A-Fa-f]{4})\..*$") # match file name
m = r.match(fileName)
@ -1201,7 +1270,7 @@ class NRSC5_DUI(object):
imgBig = (981,981) # size of a weather map
posTS = (imgBig[0]-235, imgBig[1]-29) # calculate position to put timestamp (bottom right)
imgTS = self.mkTimestamp(t, imgBig, posTS) # create timestamp for a weather map
imgTS = imgTS.resize((imgMap.size[0], imgMap.size[1]), Image.Resampling.LANCZOS) # resize it so it's proportional to the size of a traffic map (981 -> 600)
imgTS = imgTS.resize((imgMap.size[0], imgMap.size[1]), imgLANCZOS) # resize it so it's proportional to the size of a traffic map (981 -> 600)
imgMap = Image.alpha_composite(imgMap, imgTS) # overlay timestamp on traffic map
imgMap.save(os.path.join(mapDir, "TrafficMap.png")) # save traffic map
@ -1209,13 +1278,13 @@ class NRSC5_DUI(object):
# display on map page
if (self.radMapTraffic.get_active()):
img_size = min(self.alignmentMap.get_allocated_height(), self.alignmentMap.get_allocated_width()) - 12
imgMap = imgMap.resize((img_size, img_size), Image.Resampling.LANCZOS) # scale map to fit window
imgMap = imgMap.resize((img_size, img_size), imgLANCZOS) # scale map to fit window
self.imgMap.set_from_pixbuf(imgToPixbuf(imgMap)) # convert image to pixbuf and display
if (self.mapViewer is not None): self.mapViewer.updated(0) # notify map viwerer if it's open
def processWeatherOverlay(self, fileName):
global aasDir, mapDir
global aasDir, mapDir, imgLANCZOS
r = re.compile("^[\d]+_DWRO_(.*)_.*_([\d]{4})([\d]{2})([\d]{2})_([\d]{2})([\d]{2})_([0-9A-Fa-f]+)\..*$") # match file name
m = r.match(fileName)
@ -1264,7 +1333,7 @@ class NRSC5_DUI(object):
posTS = (imgMap.size[0]-235, imgMap.size[1]-29) # calculate position to put timestamp (bottom right)
imgTS = self.mkTimestamp(t, imgMap.size, posTS) # create timestamp
imgRadar = Image.open(wxOlPath).convert("RGBA") # open radar overlay
imgRadar = imgRadar.resize(imgMap.size, Image.Resampling.LANCZOS) # resize radar overlay to fit the map
imgRadar = imgRadar.resize(imgMap.size, imgLANCZOS) # resize radar overlay to fit the map
imgMap = Image.alpha_composite(imgMap, imgRadar) # overlay radar image on map
imgMap = Image.alpha_composite(imgMap, imgTS) # overlay timestamp
imgMap.save(wxMapPath) # save weather map
@ -1274,7 +1343,7 @@ class NRSC5_DUI(object):
# display on map page
if (self.radMapWeather.get_active()):
img_size = min(self.alignmentMap.get_allocated_height(), self.alignmentMap.get_allocated_width()) - 12
imgMap = imgMap.resize((img_size, img_size), Image.Resampling.LANCZOS) # scale map to fit window
imgMap = imgMap.resize((img_size, img_size), imgLANCZOS) # scale map to fit window
self.imgMap.set_from_pixbuf(imgToPixbuf(imgMap)) # convert image to pixbuf and display
self.proccessWeatherMaps() # get rid of old maps and add new ones to the list
@ -1448,6 +1517,16 @@ class NRSC5_DUI(object):
self.lastLOT = lot
self.xhdrChanged = True
self.debugLog("XHDR Changed: {:s} (lot {:s})".format(xhdr,lot))
elif (self.regex[23].match(line)):
# match HERE Images
m = self.regex[23].match(line)
if (m):
p = int(m.group(1),16)
mime = m.group(2)
fileSize = int(m.group(3))
fileName = "HERE_Image.jpg"
# if (mime == "B7F03DFC"):
# print (line)
elif (self.regex[7].match(line)):
# match album art
m = self.regex[7].match(line)
@ -1556,7 +1635,7 @@ class NRSC5_DUI(object):
# match Open device failed
self.on_btnStop_clicked(None)
self.set_synchronization(-1)
def getControls(self):
global resDir
# setup gui
@ -1580,6 +1659,7 @@ class NRSC5_DUI(object):
self.alignmentMap = builder.get_object("alignment_map")
self.imgMap = builder.get_object("imgMap")
self.spinFreq = builder.get_object("spinFreq")
self.cbxAspect = builder.get_object("cbxAspect")
self.cbxSDRRadio = builder.get_object("cbxSDRRadio")
self.spinGain = builder.get_object("spinGain")
self.cbAutoGain = builder.get_object("cbAutoGain")
@ -1617,6 +1697,8 @@ class NRSC5_DUI(object):
self.lblSlogan = builder.get_object("lblSlogan")
self.lblMessage = builder.get_object("lblMessage")
self.lblAlert = builder.get_object("lblAlert")
self.txtMessage2 = builder.get_object("txtMessage2")
self.txtAlert2 = builder.get_object("txtAlert2")
self.btnAudioPrgs0 = builder.get_object("btn_audio_prgs0")
self.btnAudioPrgs1 = builder.get_object("btn_audio_prgs1")
self.btnAudioPrgs2 = builder.get_object("btn_audio_prgs2")
@ -1743,6 +1825,14 @@ class NRSC5_DUI(object):
self.lblSlogan.set_tooltip_text("")
self.lblMessage.set_label("")
self.lblMessage.set_tooltip_text("")
if (self.txtMessage2):
self.txtMessage2.set_label("")
self.txtMessage2.set_tooltip_text("")
self.lblAlert.set_label("")
self.lblAlert.set_tooltip_text("")
if (self.txtAlert2):
self.txtAlert2.set_label("")
self.txtAlert2.set_tooltip_text("")
self.btnAudioPrgs0.set_sensitive(False)
self.btnAudioPrgs1.set_sensitive(False)
self.btnAudioPrgs2.set_sensitive(False)
@ -2122,12 +2212,13 @@ class NRSC5_Map(object):
self.callback() # run the callback
def animate(self):
global imgLANCZOS
fileName = self.weatherMaps[self.mapIndex] if len(self.weatherMaps) else ""
if (os.path.isfile(fileName)):
self.animateBusy = True # set busy to true
if (self.config["scale"]):
mapImg = imgToPixbuf(Image.open(fileName).resize((600,600), Image.Resampling.LANCZOS)) # open weather map, resize to 600x600, and convert to pixbuf
mapImg = imgToPixbuf(Image.open(fileName).resize((600,600), imgLANCZOS)) # open weather map, resize to 600x600, and convert to pixbuf
else:
mapImg = imgToPixbuf(Image.open(fileName)) # open weather map and convert to pixbuf
@ -2150,9 +2241,11 @@ class NRSC5_Map(object):
self.mapIndex = 0
def showImage(self, fileName, scale):
global imgLANCZOS
if (os.path.isfile(fileName)):
if (scale):
mapImg = Image.open(fileName).resize((600,600), Image.Resampling.LANCZOS) # open and scale map to fit window
mapImg = Image.open(fileName).resize((600,600), imgLANCZOS) # open and scale map to fit window
else:
mapImg = Image.open(fileName) # open map

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
pygobject==3.44.1
musicbrainzngs==0.7.1
numpy==1.25.1
Pillow==10.0.0
pyOpenSSL==23.0.0
python-dateutil==2.8.2
urllib3==1.26.14

2454
res/mainForm-narrow.glade Normal file

File diff suppressed because it is too large Load Diff

2507
res/mainForm-wide.glade Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,9 @@
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjGain">
<property name="lower">0.00</property>
<property name="upper">49.60</property>
<property name="step_increment">0.10</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjPPM">
@ -1340,7 +1341,7 @@
<object class="GtkTable" id="tableSettings">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">11</property>
<property name="n_rows">12</property>
<property name="n_columns">3</property>
<property name="column_spacing">10</property>
<property name="row_spacing">3</property>
@ -1353,6 +1354,57 @@
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel" id="lblAspect">
<property name="visible">True</property>
<property name="sensitive">True</property>
<property name="can_focus">False</property>
<property name="xpad">10</property>
<property name="label" translatable="yes">Aspect ratio:</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
<property name="xalign">1</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="cbxAspect">
<property name="visible">True</property>
<property name="sensitive">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Choose display style</property>
<property name="has_entry">False</property>
<property name="active">0</property>
<signal name="changed" handler="on_cbxAspect_changed" swapped="no"/>
<items>
<item translatable="yes" id="aspectnull">no change</item>
<item translatable="yes" id="aspectnarrow">narrow</item>
<item translatable="yes" id="aspectwidw">wide</item>
</items>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label14e">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lblSDRRadio">
<property name="visible">True</property>
@ -1367,25 +1419,29 @@
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="cbxSDRRadio">
<property name="visible">True</property>
<property name="sensitive">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Choose SDR device</property>
<property name="has_entry">False</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Choose SDR device</property>
<property name="has_entry">False</property>
<property name="active">0</property>
<signal name="changed" handler="on_cbxSDRRadio_changed" swapped="no"/>
<items>
<item translatable="yes" id="rcvrRTL-SDR">RTL-SDR</item>
<item translatable="yes" id="rcvrRTL-SDR">RTL-SDR</item>
<item translatable="yes" id="rcvrSDRPlay">SDRPlay</item>
</items>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1399,6 +1455,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1414,8 +1472,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1430,15 +1488,15 @@
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjGain</property>
<property name="digits">1</property>
<property name="digits">2</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1458,8 +1516,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1475,8 +1533,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1497,8 +1555,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1515,8 +1573,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1533,8 +1591,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1556,8 +1614,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1571,8 +1629,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1589,8 +1647,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1601,14 +1659,14 @@
<property name="sensitive">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">RTL-TCP listening address</property>
<property name="max_length">16</property>
<property name="max_width_chars">16</property>
<property name="max_length">21</property>
<property name="max_width_chars">21</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1627,8 +1685,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1645,8 +1703,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1663,8 +1721,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1678,8 +1736,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1696,8 +1754,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1706,12 +1764,13 @@
<object class="GtkComboBoxText" id="cbxSDRPlayAnt">
<property name="visible">False</property>
<property name="sensitive">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Choose SDRPlay antenna</property>
<property name="has_entry">False</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Choose SDRPlay antenna</property>
<property name="has_entry">False</property>
<property name="active">0</property>
<items>
<item translatable="yes" id="antA">A</item>
<item translatable="yes" id="antAuto">Auto</item>
<item translatable="yes" id="antA">A</item>
<item translatable="yes" id="antB">B</item>
<item translatable="yes" id="antC">C</item>
</items>
@ -1719,8 +1778,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1734,8 +1793,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1751,8 +1810,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1770,8 +1829,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
@ -1784,8 +1843,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1801,8 +1860,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1821,8 +1880,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
@ -1835,8 +1894,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1853,8 +1912,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1873,8 +1932,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
@ -1887,8 +1946,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1905,8 +1964,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
<property name="top_attach">11</property>
<property name="bottom_attach">12</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@ -1925,8 +1984,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
<property name="top_attach">11</property>
<property name="bottom_attach">12</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
@ -1939,8 +1998,8 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
<property name="top_attach">11</property>
<property name="bottom_attach">12</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>