pyds9: графический интерфейс DS9 зависает после отображения двух больших изображений
Мне дали короткий скрипт Python 2.7, который отслеживает попадание новых файлов FITS в каталог, используя XPA для управления экземпляром DS9 8.0.1 (Fedora 30, xpa-devel.x86_64 2.1.18-7.f30) для отображать их по два кадра за раз.
В исходном сценарии используется серия Popen()
команды для запуска xpaset
для форматирования окна DS9 и указания файлов для загрузки. Коду уже десять лет, и он не может пройти ночь наблюдений без его сбоя / зависания DS9, отчасти из-за сетевых задержек, влияющих на XPA.
Я заново реализовал скрипт с нуля, используя Python 3.7 и pyds9 (pyds9.noarch 1.8.1-3.2). Новый код и DS9 будут работать всю ночь при загрузке 6,5 МБ фокусирующего изображения. Но после загрузки первых двух полноразмерных изображений размером 85 МБ DS9 блокируется и требует "kill -9", чтобы избавиться от него.
Версии ОС, xpa и DS9 совпадают, что говорит о том, что ошибка связана с моим скриптом. Сложность ожидала завершения процесса, записывающего файлы FITS, и закрытия файлов.
В интересах краткости, я надеюсь, что причина зависания DS9 с моим использованием pyds9 будет очевидна из урезанных образцов ниже. В противном случае я отредактирую это, чтобы включить функциональный код.
Изменения: Обращаясь к комментариям @Iguananaut, я консолидировал время ожидания запуска DS9 и добавил новый поиск файлов и функцию wait_til_file_is_stable в новый образец кода.
Исходные инструкции на основе Popen():
#!/bin/env python
from subprocess import Popen
from time import sleep
import uuid
new_fits_filename = ""
old_fits_filename = ""
display_id='This_Script-' + uuid.uuid4().hex[:6].upper()
ds9_pid = Popen(['ds9','-geometry','940x1200','-title',display_id]).pid
if Popen(['xpaget',display_id,'blink'],stdout=DEVNULL,stderr=DEVNULL).wait() == 0:
break
sleep(.5)
Popen(['xpaset','-p',display_id,'scale','mode','zscale'])
Popen(['xpaset','-p',display_id,'frame','1'])
Popen(['xpaset','-p',display_id,'cmap','cool'])
Popen(['xpaset','-p',display_id,'frame','2'])
Popen(['xpaset','-p',display_id,'cmap','bb'])
Popen(['xpaset','-p',display_id,'tile'])
while True:
[detect new FITS file in NFS target directory]
Popen(['xpaset','-p',display_id,'frame','1'])
Popen(['xpaset','-p',display_id,'file','mosaicimage','wcs',directory+'/'+new_fits_filename])
sleep(1) # Sleep again to avoid DS9 crashing
Popen(['xpaset','-p',display_id,'zoom','to','fit'])
if old_fits_filename != "":
Popen(['xpaset','-p',display_id,'frame','2'])
Popen(['xpaset','-p',display_id ,'file','mosaicimage','wcs',directory+ '/'+ old_fits_filename])
sleep(1) # Sleep again to avoid DS9 crashing
Popen(['xpaset','-p',display_id,'zoom','to','fit'])
old_fits_filename = new_fits_filename
Новые инструкции на основе pyds9:
#!/bin/env python3
import glob
import os
from pathlib import Path
import pyds9
from time import sleep
import uuid
def wait_til_file_is_stable(filename):
the_file = Path(filename)
the_current_size = 0
the_previous_size = -1
while the_current_size != the_previous_size:
the_previous_size = the_current_size
the_current_size = the_file.stat().st_size
sleep(0.5)
global_d = None
fits_folder = Path("/data/fits/new/") # an NFS mount
fits_file_prefix = "image"
fits_file_pattern = fits_file_prefix + "*.fits"
fits_full_path = str(fits_folder.resolve() / fits_file_pattern)
new_fits_filename = ""
old_fits_filename = ""
wait_time_launch = 70
display_id = 'This_Script-' + uuid.uuid4().hex[:6].upper()
global_d = pyds9.DS9(display_id, start="-geometry 940x1200", wait=wait_time_launch, verify=True)
global_d.set('scale mode zscale')
global_d.set('frame 1')
global_d.set('cmap cool')
global_d.set('frame 2')
global_d.set('cmap bb')
global_d.set('tile')
while True:
sleep(0.5)
# detect new FITS file in NFS target directory
dirlisting = glob.glob(fits_full_path)
if len(dirlisting) > 0:
new_fits_filename = max(filter(lambda fname: fits_file_prefix in fname, dirlisting), key=os.path.getctime)
wait_til_file_is_stable(new_fits_filename)
global_d.set('frame 1')
global_d.set('file mosaicimage wcs ' + str(new_fits_filename))
global_d.set('zoom to fit')
if old_fits_filename != "":
global_d.set('frame 2')
global_d.set('file mosaicimage wcs ' + str(old_fits_filename))
global_d.set('zoom to fit')
old_fits_filename = new_fits_filename