2

I have some problems because I'm newbie in Python and Pyside.

I have N processes which are running at the same time.

Since these processes take some times to finish their job, it's possible that end user wants to cancel a specific process. So I need a way to know the IDs of processes for adding this feature to the program.

There is an answer in Stackoverflow which is exactly what I'm doing.

Here is the code :

#!/usr/bin/env python3
import multiprocessing, multiprocessing.pool, time, random, sys
from PySide.QtCore import *
from PySide.QtGui import *

def compute(num_row):
    print("worker started at %d" % num_row)
    random_number = random.randint(1, 10)
    for second in range(random_number):
        progress = float(second) / float(random_number) * 100
        compute.queue.put((num_row, progress,))
        time.sleep(1)
    compute.queue.put((num_row, 100))

def pool_init(queue):
    # see https://stackoverflow.com/a/3843313/852994
    compute.queue = queue

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.toolBar = self.addToolBar("Toolbar")
        self.toolBar.addAction(QAction('Add Task', self, triggered=self.addTask))
        self.table = QTableWidget()
        self.table.verticalHeader().hide()
        self.table.setColumnCount(2)
        self.setCentralWidget(self.table)

        # Pool of Background Processes
        self.queue = multiprocessing.Queue()
        self.pool = multiprocessing.Pool(processes=4, initializer=pool_init, initargs=(self.queue,))
        # Check for progress periodically
        self.timer = QTimer()
        self.timer.timeout.connect(self.updateProgress)
        self.timer.start(2000)

    def addTask(self):
        num_row = self.table.rowCount()
        self.pool.apply_async(func=compute, args=(num_row,))
        label = QLabel("Queued")
        bar = QProgressBar()
        bar.setValue(0)
        self.table.setRowCount(num_row + 1)
        self.table.setCellWidget(num_row, 0, label)
        self.table.setCellWidget(num_row, 1, bar)

    def updateProgress(self):
        if self.queue.empty(): return
        num_row, progress = self.queue.get() # unpack
        print("received progress of %s at %s" % (progress, num_row))
        label = self.table.cellWidget(num_row, 0)
        bar = self.table.cellWidget(num_row, 1)
        bar.setValue(progress)
        if progress == 100:
            label.setText('Finished')
        elif label.text() == 'Queued':
            label.setText('Downloading')
        self.updateProgress() # recursion

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

I added an "stop" button, and I know how to get the selected row in the table, but I don't know how to get the process id of the selected row for terminating.

update 1 :

for make this easier I can change

multiprocessing.Pool(processes=4, initializer=pool_init, initargs=(self.queue,)) to

multiprocessing.Pool(processes=1, initializer=pool_init, initargs=(self.queue,))

in this way all processes have to wait till a process finish

now we have one process running and others are in queue,How I can get just the process id of that running process ?

Community
  • 1
  • 1
Mohammadhzp
  • 498
  • 7
  • 20
  • What exactly are your processes doing? Downloading, or some other kind of tasks? Do you actually need to use multiprocessing? It may be that there are easier ways to implement what you want, but it depends on the precise nature of the tasks you want to run in parallel. – ekhumoro Jan 02 '14 at 01:33
  • processes are uploading actually,User select a file and upload it on server.I'm totally new to python/pyside and don't know what is the best exactly,What's your suggest ekhumoro ?it would be great if you tell me from which way I get it done,thanks – Mohammadhzp Jan 02 '14 at 02:20
  • You should look at [QNetworkAccessManager](http://seanfisk.com/pyside-docs/pyside/PySide/QtNetwork/QNetworkAccessManager.html) and [QNetworkReply](http://seanfisk.com/pyside-docs/pyside/PySide/QtNetwork/QNetworkReply.html). This will allow you to get rid of `multiprocessing` (which you don't need), and handle everything with signals. – ekhumoro Jan 02 '14 at 02:47
  • Just one more question to ask,if I implement program this way is it possible to do parallel processes ?(i.e it must show speed of uploading file real-time and "time left" for uploading or adding more file to upload...).I'm asking this because I want to know I'm doing the right thing and later I'm not facing a problem.thanks – Mohammadhzp Jan 02 '14 at 08:02
  • See my answer for a basic demo that does parallel upoading with progress and aborting. The remaining features I will leave as an exercise for you... – ekhumoro Jan 03 '14 at 04:10

2 Answers2

1

I hacked together an demo that more or less reproduces the multiprocessing example, with the addition of the ability to abort uploads. It can only handle six parallel uploads at a time because that is the maximum QNetworkAccessManager will allow. However, this limit could be increased by simply adding another QNetworkAccessManager.

There was one issue I came across while testing the demo. It seems that under some circumstances, the post-data can get sent twice (see here, for example). But I don't know whether this is a Qt bug, or an issue with my test setup (I used a python threaded httpserver). Anyway, it was easy enough to fix by closing the reply-object in the uploadProgress handler (see below).

from PyQt4 import QtCore, QtGui, QtNetwork

class Window(QtGui.QWidget):
    def __init__(self, address):
        QtGui.QWidget.__init__(self)
        self.address = address
        self.table = QtGui.QTableWidget(self)
        header = self.table.horizontalHeader()
        header.setStretchLastSection(True)
        header.hide()
        self.table.setColumnCount(2)
        self.button = QtGui.QPushButton('Add Upload', self)
        self.button.clicked.connect(self.handleAddUpload)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.table)
        layout.addWidget(self.button)
        self.netaccess = QtNetwork.QNetworkAccessManager(self)
        self._uploaders = {}

    def handleAddUpload(self):
        stream = QtCore.QFile('files/sample.tar.bz2')
        if stream.open(QtCore.QIODevice.ReadOnly):
            data = stream.readAll()
            stream.close()
            row = self.table.rowCount()
            button = QtGui.QPushButton('Abort', self.table)
            button.clicked.connect(lambda: self.handleAbort(row))
            progress = QtGui.QProgressBar(self.table)
            progress.setRange(0, len(data))
            self.table.setRowCount(row + 1)
            self.table.setCellWidget(row, 0, button)
            self.table.setCellWidget(row, 1, progress)
            uploader = self._uploaders[row] = Uploader(row, self.netaccess)
            uploader.uploadProgress.connect(self.handleUploadProgress)
            uploader.uploadFinished.connect(self.handleUploadFinished)
            uploader.upload(data, self.address)

    def handleUploadProgress(self, key, sent, total):
        print('upload(%d): %d [%d]' % (key, sent, total))
        progress = self.table.cellWidget(key, 1)
        progress.setValue(sent)

    def handleUploadFinished(self, key):
        print('upload(%d) finished' % key)
        button = self.table.cellWidget(key, 0)
        button.setDisabled(True)
        uploader = self._uploaders.pop(key)
        uploader.deleteLater()

    def handleAbort(self, key):
        try:
            self._uploaders[key].abort()
        except (KeyError, AttributeError):
            pass

class Uploader(QtCore.QObject):
    uploadProgress = QtCore.pyqtSignal(object, int, int)
    uploadFinished = QtCore.pyqtSignal(object)

    def __init__(self, key, parent):
        QtCore.QObject.__init__(self, parent)
        self._key = key
        self._reply = None

    def abort(self):
        if self._reply is not None:
            self._reply.abort()

    def upload(self, data, url):
        if self._reply is None:
            request = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
            request.setHeader(
                QtNetwork.QNetworkRequest.ContentTypeHeader,
                'application/x-www-form-urlencoded')
            self._reply = self.parent().post(request, data)
            self._reply.uploadProgress.connect(self.handleUploadProgress)
            self._reply.finished.connect(self.handleFinished)

    def handleUploadProgress(self, sent, total):
        self.uploadProgress.emit(self._key, sent, total)
        if sent >= total:
            # prevent duplicated uploads
            self._reply.close()

    def handleFinished(self):
        self._reply.deleteLater()
        self._reply = None
        self.uploadFinished.emit(self._key)

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window('http://localhost:54321/upload')
    window.setGeometry(500, 300, 500, 300)
    window.show()
    sys.exit(app.exec_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
0

The easiest way is to list all of the processes and then use os.kill to kill it.

import os
pids= [pid for pid in os.listdir('/proc') if pid.isdigit()]

for pid in pids:
    print open(os.path.join('/proc', pid, 'cmdline'), 'rb').read()

But you should look at doing this with threading.

cylus
  • 357
  • 1
  • 4
  • 14
  • There is noway to solve this with multiprocessing ? I updated my question,maybe now it's possible to do that – Mohammadhzp Jan 02 '14 at 02:39
  • I guess I dont really understand why you want to do this with multiprocessing instead of multithreading. Processes get their own memory space and have quite a bit more overhead than a thread. A thread can be spun off very easily, and honestly, you would probably want to kill all threads at once instead of killing a specific one (just a guess... not completely sure what you are trying to do). Again, I would look at threads, but what are you trying to do? – cylus Jan 02 '14 at 14:04
  • This http://stackoverflow.com/questions/20874870/how-to-get-each-process-id-when-multiprocessing/20875250?noredirect=1#comment31325061_20874870 is what I'm trying to do,ekhumoro suggested me to handle by signals,and actually QNetworkAccessManager and QNetworkReply have good feature but I want to do the right thing to (http://stackoverflow.com/questions/20874870/how-to-get-each-process-id-when-multiprocessing/20875250?noredirect=1#comment31330588_20874870) avoid problems in my program later,Thanks for your reply – Mohammadhzp Jan 02 '14 at 15:14