Terminate a python thread that calls gdal functions - python

i have a program that uses gdal to process pictures. That is done in a thread that is started with a button click.
Now, i need a way to terminate this thread at any given point, also with a button click. So i defined a function that i call when the button is clicked:
def terminateThread(self):
self.change_button_caption.emit('Start')
self.print_log.emit('Vorgang abgebrochen')
self.show_progress.emit(0)
self.terminate()
When i replace the actual code with a sleep order, this works. But it doesn't when the thread called a gdal function.
How can i achieve the termination of this thread at any time?
this is the thread:
class MapThread(QThread):
print_log = Signal(str)
show_progress = Signal(int)
change_button_caption = Signal(str)
def __init__(self, path, tab_filelist, targetpath):
QThread.__init__(self)
self.path = path
self.tab_filelist = tab_filelist
self.targetpath = targetpath
def run(self):
self.change_button_caption.emit('Stop')
print('MapThread run', flush=True)
# try:
from osgeo import gdal
from osgeo import osr
self.show_progress.emit(0)
filename = self.tab_filelist[0].rsplit('\\', 1)[1].rsplit('.', 1)[0]
path2 = self.targetpath + "\\" + filename + ".vrt"
pathout = self.targetpath + "\\" + filename + ".mbtiles"
d = gdal.Open(self.path)
proj = osr.SpatialReference(wkt=d.GetProjection())
self.print_log.emit('EPSG Code der Bilddaten: ' + proj.GetAttrValue('AUTHORITY', 1))
# Vituelles Raster erzeugen
vrt_options = gdal.BuildVRTOptions(resampleAlg='cubic', outputSRS=proj)
gdal.BuildVRT(path2, self.tab_filelist, options=vrt_options)
print('VRT Datei erzeugt', flush=True)
self.print_log.emit('VRT Datei erzeugt')
self.show_progress.emit(10)
# mbtiles generieren
creation_options = ["TILE_FORMAT=JPEG", "QUALITY=90"]
src_ds = gdal.Open(path2)
gdal.Translate(pathout, src_ds, format='mbtiles', creationOptions=creation_options)
print('MB Tiles generiert', flush=True)
self.print_log.emit('MB Tiles generiert')
self.show_progress.emit(75)
# Overviews erzeugen
Image = gdal.Open(pathout, 1)
gdal.SetConfigOption("COMPRESS_OVERVIEW", "DEFLATE")
Image.BuildOverviews("AVERAGE", [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048])
self.show_progress.emit(100)
print('Overviews erfolgreich berechnet', flush=True)
self.print_log.emit('Overviews erfolgreich berechnet')
self.change_button_caption.emit('Start')
# except Exception as err:
# print(err)
def terminateThread(self):
self.change_button_caption.emit('Start')
self.print_log.emit('Vorgang abgebrochen')
self.show_progress.emit(0)
self.terminate()
and the functions for comunicating with the ui:
#Slot(str)
def printLog(self, msg):
self.ui.list_status.addItem(msg)
#Slot(int)
def showProgress(self, value):
self.ui.progressBar.setValue(value)
#Slot(str)
def changeCaption(self, txt):
self.ui.btn_start_mbtiles.setText(txt)

Looks like gdal library is a binding - non-python code is called from python. So python's terminate() just can't stop it until execution returned to the python part. If you need to terminate your function immediatly, you may use processes instead of threads.
This may be helpful:
Is it possible to run function in a subprocess without threading or writing a separate file/script.

Related

Trouble with connecting functions to GUI generated with PyQt

I am currently working on a project of simple spectrum analyzer that cooperates with PlutoSDR, which is programmable radio.
I created a simple GUI with QTDesigner, then pyuic'ed it into python code (my version is 3.8)
Here's my code: (I've cut out some irrelevant parts)
# Form implementation generated from reading ui file 'signal_analyzer.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import pyqtgraph
from PyQt5 import QtCore, QtGui, QtWidgets
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.widgets import PlotWidget
import sys
import numpy as np
import argparse
import adi
import time
import threading
import pyqtgraph as pg
np.seterr(divide='ignore')
class FakePluto:
# this is the class that simulates the radio if it's not connected to my computer
"""
perform some operations
"""
# output vector of data
return samples[:self.rx_buffer_size]
class SpectrumAnalyzerThread(threading.Thread):
def __init__(self, sdr, *args, **kwargs):
super().__init__(*args, **kwargs)
"""
set some initial parmeters
"""
self.sdr = sdr
elf.start_freq = int(70e6)
self.end_freq = int(150e6)
self.settings_changed = True
self.result = None
#property
def steps_per_scan(self):
return (self.end_freq - self.start_freq) // self.step_size
def setStop(self):
self.stop = True
print("stop")
def setStart(self):
self.stop = False
print("start")
def setStopFreq(self, win):
self.stop_freq = int(win.lineEdit.text)
self.start_freq += (self.stop_freq - self.start_freq) % self.step_size
def setStartFreq(self, win):
self.start_freq = int(win.lineEdit_2.text)
self.stop_freq += (self.stop_freq - self.start_freq) % self.step_size
def reconfigure(self, start_freq, end_freq, step_size):
self.start_freq = int(start_freq)
self.end_freq = int(end_freq)
self.step_size = int(step_size)
if (self.end_freq - self.start_freq) % self.step_size != 0:
raise Exception('range is not a multiple of the step size')
self.settings_changed = True
def run(self):
while not self.stop:
print("2", end = "\r")
if self.settings_changed:
self._current_freq = self.start_freq
self.result = np.zeros(self.steps_per_scan * self.samples_per_step)
self.sdr.gain_control_mode_chan0 = 'manual'
self.sdr.rx_hardwaregain_chan0 = self.gain
self.sdr.sample_rate = self.step_size
self.sdr.rx_rf_bandwidth = self.step_size
self.sdr.rx_buffer_size = self.samples_per_step
self.settings_changed = False
else:
if self._current_freq + self.step_size >= self.end_freq:
self._current_freq = self.start_freq
else:
self._current_freq += self.step_size
self.sdr.rx_lo = self._current_freq + self.step_size//2
self.sdr.rx() # skip one sample
rx_samples = self.sdr.rx()
psd = np.abs(np.fft.fftshift(np.fft.fft(rx_samples))) ** 2
assert len(psd) == self.samples_per_step
start_idx = (self._current_freq - self.start_freq) // self.step_size * self.samples_per_step
self.result[start_idx:start_idx + self.samples_per_step] = psd
#print(self.result.tolist())
class Ui_MainWindow(QtWidgets.QMainWindow):
def setupUi(self, MainWindow, analyzer_thread):
"""
define UI elements - this part was generated from .UI file
"""
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(800, 140, 231, 25))
self.lineEdit.setObjectName("lineEdit")
self.runButton = QtWidgets.QPushButton(self.centralwidget)
self.runButton.setGeometry(QtCore.QRect(790, 310, 161, 51))
self.runButton.setObjectName("runButton")
self.stopButton = QtWidgets.QPushButton(self.centralwidget)
self.stopButton.setGeometry(QtCore.QRect(790, 380, 161, 51))
self.stopButton.setObjectName("stopButton")
self.retranslateUi(MainWindow)
# connect gui elements with functions
self.stopButton.clicked.connect(analyzer_thread.setStop) # type: ignore
self.runButton.clicked.connect(analyzer_thread.setStart) # type: ignore
self.lineEdit.textChanged[str].connect(analyzer_thread.setStartFreq)
#self.lineEdit_2.textChanged[str].connect(analyzer_thread.setStopFreq)
#self.sweepButton.clicked.connect(self.widget.singleSweep) # type: ignore
#self.lineEdit_3.modified.connect()
QtCore.QMetaObject.connectSlotsByName(self)
# function below is just for testing
def tomatoes(self, analyzer_thread):
print(analyzer_thread.start_freq)
def retranslateUi(self, MainWindow):
"""
UI retranslation
"""
def main():
# this part checks input script arguments, currently I'm using --simulate
parser = argparse.ArgumentParser(description='SDR Spectrum Analyzer for PlutoSDR')
parser.add_argument('pluto_uri', nargs='?', default=adi.Pluto._uri_auto,
help=f'URI of the PlutoSDR device (default: "{adi.Pluto._uri_auto}")')
parser.add_argument('--simulate', action='store_true',
help='Simulate by generating random noise instead of querying the Pluto device')
args = parser.parse_args()
if args.simulate:
sdr = FakePluto()
else:
sdr = adi.Pluto(args.pluto_uri)
# create and start the thread
analyzer_thread = SpectrumAnalyzerThread(sdr)
analyzer_thread.start()
app = QtGui.QApplication(sys.argv)
win = Ui_MainWindow()
win.show()
win.setupUi(win, analyzer_thread)
# this is the function that refreshes the plotWindow in GUI
def update():
if analyzer_thread.result is not None and not analyzer_thread.settings_changed:
print("1", end = "\r")
psd = analyzer_thread.result
num_bins = analyzer_thread.samples_per_step
if len(psd) % num_bins != 0:
raise Exception('num_bins is not a multiple of sample count')
binned = psd.reshape((-1, len(psd) // num_bins)).sum(axis=1)
binned_dB = 10 * np.log10(binned)
f = np.linspace(analyzer_thread.start_freq, analyzer_thread.end_freq, len(binned_dB))
#spectrum_trace.setData(f, binned_dB)
win.graphicsView.clear()
win.graphicsView.plot(f, binned_dB)
dispMax = str(np.amax(binned_dB))
win.currentMaxAmp.display(dispMax[0:5])
# update function is connected to the timer
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(1)
win.tomatoes(analyzer_thread)
app.exec()
analyzer_thread.stop = True
analyzer_thread.join()
if __name__ == '__main__':
main()
Now, I have two independent problems.
First
When I press "Stop" on the GUI, which sets analyzer_thread stop value to 'true', this works correctly (i. e. run function from analyzer_thread stops executing), though the update function is still running - the plot keeps refreshing but with the same values. Yet when I hit "start", the analyzer_thread doesn't start over. I have no idea what causes that.
Second
When I try to change stop frequency value, which should call analyzer_thread.setStopFreq I get this message:
Traceback (most recent call last):
File "main_window_ui.py", line 77, in setStopFreq
self.stop_freq = int(win.lineEdit.text)
AttributeError: 'str' object has no attribute 'lineEdit'
which I think is my mistake while connecting GUI objects to functions, but I couldn't figure out how to fix it. It seems there's a problem with function's arguments, yet when I call tomatoes function from main, it works despite having the same argument (analyzer_thread).
I know that my code is messy and sorry for that. The processing part was done by another person, my role is to make it work with GUI. I am attaching the main window view:
https://i.stack.imgur.com/vUZjj.png
I'll be thankful for any help :)
Your code wont resume running, because when you set self.stop to True once, whole ,,run'' loop terminates and self.run function ends its job, terminating thread. You should rather try something like:
while True:
if not self.stop:
#do your code
else:
time.sleep(0.001) #
And add other possibility to terminate thread completly.
Your functions should rather be named suspend/resume, not stop and start.

PyQt5; problem with threading using QRunnable and QThreadPool

It seems that I have a problem with running threads in my application as follows:
in my MainForm class I have:
.
.
self.threadPool = QtCore.QThreadPool()
self.threadPool.setMaxThreadCount(4)
.
.
def openFileFcn(self):
worker = **Worker**(self, **self.ImageViewer.showImages**, self.files)
self.threadPool.start(worker)
where Worker is a wrapper QRunnable defined as:
class Worker(QtCore.QRunnable):
def __init__(self, fn, *args, **kwargs):
QtCore.QRunnable.__init__(self)
self.fn = fn
self.args = args
self.kwargs = kwargs
def run(self) -> None:
self.fn(*self.args, **self.kwargs)
and self.ImageViewer.showImages refers to the below section of the ImageViewer class:
def loadImg(self, file):
tabName = str()
if len(file) > 15: tabName = file[0:13] + "..."
widget = ImageViewer()
localPath = os.path.abspath(file)
localPath = QtCore.QDir().filePath(localPath)
pixmap = QtGui.QPixmap(localPath)
widget.setImage(pixmap)
self.tabwidget.addTab(widget, self.mainFormHandle.sharedData.imgIcon, tabName)
self.tabwidget.setCurrentIndex(self.tabwidget.currentIndex() + 1)
def **showImages**(self, files):
files = [file.lower() for file in files]
for file in files:
self.loadImg(file)
Nothing much happens when I run this code. It just freezes and after a while the application closes with exit code -1073740791 (0xC0000409). What do you suggest to be the cause?
The issue seems to be that you are trying to create widgets from another thread than the main thread. You can only create widgets from the main thread but since loadImg() creates several widgets, calling it from the thread pool crashes your application. One way to get around this is to split loadImg() into two methods, one method that loads the pixmaps, and another one that creates the ImageViewers and adds the tabs. The first one can then be moved to the thread pool, and you can use a signal to automatically call the second one every time a pixmap is loaded. For example:
class TabArea(QtWidgets.QWidget):
pixmap_loaded = QtCore.pyqtSignal(str, QtGui.QPixmap)
def __init__(self, parent=None):
...
self.pixmap_loaded.connect(self.addTab)
def loadImg(self, file):
localPath = os.path.abspath(file)
localPath = QtCore.QDir().filePath(localPath)
pixmap = QtGui.QPixmap(localPath)
# emit pixmap and file name
self.pixmap_loaded.emit(file, pixmap)
def addTab(self, file, pixmap):
tabName = str()
if len(file) > 15: tabName = file[0:13] + "..."
widget = ImageViewer()
widget.setImage(pixmap)
self.tabwidget.addTab(widget, self.mainFormHandle.sharedData.imgIcon, tabName)
self.tabwidget.setCurrentIndex(self.tabwidget.currentIndex() + 1)
def showImages(self, files):
files = [file.lower() for file in files]
for file in files:
self.loadImg(file)

Concurrent functions running in separate process using pygame and multiprocessing

Suppose we want to drive an autonomous car by predicting image labels from a previous set of images and labels collected (A Machine Learning application). For this task, the car is connected via bluetooth serial (rfcomm) to the Host Computer (A PC with *NIX) and the images are streamed directly from an Android phone using IP Webcam, meanwhile, the PC is running a program that links this two functions, displaying the captured images in a drawing environment created by pygame, and sending the instructions back to the car using serial.
At the moment, I've tried to implement those processes using the multiprocessing module, the seemed to work, but when I execute the client, the drawing function (if __name__ == '__main__') works after the getKeyPress() function ends.
The question is: It is possible to parallelize or synchronize the drawing fuinction enclosed within the if __name__ == '__main__' with the process declared in getKyPress(), such that the program works in two independent processes?
Here's the implemented code so far:
import urllib
import time
import os
import sys
import serial
import signal
import multiprocessing
import numpy as np
import scipy
import scipy.io as sio
import matplotlib.image as mpimg
from pygame.locals import *
PORT = '/dev/rfcomm0'
SPEED = 115200
ser = serial.Serial(PORT)
status = False
move = None
targets = []
inputs = []
tic = False
def getKeyPress():
import pygame
pygame.init()
global targets
global status
while not status:
pygame.event.pump()
keys = pygame.key.get_pressed()
targets, status = processOutputs(targets, keys)
targets = np.array(targets)
targets = flattenMatrix(targets)
sio.savemat('targets.mat', {'targets':targets})
def rgb2gray(rgb):
r, g, b = np.rollaxis(rgb[...,:3], axis = -1)
return 0.299 * r + 0.587 * g + 0.114 * b
def processImages(inputX, inputs):
inputX = flattenMatrix(inputX)
if len(inputs) == 0:
inputs = inputX
elif inputs.shape[1] >= 1:
inputs = np.hstack((inputs, inputX))
return inputs
def flattenMatrix(mat):
mat = mat.flatten(1)
mat = mat.reshape((len(mat), 1))
return mat
def send_command(val):
connection = serial.Serial( PORT,
SPEED,
timeout=0,
stopbits=serial.STOPBITS_TWO
)
connection.write(val)
connection.close()
def processOutputs(targets, keys):
global move
global status
global tic
status = False
keypress = ['K_p', 'K_UP', 'K_LEFT', 'K_DOWN', 'K_RIGHT']
labels = [1, 2, 3, 4, 5]
commands = ['p', 'w', 'r', 'j', 's']
text = ['S', 'Up', 'Left', 'Down', 'Right']
if keys[K_q]:
status = True
return targets, status
else:
for i, j, k, g in zip(keypress, labels, commands, text):
cmd = compile('cond = keys['+i+']', '<string>', 'exec')
exec cmd
if cond:
move = g
targets.append(j)
send_command(k)
break
send_command('p')
return targets, status
targetProcess = multiprocessing.Process(target=getKeyPress)
targetProcess.daemon = True
targetProcess.start()
if __name__ == '__main__':
import pygame
pygame.init()
w = 288
h = 352
size=(w,h)
screen = pygame.display.set_mode(size)
c = pygame.time.Clock() # create a clock object for timing
pygame.display.set_caption('Driver')
ubuntu = pygame.font.match_font('Ubuntu')
font = pygame.font.Font(ubuntu, 13)
inputs = []
try:
while not status:
urllib.urlretrieve("http://192.168.0.10:8080/shot.jpg", "input.jpg")
try:
inputX = mpimg.imread('input.jpg')
except IOError:
status = True
inputX = rgb2gray(inputX)/255
out = inputX.copy()
out = scipy.misc.imresize(out, (352, 288), interp='bicubic', mode=None)
scipy.misc.imsave('input.png', out)
inputs = processImages(inputX, inputs)
print inputs.shape[1]
img=pygame.image.load('input.png')
screen.blit(img,(0,0))
pygame.display.flip()
c.tick(1)
if move != None:
text = font.render(move, False, (255, 128, 255), (0, 0, 0))
textRect = text.get_rect()
textRect.centerx = 20 #screen.get_rect().centerx
textRect.centery = 20 #screen.get_rect().centery
screen.blit(text, textRect)
pygame.display.update()
if status:
targetProcess.join()
sio.savemat('inputs.mat', {'inputs':inputs})
except KeyboardInterrupt:
targetProcess.join()
sio.savemat('inputs.mat', {'inputs':inputs})
targetProcess.join()
sio.savemat('inputs.mat', {'inputs':inputs})
Thanks in advance.
I would personally suggest writing this without using the multiprocessing module: it uses fork() which has unspecified effects with most complex libraries, like in this case pygame.
You should try to write this as two completely separate programs. It forces you to think about what data needs to go from one to the other, which is both a bad and a good thing (as it may clarify things). You can use some inter-process communication facility, like the stdin/stdout pipe; e.g. in one program (the "main" one) you start the other as a sub-process like this:
popen = subprocess.Popen([sys.executable, '-u', 'my_subproc.py'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(The -u is for unbuffered.)
Then read/write the data to popen.stdin/popen.stdout in the parent process, and to sys.stdin/sys.stdout in the subprocess. The simplest example would be if the two processes only need a synchronization signal, e.g. the parent process waits in a loop for the subprocess to say "next please". To do this the subprocess does print 'next please', and the parent process does popen.stdin.readline(). (The print goes to sys.stdin in the subprocess.)
Unrelated small note:
keypress = ['K_p', ...]
...
cmd = compile('cond = keys['+i+']', '<string>', 'exec')
exec cmd
if cond:
This looks like very heavy code to just do:
keypress = [K_p, ...] # not strings, directly the values
...
if keys[i]:
My suggestion is to use separate threads.
#At the beginning
import threading
#Instead of def getKeyPress()
class getKeyPress(threading.Thread):
def run(self):
import pygame
pygame.init()
global targets
global status
while not status:
pygame.event.pump()
keys = pygame.key.get_pressed()
targets, status = processOutputs(targets, keys)
targets = np.array(targets)
targets = flattenMatrix(targets)
sio.savemat('targets.mat', {'targets':targets})
#Instead of
#targetProcess = multiprocessing.Process(target=getKeyPress)
#targetProcess.daemon = True
#targetProcess.start()
gkp = getKeyPress()
gkp.start()
An alternative would be creating two different scripts and using sockets to handle the inter-process communication.

PyQt4: Interrupted system call while calling commands.getoutput() in timer

The problem appeared to be very simple, but I can not find any solution after a day of googling and looking at stackoverflow.
Originally I am developing a simple plasmoid which will send a specific request to local web-server every 30 minutes, parse output and display in a label on panel. I took an example of plasmoid - BWC-Balance - and modified it. Here is the code:
#!/usr/bin/env python
# coding: utf-8
"""
BWC Balance plasmoid
Site: http://bitbucket.org/svartalf/bwc-balance-plasmoid/
Author: SvartalF (http://svartalf.info)
Original idea: m0nochr0me (http://m0nochr0me.blogspot.com)
"""
import re
from urllib import urlencode
import urllib2
import cookielib
import datetime
import sys
import re
import string
import os
import gobject
import commands
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyKDE4.kio import *
from PyKDE4.kdeui import *
from PyKDE4.kdecore import *
from PyKDE4.plasma import Plasma
from PyKDE4 import plasmascript
from PyKDE4.solid import Solid
from settings import SettingsDialog
parsed_ok = 0
curr_day = ''
class BWCBalancePlasmoid(plasmascript.Applet):
"""Applet main class"""
def __init__(self, parent, args=None):
plasmascript.Applet.__init__(self, parent)
def init(self):
"""Applet settings"""
self.setHasConfigurationInterface(True)
self.setAspectRatioMode(Plasma.Square)
self.theme = Plasma.Svg(self)
# self.theme.setImagePath("widgets/background")
# self.setBackgroundHints(Plasma.Applet.DefaultBackground)
self.layout = QGraphicsLinearLayout(Qt.Horizontal, self.applet)
# Main label with balance value
self.label = Plasma.Label(self.applet)
self.label.setText(u'<b><font color=blue size=3>No data...</font></b>')
self.layout.addItem(self.label)
self.applet.setLayout(self.layout)
self.resize(350, 30)
self.startTimer(2500)
def postInit(self):
"""Start timer and do first data fetching
Fired only if user opened access to KWallet"""
self.setLabelText()
def update(self, value):
"""Update label text"""
self.label.setText(value)
def timerEvent(self, event):
"""Create thread by timer"""
self.setLabelText()
pass
def setLabelText(self):
login = 'mylogin'
request = 'curl --ntlm -sn http://some.local.resource'
out_exp = ""
out_exp = commands.getoutput(request)
table_name_exp = re.findall(r"some_regular_expression",out_exp)
tp = '| html2text | grep -i -A3 ' + login
out_exp = ''
try:
cmd_exp = 'curl --ntlm -sn ' + table_name_exp[0] + ' ' + tp
out_exp = commands.getoutput(cmd_exp)
except:
cmd_exp = ''
date_check = re.findall(r"one_more_regular_expression", out_exp)
times_exp = re.findall(r"[0-9][0-9]:[0-9][0-9]", out_exp )
if len(times_exp) != 0 and len(date_check) != 0:
self.label.setText(u'<b><font color=blue size=3>Start: ' + times_exp[0] + u' --- Finish: ' + str(int(string.split(times_exp[0], ':')[0]) + 9) + ':' + string.split(times_exp[0], ':')[1] + ' </span></b>')
else:
self.label.setText(u'<b><font color=blue size=3>No data...</span></b>')
def CreateApplet(parent):
return BWCBalancePlasmoid(parent)
And what I get is the following error:
# plasmoidviewer bwc-balance
plasmoidviewer(25255)/kdecore (services) KServiceFactory::findServiceByDesktopPath: "" not found
plasmoidviewer(25255)/libplasma Plasma::FrameSvg::resizeFrame: Invalid size QSizeF(0, 0)
plasmoidviewer(25255)/libplasma Plasma::FrameSvg::resizeFrame: Invalid size QSizeF(0, 0)
Traceback (most recent call last):
File "/home/grekhov/.kde/share/apps/plasma/plasmoids/bwc-balance/contents/code/main.py", line 116, in timerEvent
self.setLabelText()
File "/home/grekhov/.kde/share/apps/plasma/plasmoids/bwc-balance/contents/code/main.py", line 146, in setLabelText
out_exp = commands.getoutput(request)
File "/usr/lib/python2.7/commands.py", line 50, in getoutput
return getstatusoutput(cmd)[1]
File "/usr/lib/python2.7/commands.py", line 60, in getstatusoutput
text = pipe.read()
IOError: [Errno 4] Interrupted system call
As I understood after several hours of googling: reading from pipe is interrupted with some signal. But the only signal I have is timer. The only recommendation I have found is "get rid of the signal which interrupts your read". And it appears a bit strange and unrealistic for me: read data periodically without timer.
Am I missing something? Maybe there should be used some other mechanism for accessing web-resource and parsing its output? Or "Interrupted system call" is a normal situation and should be handled somehow?
Thanks in advance for help.
It appears that a signal is being delivered whilst the pipe is still reading.
So try stopping the timer before calling setLabelText(), and then restart it again afterwards.
EDIT
You should also try rewriting your code to use subprocess instead of the deprecated commands module. For example:
pipe = subprocess.Popen(['curl', '--ntlm', '-sn',
'http://some.local.resource'],
stdout=subprocess.PIPE)
output = pipe.communicate()[0]

Refresh QTextEdit in PyQt

Im writing a PyQt app that takes some input in one widget, and then processes some text files.
What ive got at the moment is when the user clicks the "process" button a seperate window with a QTextEdit in it pops up, and ouputs some logging messages.
On Mac OS X this window is refreshed automatically and you cna see the process.
On Windows, the window reports (Not Responding) and then once all the proccessing is done, the log output is shown. Im assuming I need to refresh the window after each write into the log, and ive had a look around at using a timer. etc, but havnt had much luck in getting it working.
Below is the source code. It has two files, GUI.py which does all the GUI stuff and MOVtoMXF that does all the processing.
GUI.py
import os
import sys
import MOVtoMXF
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Form(QDialog):
def process(self):
path = str(self.pathBox.displayText())
if(path == ''):
QMessageBox.warning(self, "Empty Path", "You didnt fill something out.")
return
xmlFile = str(self.xmlFileBox.displayText())
if(xmlFile == ''):
QMessageBox.warning(self, "No XML file", "You didnt fill something.")
return
outFileName = str(self.outfileNameBox.displayText())
if(outFileName == ''):
QMessageBox.warning(self, "No Output File", "You didnt do something")
return
print path + " " + xmlFile + " " + outFileName
mov1 = MOVtoMXF.MOVtoMXF(path, xmlFile, outFileName, self.log)
self.log.show()
rc = mov1.ScanFile()
if( rc < 0):
print "something happened"
#self.done(0)
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.log = Log()
self.pathLabel = QLabel("P2 Path:")
self.pathBox = QLineEdit("")
self.pathBrowseB = QPushButton("Browse")
self.pathLayout = QHBoxLayout()
self.pathLayout.addStretch()
self.pathLayout.addWidget(self.pathLabel)
self.pathLayout.addWidget(self.pathBox)
self.pathLayout.addWidget(self.pathBrowseB)
self.xmlLabel = QLabel("FCP XML File:")
self.xmlFileBox = QLineEdit("")
self.xmlFileBrowseB = QPushButton("Browse")
self.xmlLayout = QHBoxLayout()
self.xmlLayout.addStretch()
self.xmlLayout.addWidget(self.xmlLabel)
self.xmlLayout.addWidget(self.xmlFileBox)
self.xmlLayout.addWidget(self.xmlFileBrowseB)
self.outFileLabel = QLabel("Save to:")
self.outfileNameBox = QLineEdit("")
self.outputFileBrowseB = QPushButton("Browse")
self.outputLayout = QHBoxLayout()
self.outputLayout.addStretch()
self.outputLayout.addWidget(self.outFileLabel)
self.outputLayout.addWidget(self.outfileNameBox)
self.outputLayout.addWidget(self.outputFileBrowseB)
self.exitButton = QPushButton("Exit")
self.processButton = QPushButton("Process")
self.buttonLayout = QHBoxLayout()
#self.buttonLayout.addStretch()
self.buttonLayout.addWidget(self.exitButton)
self.buttonLayout.addWidget(self.processButton)
self.layout = QVBoxLayout()
self.layout.addLayout(self.pathLayout)
self.layout.addLayout(self.xmlLayout)
self.layout.addLayout(self.outputLayout)
self.layout.addLayout(self.buttonLayout)
self.setLayout(self.layout)
self.pathBox.setFocus()
self.setWindowTitle("MOVtoMXF")
self.connect(self.processButton, SIGNAL("clicked()"), self.process)
self.connect(self.exitButton, SIGNAL("clicked()"), self, SLOT("reject()"))
self.ConnectButtons()
class Log(QTextEdit):
def __init__(self, parent=None):
super(Log, self).__init__(parent)
self.timer = QTimer()
self.connect(self.timer, SIGNAL("timeout()"), self.updateText())
self.timer.start(2000)
def updateText(self):
print "update Called"
AND MOVtoMXF.py
import os
import sys
import time
import string
import FileUtils
import shutil
import re
class MOVtoMXF:
#Class to do the MOVtoMXF stuff.
def __init__(self, path, xmlFile, outputFile, edit):
self.MXFdict = {}
self.MOVDict = {}
self.path = path
self.xmlFile = xmlFile
self.outputFile = outputFile
self.outputDirectory = outputFile.rsplit('/',1)
self.outputDirectory = self.outputDirectory[0]
sys.stdout = OutLog( edit, sys.stdout)
class OutLog():
def __init__(self, edit, out=None, color=None):
"""(edit, out=None, color=None) -> can write stdout, stderr to a
QTextEdit.
edit = QTextEdit
out = alternate stream ( can be the original sys.stdout )
color = alternate color (i.e. color stderr a different color)
"""
self.edit = edit
self.out = None
self.color = color
def write(self, m):
if self.color:
tc = self.edit.textColor()
self.edit.setTextColor(self.color)
#self.edit.moveCursor(QtGui.QTextCursor.End)
self.edit.insertPlainText( m )
if self.color:
self.edit.setTextColor(tc)
if self.out:
self.out.write(m)
self.edit.show()
If any other code is needed (i think this is all that is needed) then just let me know.
Any Help would be great.
Mark
It looks like your are running an external program, capturing its output into a QTextEdit. I didn't see the code of Form.process, but I am guessing on windows your function waits for the external program to finish, then quickly dumps everything to the QTextEdit.
If your interface really is waiting for the other process to finish, then it will hang in the manner you describe. You'll need to look at subprocess or perhaps even popen to get the program's output in a "non-blocking" manner.
The key to avoiding "(Not Responding)" is to call QApplication.processEvents a few times every few seconds. The QTimer is not going to help in this case, because if Qt cannot process its events, it cannot call any signal handlers.

Categories

Resources