How can i start a separate script in a separate file using PyQt like a button signal or something.
from everywhere* import everthing*
def bJeff(driver):
...
def bLexi(driver):
...
def browser(url, id, user1, parolauser1, user2, parolauser2):
...
#starting 2 browsers and passing them for further processing
bLexi(driver)
...
bJeff(driver)
return
if __name__ == '__main__':
jobs = []
user_Lexi = "user1#mail.com"
parola_user_Lexi = "pass1"
user_Jeff = "user#mail.com"
parola_user_Jeff = "pass2"
sites = ["http://www.didi.com", "http://www.didi.com/"]
id = 0
for pagina in sites:
p = multiprocessing.Process(target=browser, args=(pagina, id, user_Lexi, parola_user_Lexi, user_Jeff, parola_user_Jeff))
id+=1
jobs.append(p)
p.start()
I read and saw how can i make a button but i didn't saw how can i start an outside function from that button.
Edit
I did it like this:
import os, sys, subprocess, filetoinclude
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import PyQt4.QtCore as QtCore
import PyQt4.QtGui as QtGui
class QButton(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
# create objects
self.name='me'
label = QLabel(self.tr("Enter command and press Return"))
self.le = QLineEdit()
self.te = QTextEdit()
self.button = QtGui.QPushButton('Button', self)
# layout
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(self.le)
layout.addWidget(self.button)
layout.addWidget(self.te)
self.setLayout(layout)
# create connection
self.button.clicked.connect(self.calluser)
self.connect(self.le, SIGNAL("returnPressed(void)"), self.run_command)
def calluser(self):
print(self.name)
filetoinclude.dadada()
def run_command(self):
cmd = str(self.le.text())
result = self.system_call(cmd)
self.te.setText(result)
def system_call(self, command):
p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
return p.stdout.read()
def demo_QButton():
app = QtGui.QApplication(sys.argv)
tb = QButton()
tb.show()
app.exec_()
if __name__=='__main__':
demo_QButton()
And i renamed the function from the file instead of
this: if __name__ == '__main__':<---
to this: --->def dadada():
Thank you very much everyone, i knew i will get the right answers here, as always.
You can do it by exactly that: using signals and slots. Signals can be emitted and received from anywhere and are thread safe. You can import your foreign script/function/whatever you need to run in the separate file, and connect your button's clicked() signal to that function. For example, if you need to run a script called myScript() when the button is clicked:
import myScript
...
self.myButton.clicked.connect(self.run_myScript)
def run_myScript(self):
myScript()
When the button is clicked, it'll run myScript. If you want to run it in a separate thread, you can do it like you've been doing.
There are 2 ways to do this.
import file and call the function as file.functionName(). Highly recommended. Note that if your file is called file.py, your import should not include the .py extension at the end.
Using a shell process: os.system('python file.py'). You need to import os for this one.
#If your are not expecting this answer, sorry.
def button_OutsideScript (self) :
import OutsideScript_Name as osScript
reload (osScript)
#If you have class inside the Outside Script use below line
osScript.className ()
Related
I am trying to tail a file and output it continuously to a QTextEdit box. However, I have my subprocess and output located within a multiprocess. Here is my code:
shouldRun = True
wMain = QtGui.QWidget()
textboxSideA = QtGui.QTextEdit(wMain)
def tailLog():
subA = subprocess.Popen(["tail", "-F", "<FILENAME>", stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid)
pollA = select.poll()
pollA.register(subA.stdout)
while shouldRun:
if pollA.poll(1):
textboxSideA.append(subA.stdout.readline())
subA.kill()
os.killpg(subA.pid, signal.SIGKILL)
return
processSideA = multiprocessing.Process(target = tailLog)
processSideA.start()
wMain.show()
when the textboxSideA.append is called, the textbox doesn't show anything. I have tried just appending a direct string to it just to make sure it wasn't my readline that was bad. However, that wasn't the issue. I then tried to print out my readline directly to the terminal using print(subA.stdout.readline()) which has worked fine. So I concluded that the QTextEdit textbox GUI isn't being updated. I have tried everything and not even Google has given me an answer.
Also, I can type in the textbox and that shows up properly, and I can save what I have typed. My GUI just doesn't seem to like the multiprocess as I can call .append() outside of the multiprocess and it works just fine.
Qt does not support multiprocessing so it is dangerous to update the GUI from another process, the GUI can only and should be updated from the thread of the process where it was created.
On the other hand in this case it is not necessary to use multiprocessing since you can use QProcess:
import sys
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.process = QtCore.QProcess(self)
self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
self.textedit = QtGui.QTextEdit()
self.setCentralWidget(self.textedit)
def tail(self, filename):
self.process.kill()
self.process.start("tail", ["-F", filename])
#QtCore.pyqtSlot()
def on_readyReadStandardOutput(self):
msg = self.process.readAllStandardOutput().data().encode()
self.textedit.append(msg)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.tail("<FILENAME>")
w.show()
sys.exit(app.exec_())
I have a PyQt5 GUI application and would like to execute an external program and display stdout, stderr and stdin of the external program on a QTextEdit widget. I have managed to do so for stdout and stderr. I need help for the stdin of the external process.
Imagine the following snippets
self.te = QTextEdit(self)
self.te.move(self.x0, 150)
self.te.resize(self.mainWinWidth - 100, self.mainWinHeight - 200)
And the snippet to get QProcess going ...
self.process = QtCore.QProcess(self)
self.process.setProcessChannelMode( QProcess.MergedChannels )
self.process.readyRead.connect(self.readReady)
# ... and elsewhere I start the sub process like
os.environ["PYTHONUNBUFFERED"] = "1"
self.process.start('./goo', [])
and readReady() implemented as:
def readReady(self):
cursor = self.te.textCursor()
cursor.movePosition(cursor.End)
cursor.insertText(str(self.process.readAll(), 'utf-8'))
self.te.ensureCursorVisible()
And goo(1) is a basic sub process implemented as
#!/usr/bin/python3
import time
import sys
for i in range(0,5):
print(f"============ {i} ===")
time.sleep(1)
sys.stderr.write("Testing stderr...\n")
print("Enter name:")
name = sys.stdin.readline()
print(f"Got {name}")
With that said, I do see stdout and stderr all working ok. I also see "Enter name:" but when I enter "joe" or "moe" on the QTextEdit, nothing happens, ie the back end sub process is still waiting.
So it seems like I need an event handler for the write. That is when the sub-process (via QProcess) waits for input on its stdin, I need to detect that and somehow read that from QTextEdit (from the user) and then feed that to the sub process (i.e write to its stdin).
Think ssh or telnet or xterm. Isn't there any on the shelf widget for this ?
You have to use the write() method of QProcess:
├── goo
└── main.py
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._textedit = QtWidgets.QTextEdit(readOnly=True)
self._lineedit = QtWidgets.QLineEdit()
self._pushbutton = QtWidgets.QPushButton("Send")
self._pushbutton.clicked.connect(self.on_clicked)
lay = QtWidgets.QGridLayout(self)
lay.addWidget(self._textedit, 0, 0, 1, 2)
lay.addWidget(self._lineedit, 1, 0)
lay.addWidget(self._pushbutton, 1, 1)
self._process = QtCore.QProcess(self)
self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self._process.readyRead.connect(self.on_readReady)
current_dir = os.path.dirname(os.path.realpath(__file__))
self._process.start(os.path.join(current_dir, "goo"))
#QtCore.pyqtSlot()
def on_readReady(self):
cursor = self._textedit.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(str(self._process.readAll(), "utf-8"))
self._textedit.ensureCursorVisible()
#QtCore.pyqtSlot()
def on_clicked(self):
text = self._lineedit.text() + "\n"
self._process.write(text.encode())
if __name__ == "__main__":
os.environ["PYTHONUNBUFFERED"] = "1"
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I've got a Python 2.7 application running with PyQt4 that has a QWebView in it, that has two way communication to and from Javascript.
The application is multithreaded via QThreadPool, QRunnables, so I'm communicating with a ViewController class with signals.
When I run the application, the QWebView loads my HTML with external JS and CSS just fine. I'm able to interact with the Javascript functions via the main program thread and ViewController class.
Once the user selects a directory and certain criteria are met, it starts looping through QRunnable tasks one at a time. During that time it calls back to the ViewController -> Javascript via Signal slots, just as expected. The problem is when I'm calling those ViewController methods that execute evaluateJavaScript, I get a Javascript error returned,
undefined line 1: SyntaxError: Parse error
I've done lots of trial error back and forth, but can't seem to figure out why evaluateJavaScript won't run in these instances. I've tried sending simple Javascript calls ranging from test functions that don't accept any arguments (thinking maybe it was some weird encoding issue), to just sending things like Application.main.evaluateJavaScript("alert('foo')"), which normally work outside of the threads. The only other thing I can think of is that maybe self.main.addToJavaScriptWindowObject('view', self.view) needs to be called in the threads again, but I've run a dir() on Application.main and it appears to have the evaluateJavaScript method attached to it already.
Any thoughts on why this could be occurring, when the scope seems to be correct, and the ViewController appears to be communicating just fine to the QWebView otherwise? Answers in Qt C++ will probably work as well, if you've seen this happen before!
I tried to simplify the code for example purposes:
# coding: utf8
import subprocess as sp
import os.path, os, sys, time, datetime
from os.path import basename
import glob
import random
import string
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.QtCore import QObject, pyqtSlot, QThreadPool, QRunnable, pyqtSignal
from PyQt4.QtGui import QApplication, QFileDialog
from PyQt4.QtWebKit import QWebView
from ImportController import *
class Browser(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(800,500)
self.centralwidget = QtGui.QWidget(self)
self.mainLayout = QtGui.QHBoxLayout(self.centralwidget)
self.mainLayout.setSpacing(0)
self.mainLayout.setMargin(0)
self.frame = QtGui.QFrame(self.centralwidget)
self.gridLayout = QtGui.QVBoxLayout(self.frame)
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0)
self.html = QtWebKit.QWebView()
# for javascript errors
errors = WebPage()
self.html.setPage(errors)
self.main = self.html.page().mainFrame()
self.gridLayout.addWidget(self.html)
self.mainLayout.addWidget(self.frame)
self.setCentralWidget(self.centralwidget)
path = os.getcwd()
if self.checkNetworkAvailability() and self.checkApiAvailbility():
self.default_url = "file://"+path+"/View/mainView.html"
else:
self.default_url = "file://"+path+"/View/errorView.html"
# load the html view
self.openView()
# controller class that sends and receives to/from javascript
self.view = ViewController()
self.main.addToJavaScriptWindowObject('view', self.view)
# on gui load finish
self.html.loadFinished.connect(self.on_loadFinished)
# to javascript
def selectDirectory(self):
# This evaluates the directory we've selected to make sure it fits the criteria, then parses the XML files
pass
def evaluateDirectory(self, directory):
if not directory:
return False
if os.path.isdir(directory):
return True
else:
return False
#QtCore.pyqtSlot()
def on_loadFinished(self):
# open directory select dialog
self.selectDirectory()
def openView(self):
self.html.load(QtCore.QUrl(self.default_url))
self.html.show()
def checkNetworkAvailability(self):
#TODO: make sure we can reach the outside world before trying anything else
return True
def checkApiAvailbility(self):
#TODO: make sure the API server is alive and responding
return True
class WebPage(QtWebKit.QWebPage):
def javaScriptConsoleMessage(self, msg, line, source):
print '%s line %d: %s' % (source, line, msg)
class ViewController(QObject):
def __init__(self, parent=None):
super(ViewController, self).__init__(parent)
#pyqtSlot()
def did_load(self):
print "View Loaded."
#pyqtSlot()
def selectDirectoryDialog(self):
# FROM JAVASCRIPT: in case they need to re-open the file dialog
Application.selectDirectory()
def prepareImportView(self, displayPath):
# TO JAVASCRIPT: XML directory parsed okay, so let's show the main
Application.main.evaluateJavaScript("prepareImportView('{0}');".format(displayPath))
def generalMessageToView(self, target, message):
# TO JAVASCRIPT: Send a general message to a specific widget target
Application.main.evaluateJavaScript("receiveMessageFromController('{0}', '{1}')".format(target, message))
#pyqtSlot()
def startProductImport(self):
# FROM JAVASCRIPT: Trigger the product import loop, QThreads
print "### view.startProductImport"
position = 1
count = len(Application.data.products)
importTasks = ProductImportQueue(Application.data.products)
importTasks.start()
#pyqtSlot(str)
def updateProductView(self, data):
# TO JAVASCRIPT: Send product information to view
print "### updateProductView "
Application.main.evaluateJavaScript('updateProductView("{0}");'.format(QtCore.QString(data)) )
class WorkerSignals(QObject):
''' Declares the signals that will be broadcast to their connected view methods '''
productResult = pyqtSignal(str)
class ProductImporterTask(QRunnable):
''' This is where the import process will be fired for each loop iteration '''
def __init__(self, product):
super(ProductImporterTask, self).__init__()
self.product = product
self.count = ""
self.position = ""
self.signals = WorkerSignals()
def run(self):
print "### ProductImporterTask worker {0}/{1}".format(self.position, self.count)
# Normally we'd create a dict here, but I'm trying to just send a string for testing purposes
self.signals.productResult.emit(data)
return
class ProductImportQueue(QObject):
''' The synchronous threadpool that is going to one by one run the import threads '''
def __init__(self, products):
super(ProductImportQueue, self).__init__()
self.products = products
self.pool = QThreadPool()
self.pool.setMaxThreadCount(1)
def process_result(self, product):
return
def start(self):
''' Call the product import worker from here, and format it in a predictable way '''
count = len(self.products)
position = 1
for product in self.products:
worker = ProductImporterTask("test")
worker.signals.productResult.connect(Application.view.updateProductView, QtCore.Qt.DirectConnection)
self.pool.start(worker)
position = position + 1
self.pool.waitForDone()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Application = Browser()
Application.raise_()
Application.show()
Application.activateWindow()
sys.exit(app.exec_())
You know, I love PyQt4 but after searching and searching, I believe this is actually a bug and not as designed.
I've since moved on and am trying to implement this in CEFPython with WxPython, which seems to have a much more elegant implementation for this specific purpose.
I am trying to offload a heavy background job to a multiprocessing process. I just want the separate process to be able to report it's progress to my GUI. Here's my last try, the GUI is simple, a couple of buttons and a progress bar:
from PySide.QtGui import *
from PySide.QtCore import *
import sys
from multiprocessing import Process, Pipe
import time
class WorkerClass:
#This class has the job to run
def worker(self, pipe):
for i in range(101):
pipe.send(i)
time.sleep(.02)
class WorkStarter(QThread):
#this thread takes a widget and updates it using progress sent from
#process via Pipe
def __init__(self, progressBar):
super().__init__()
self.progress_bar = progressBar
def run(self):
worker_obj = WorkerClass()
myend, worker_end = Pipe(False)
self.p = Process(target=worker_obj.worker, args=(worker_end,))
self.p.start()
while True:
val = myend.recv()
self.progress_bar.setValue(val)
if val == 100:
break
class WorkingWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Blue collar widget')
layout = QHBoxLayout()
start_btn = QPushButton('Start working')
start_btn.clicked.connect(self.startWorking)
end_btn = QPushButton('End working')
end_btn.clicked.connect(self.endWorking)
layout.addWidget(start_btn)
layout.addWidget(end_btn)
self.progress_bar = QProgressBar()
layout.addWidget(self.progress_bar)
self.setLayout(layout)
def startWorking(self):
self.thread = WorkStarter(self.progress_bar)
self.thread.start()
def endWorking(self):
self.thread.terminate()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = WorkingWidget()
main.show()
sys.exit(app.exec_())
I cannot pass any QObject as an argument to the process, since that is not pickleable:
#cannot do the following
...
def startWorking(self):
self.worker_obj = WorkerClass()
#pass the progress bar to the process and the process updates the bar
self.p = Process(target=self.worker_obj.worker, args=(self.progress_bar,))
The problem is that this gui some times works, other times it freezes (So please press 'start' multiple times until it freezes :) ), and here on Windows it says : pythonw.exe has stopped working...
Any clue what's the reason for that?. I cannot figure it out by myself. Thanks
You are not supposed to create the object inside "run" method of QThread, emit signal from "run", implement a function say "callerFunction" create object in this function and finally call this function on signal which is emitted by the "run" function.
You can emit the signal in the while loop that you have already created.
Have a look at this solution
don't create a python process, QThread is sufficient for this job
I have this application that controls a deamon of mine. It basically checks if the deamon is already running, if so it gives the option to close, if not it gives the option to close, also it offers you a log of it, and everything is on a menu that opens from a QSystemTrayIcon. When I run it it works perfectly fine, but when I set it to run automatically after i log in it runs, but the trayicon doesn't show, you can even see the process with "ps aux". I'm running on Ubuntu 12.04.
#!/usr/bin/env python
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from subprocess import call
from subprocess import Popen
import os
import time
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, parent=None):
QtGui.QSystemTrayIcon.__init__(self, parent)
self.setIcon(QtGui.QIcon("./mail_open_process.png"))
global closeAction
global openAction
global startOnLaunch
self.menu = QtGui.QMenu(parent)
openAction = self.menu.addAction("Open")
closeAction = self.menu.addAction("Close")
logAction = self.menu.addAction("Log")
startOnLaunch = self.menu.addAction("Start on Launch")
startOnLaunch.setCheckable(True)
exitAction = self.menu.addAction("Exit")
self.setContextMenu(self.menu)
self.connect(openAction,QtCore.SIGNAL('triggered()'),self.openDaemon)
self.connect(closeAction,QtCore.SIGNAL('triggered()'),self.closeDaemon)
self.connect(logAction,QtCore.SIGNAL('triggered()'),self.openLog)
self.connect(exitAction,QtCore.SIGNAL('triggered()'),self.Exit)
#self.connect(startOnLaunch,QtCore.SIGNAL('triggered()'),self.startLaunch)
if os.path.exists("/dev/repa"):
closeAction.setVisible(True)
openAction.setVisible(False)
else:
closeAction.setVisible(False)
openAction.setVisible(True)
def Exit(self):
os.remove("/tmp/daemonMenu.pid")
sys.exit()
def openDaemon(self):
call(["gksu", os.path.expandvars("$HOME")+"/repad/apps/repad -f"])
time.sleep(1)
if os.path.exists("/dev/repa"):
closeAction.setVisible(True)
openAction.setVisible(False)
def closeDaemon(self):
call(["gksu", os.path.expandvars("$HOME")+"/repad/apps/repad -c"])
time.sleep(1)
if not os.path.exists("/dev/repa"):
closeAction.setVisible(False)
openAction.setVisible(True)
def openLog(self):
Popen(["gedit", "/dev/repad.log"])
#def startLaunch(self):
# Dir = "/etc/init.d/S99scriptDaemon"
# if startOnLaunch.isChecked():
# call(["gksu","cp ./S99scriptDaemon /etc/rc5.d"])
#if not startOnLaunch.isChecked():
# call (["gksu","rm /etc/rc5.d/S99scriptDaemon"])
def main():
pid = str(os.getpid())
pidDir = "/tmp/daemonMenu.pid"
if os.path.isfile(pidDir):
pidFile = open(pidDir,"r")
pidLine = pidFile.readline()
call(["kill", "%s" %(pidLine)])
os.remove(pidDir)
file(pidDir, 'w+').write(pid)
if name == 'main':
main()
app = QtGui.QApplication(sys.argv)
trayIcon = SystemTrayIcon()
trayIcon.show()
sys.exit(app.exec_())
I've got the same issue. I made script start.sh and at startup system starts this script. In script I added 10 second delay before starting application. This script seems like this:
sleep 10
./main.py