How to close 1st window after opening 2nd window in pygtk - python

In my pygtk app, I want to close current window after opening next window.
this is the code which i have written
#!/usr/bin/env python
# example base.py
import pygtk
pygtk.require('2.0')
import gtk
import subprocess
class Base:
def next(self,widget):
subprocess.call('fabfile.py', shell=True)
self.window.destroy()
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(200,200)
self.button = gtk.Button("Hello World")
self.button.show()
self.button.connect("clicked", self.next)
self.window.add(self.button)
self.window.show()
def main(self):
gtk.main()
print __name__
if __name__ == "__main__":
base = Base()
base.main()
when I click on next button it opens next window but it does not close my current window in background and current window go to hang in background after opening next window.
def next(self,widget):
subprocess.call("scan.py", shell=True)
self.win.destroy()
when i run this code in window machine it is not closing existing window and when i run in Linux machine it is giving this error.
/bin/sh: fabfile.py: command not found
Anyone let me know how to do this.
Thanks...

Try this, I hope it will work,
import subprocess
proc=subprocess.Popen(["fabfile.py"], bufsize=2048, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
proc.wait()
self.win.destroy()
or
import subprocess
proc=subprocess.Popen(["fabfile.py"], bufsize=2048, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
completed = False
while not completed:
if process.poll() is not None:
completed = True
print "communicating"
process.communicate("k")
print "communicated"
subprocess.Popen will open your 2nd window and close after current window.
Update
For linux you have to write below code when you will use subprocess call
The script contains CR characters. The shell interprets these CR characters as arguments.
Solution: Remove the CR characters from the script using the following script.
with open('beak', 'rb+') as f:
content = f.read()
f.seek(0)
f.write(content.replace(b'\r', b''))
f.truncate()
for more clarification you can see this recorded link

http://www.python.org/doc//current/library/subprocess.html#convenience-functions
Run command with arguments. Wait for command to complete, then return
the returncode attribute.
The call to subprocess.call is blocking, win.destroy nor any GTK function will be executed until your scan.py app has exited. In C, you'd use g_spawn_async from GLib, you should be able to figure out the Python equivalent. Or use a thread.

Related

QProcess CLI Command - 'Bad parameter' when specifying output save location

I am using QProcess to run a typical cli command such as 'ping' or 'netstat'. I would like it to run continuously until I tell it to stop. I've provided a cut down version of my code below that uses the "ping" command. If I run the command "ping -t 192.168.0.1 > test.txt" from a cmd prompt it works fine, but when I try to run this in my program below, it produces the error "Bad parameter > test.txt."
It appears that saving the output of cli commands does not count as a recognized argument/parameter for QProcess (the rest of the code works fine as far as I can tell).
from PyQt5 import QtCore, QtGui, QtWidgets
import os
import psutil
## Define origin path.
scriptpath = os.path.realpath(__file__)
scriptpath = scriptpath.replace(os.path.basename(__file__), "")
os.chdir(scriptpath)
origin = os.getcwd()
## Establish UI and interactions.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# event actions
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
process_btn = QtWidgets.QPushButton("process")
process_btn.clicked.connect(self.process)
end_btn = QtWidgets.QPushButton("end")
end_btn.clicked.connect(self.end)
lay.addWidget(process_btn)
lay.addWidget(end_btn)
self.process = QtCore.QProcess(self)
self._pid = -1
#QtCore.pyqtSlot()
def process(self):
program = 'ping'
arguments = ['-t', '192.168.0.1', '> test.txt'] # Ping 10 times to the router address and save output.
self.process.setProgram(program)
self.process.setArguments(arguments)
ok, pid = self.process.startDetached()
if ok:
self._pid = pid
#QtCore.pyqtSlot()
def end(self):
if self._pid > 0:
p = psutil.Process(self._pid)
p.terminate()
self._pid = -1
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Is there a way to save the output of cli commands run through QProcess to a text file?
Note: I have tried to implement the same feature with subprocess.call('ping -t 192.168.0.1 > test.txt', shell=True) but then I ran into issues with being able to stop the ping. The only way I could stop it was to use the exit command in the cli from where my PyQt5 program was run (closing the app just left the text file continuing to update), which isn't ideal for a program with a GUI. If anyone has a solution to this then perhaps I could go back to it.
Use QProcess setStandardOutputFile method to save process's result into a file.

Can't Update QTextEdit While in a Multiprocess

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_())

Runing a "child" python file as regular user when main python file needs to be run as root

I'm doing a python program (and using PyQt5 for GUI) that needs to be run as root (because I'm programming sockets on it). It has a button,when I click on it, it opens another python file (the "child" file: chrometest.py, it's based on this library, eel: https://www.youtube.com/watch?v=2kbeBzEQfXE, it lets me open a js file). The problem is that eel won't work when it's run as root, so I don't know how I could switch users to run only this function as regular user.
Main python program (sample, the one runing as root)
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Window(QtWidgets.QMainWindow):
def __init__(self, *args):
super(Window, self).__init__()
self.img = QtWidgets.QLabel()
self.open_js= QtWidgets.QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
self.open_js.clicked.connect(self.openjs)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
h_layout = QtWidgets.QHBoxLayout(central_widget)
h_layout.addWidget(self.img)
h_layout.addWidget(self.open_js)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
def openjs(self):
#here is where I think I need to switch to regular user
exec(open("chrometest.py").read())
def main():
app = QtWidgets.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
chrometest.py (program that needs to be run as regular user)
import eel
eel.init('webfiles')
eel.start('index.html')
I tried to use this tutorial https://www.tutorialspoint.com/python/os_chown.htm in the line commented but it didn't work
It is common in Unix-like systems to have a process that needs to be root at a time, for example to listen on ports below 1024, but then executes as a regular non priviledges user for security reasons. Whatever the language the design is as follow:
priviledged part (extensively tested for security flaws) executes only code requiring root privileges
as soon as code that does not require root has to be executed it forks and
parent remains priviledged and continue to run priviledged code
child switch to a regular user (allowed because it is still at priviledged-root level) and executes normal code
That is what you should do here:
def openjs(self):
pid = os.fork()
if 0 == pid:
os.setuid(uid_of_non_priviledged_user)
# you can now safely execute code from chrometest.
else:
# optionaly wait for child:
os.waitpid(pid, os.WEXITED)
oh yes!!.. you can able to run a child python file as a regular python file.but u need to add this line in your parent python file.
import os
os.system("python chrometest.py")
by this line your parent python file can run your child python file.
Like Mani Kandan said, you can run process by os.system, but if you already working under root, it will be executed with same privileges.
You can run program under specified user using sudo -u or sudo --user argument like
sudo -u regular_user "python3 chrometest.py"
So, in Python it will look like:
import os
os.system('sudo -u regular_user python3 chrometest.py')
and about your TabError: inconsistent use of tabs and spaces in indentation error: change all tabs in your script to spaces or vice versa.

Activating and Disabling button after process in python and pyGTK

Essentially, I am trying to make a button "active" first, run a process, and then after that process has finished running, disable the button again.
Using pyGTK and Python, the code in question looks like this...
self.MEDIA_PLAYER_STOP_BUTTON.set_sensitive(True) #Set button to be "active"
playProcess = Popen("aplay " + str(pathToWAV) + " >/dev/null 2>&1",shell=True) #Run Process
playProcess.wait() #Wait for process to complete
self.MEDIA_PLAYER_STOP_BUTTON.set_sensitive(False) #After process is complete, disable the button again
However, this does not work at all.
Any help would be greatly appreciated.
All is working normally (python 2.7.3). But if you call playProcess.wait() in gui thread - you freeze gui thread without redrawing (sorry, my english isn`t very well). And are you sure that you try to use subprocess.popen()? Maybe os.popen()?
My small test:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygtk, gtk, gtk.glade
import subprocess
def aplay_func(btn):
btn.set_sensitive(True)
print "init"
playProcess = subprocess.Popen("aplay tara.wav>/dev/null 2>&1", shell=True)
print "aaa"
playProcess.wait()
print "bbb"
btn.set_sensitive(False)
wTree = gtk.glade.XML("localize.glade")
window = wTree.get_widget("window1")
btn1 = wTree.get_widget("button1")
window.connect("delete_event", lambda wid, we: gtk.main_quit())
btn1.connect("clicked", aplay_func)
window.show_all()
gtk.main()
Result:
init
aaa
bbb
And yes, button is working correctly. Sound too.

Redirect command line results to a tkinter GUI

I have created a program that prints results on command line.
(It is server and it prints log on command line.)
Now, I want to see the same result to GUI .
How can I redirect command line results to GUI?
Please, suggest a trick to easily transform console application to simple GUI.
Note that it should work on Linux and Windows.
You could create a script wrapper that runs your command line program as a sub process, then add the output to something like a text widget.
from tkinter import *
import subprocess as sub
p = sub.Popen('./script',stdout=sub.PIPE,stderr=sub.PIPE)
output, errors = p.communicate()
root = Tk()
text = Text(root)
text.pack()
text.insert(END, output)
root.mainloop()
where script is your program. You can obviously print the errors in a different colour, or something like that.
To display subprocess' output in a GUI while it is still running, a portable stdlib-only solution that works on both Python 2 and 3 has to use a background thread:
#!/usr/bin/python
"""
- read output from a subprocess in a background thread
- show the output in the GUI
"""
import sys
from itertools import islice
from subprocess import Popen, PIPE
from textwrap import dedent
from threading import Thread
try:
import Tkinter as tk
from Queue import Queue, Empty
except ImportError:
import tkinter as tk # Python 3
from queue import Queue, Empty # Python 3
def iter_except(function, exception):
"""Works like builtin 2-argument `iter()`, but stops on `exception`."""
try:
while True:
yield function()
except exception:
return
class DisplaySubprocessOutputDemo:
def __init__(self, root):
self.root = root
# start dummy subprocess to generate some output
self.process = Popen([sys.executable, "-u", "-c", dedent("""
import itertools, time
for i in itertools.count():
print("%d.%d" % divmod(i, 10))
time.sleep(0.1)
""")], stdout=PIPE)
# launch thread to read the subprocess output
# (put the subprocess output into the queue in a background thread,
# get output from the queue in the GUI thread.
# Output chain: process.readline -> queue -> label)
q = Queue(maxsize=1024) # limit output buffering (may stall subprocess)
t = Thread(target=self.reader_thread, args=[q])
t.daemon = True # close pipe if GUI process exits
t.start()
# show subprocess' stdout in GUI
self.label = tk.Label(root, text=" ", font=(None, 200))
self.label.pack(ipadx=4, padx=4, ipady=4, pady=4, fill='both')
self.update(q) # start update loop
def reader_thread(self, q):
"""Read subprocess output and put it into the queue."""
try:
with self.process.stdout as pipe:
for line in iter(pipe.readline, b''):
q.put(line)
finally:
q.put(None)
def update(self, q):
"""Update GUI with items from the queue."""
for line in iter_except(q.get_nowait, Empty): # display all content
if line is None:
self.quit()
return
else:
self.label['text'] = line # update GUI
break # display no more than one line per 40 milliseconds
self.root.after(40, self.update, q) # schedule next update
def quit(self):
self.process.kill() # exit subprocess if GUI is closed (zombie!)
self.root.destroy()
root = tk.Tk()
app = DisplaySubprocessOutputDemo(root)
root.protocol("WM_DELETE_WINDOW", app.quit)
# center window
root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id()))
root.mainloop()
The essence of the solution is:
put the subprocess output into the queue in a background thread
get the output from the queue in the GUI thread.
i.e., call process.readline() in the background thread -> queue -> update GUI label in the main thread. Related kill-process.py (no polling -- a less portable solution that uses event_generate in a background thread).
Redirecting stdout to a write() method that updates your gui is one way to go, and probably the quickest - although running a subprocess is probably a more elegant solution.
Only redirect stderr once you're really confident it's up and working, though!
Example implimentation (gui file and test script):
test_gui.py:
from Tkinter import *
import sys
sys.path.append("/path/to/script/file/directory/")
class App(Frame):
def run_script(self):
sys.stdout = self
## sys.stderr = self
try:
del(sys.modules["test_script"])
except:
## Yeah, it's a real ugly solution...
pass
import test_script
test_script.HelloWorld()
sys.stdout = sys.__stdout__
## sys.stderr = __stderr__
def build_widgets(self):
self.text1 = Text(self)
self.text1.pack(side=TOP)
self.button = Button(self)
self.button["text"] = "Trigger script"
self.button["command"] = self.run_script
self.button.pack(side=TOP)
def write(self, txt):
self.text1.insert(INSERT, txt)
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.build_widgets()
root = Tk()
app = App(master = root)
app.mainloop()
test_script.py:
print "Hello world!"
def HelloWorld():
print "HelloWorldFromDef!"
Sorry for my bad English. I actually, used a different way to print Command Prompt output into my new Automation tool.
Please find those steps below.
1> Create a Bat File & redirect its output to a LOG file.
Command Prompt command: tasklist /svc
2> Make read that file with Python 3.x.
`processedFile = open('D:\LOG\taskLog.txt', 'r')
3> The Finale step.
ttk.Label(Tab4, text=[ProcessFile.read()]).place(x=0, y=27)
**Hence please be informed that, I have not include scrollbar into this code yet.
Posting Screenshot:

Categories