IPython iterate main loop manually? - python

Can i somehow instance ipython (or even better, ipython-qtconsole) and step trough its's (IPython's) main loop manually?
I want to edit panda3d programs on the fly.
EDIT1:
Here is code sample which should clarify a bit what i want to do.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from direct.showbase.ShowBase import ShowBase
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.qtApp = QApplication(sys.argv)
label = QLabel("Hello World")
label.show()
self.m = loader.loadModel("frowney")
self.m.reparentTo(render)
while 1:
self.qtApp.processEvents() #manual step trough Qt loop
taskMgr.step() #manual step trough Panda3D loop
app = MyApp()
So you can see how i can manually step trough panda and qt, i want to do same with ipython if its possible.
ANSWER
Complete file:
from direct.showbase.ShowBase import ShowBase
from IPython.lib import inputhook
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.m = loader.loadModel("frowney")
self.m.reparentTo(render)
def stepMe(self):
taskMgr.step() #manual step trough Panda3D loop
return 0
if __name__ == "__main__":
app = MyApp()
inputhook.set_inputhook(app.stepMe)
In your cmd line, just go to directory where file is and do
ipython
run file.py
app.m.setPos(1,1,1)

By "edit panda3d programs on the fly", do you just mean changing things in order to test your running program? Or actually making persistent edits to your program's structure in the interactive environment?
Simply stepping over your loop in an interactive python session is quite easy. You can just replace while 1: with a method declaration such as def step(self):, then call it for each step.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from direct.showbase.ShowBase import ShowBase
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.qtApp = QApplication(sys.argv)
label = QLabel("Hello World")
label.show()
self.m = loader.loadModel("frowney")
self.m.reparentTo(render)
def step(self):
self.qtApp.processEvents() #manual step trough Qt loop
taskMgr.step() #manual step trough Panda3D loop
return 0 #PyOS_InputHook expects an integer
if __name__ == "__main__":
app = MyApp()
while 1: app.step()
With if __name__ == "__main__" for the main loop, your file will still work as it should when run standalone. But now you can import it into an interactive session and modify things in between steps.
>>> import myfile
>>> app = myfile.MyApp()
>>> app.step()
>>> app.something = something_else
>>> app.step()
Now to add it to IPython's event loop, so it will be run as you use the interpreter, you can use IPython.lib.inputhook.set_inputhook() (new in IPython 0.11).
>>> from IPython.lib import inputhook
>>> inputhook.set_inputhook(app.step)
This should cause your program to run while the interpreter is idle, but still allow manipulation as usual.

Run the script in the ipython debugger using
%run -d -b40 myscript
The -b40 parameter sets a breakpoint in the script on line 40 (which should be the first line of the loop you want to debug). Type c at the prompt to start the script which will stop at the breakpoint. Type h to get help on the debugger.

Related

How to attach and detach an external app with PyQT5 or dock an external application?

I'm developing an GUI for multi-robot system using ROS, but i'm freezing in the last thing i want in my interface: embedding the RVIZ, GMAPPING or another screen in my application. I already put an terminal in the interface, but i can't get around of how to add an external application window to my app. I know that PyQt5 have the createWindowContainer, with uses the window ID to dock an external application, but i didn't find any example to help me with that.
If possible, i would like to drag and drop an external window inside of a tabbed frame in my application. But, if this is not possible or is too hard, i'm good with only opening the window inside a tabbed frame after the click of a button.
I already tried to open the window similar to the terminal approach (see the code bellow), but the RVIZ window opens outside of my app.
Already tried to translate the attaching/detaching code code to linux using the wmctrl command, but didn't work wither. See my code here.
Also already tried the rviz Python Tutorial but i'm receveing the error:
Traceback (most recent call last):
File "rvizTutorial.py", line 23, in
import rviz
File "/opt/ros/indigo/lib/python2.7/dist-packages/rviz/init.py", line 19, in
import librviz_shiboken
ImportError: No module named librviz_shiboken
# Frame where i want to open the external Window embedded
self.Simulation = QtWidgets.QTabWidget(self.Base)
self.Simulation.setGeometry(QtCore.QRect(121, 95, 940, 367))
self.Simulation.setTabPosition(QtWidgets.QTabWidget.North)
self.Simulation.setObjectName("Simulation")
self.SimulationFrame = QtWidgets.QWidget()
self.SimulationFrame.setObjectName("SimulationFrame")
self.Simulation.addTab(rviz(), "rViz")
# Simulation Approach like Terminal
class rviz(QtWidgets.QWidget):
def __init__(self, parent=None):
super(rviz, self).__init__(parent)
self.process = QtCore.QProcess(self)
self.rvizProcess = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.rvizProcess)
# Works also with urxvt:
self.process.start('rViz', [str(int(self.winId()))])
self.setGeometry(121, 95, 940, 367)
I've not tested this specifically, as I've an old version of Qt5 I can't upgrade right now, while from Qt5 5.10 startDetached also returns the pid along with the bool result from the started process.
In my tests I manually set the procId (through a static QInputBox.getInt()) before starting the while cycle that waits for the window to be created.
Obviously there are other ways to do this (and to get the xid of the window).
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck, Gdk
class Container(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.embed('xterm')
def embed(self, command, *args):
proc = QtCore.QProcess()
proc.setProgram(command)
proc.setArguments(args)
started, procId = proc.startDetached()
if not started:
QtWidgets.QMessageBox.critical(self, 'Command "{}" not started!')
return
attempts = 0
while attempts < 10:
screen = Wnck.Screen.get_default()
screen.force_update()
# this is required to ensure that newly mapped window get listed.
while Gdk.events_pending():
Gdk.event_get()
for w in screen.get_windows():
if w.get_pid() == procId:
window = QtGui.QWindow.fromWinId(w.get_xid())
container = QtWidgets.QWidget.createWindowContainer(window, self)
self.addTab(container, command)
return
attempts += 1
QtWidgets.QMessageBox.critical(self, 'Window not found', 'Process started but window not found')
app = QtWidgets.QApplication(sys.argv)
w = Container()
w.show()
sys.exit(app.exec_())
I couldn't get the code in the accepted answer to work on Ubuntu 18.04.3 LTS; even when I got rid of the exceptions preventing the code to run, I'd still get a separate PyQt5 window, and separate xterm window.
Finally after some tries, I got the xterm window to open inside the tab; here is my code working in Ubuntu 18.04.3 LTS (with all the misses commented):
#!/usr/bin/env python3
# (same code seems to run both with python3 and python2 with PyQt5 in Ubuntu 18.04.3 LTS)
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck, Gdk
import time
class Container(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.embed('xterm')
def embed(self, command, *args):
proc = QtCore.QProcess()
proc.setProgram(command)
proc.setArguments(args)
#started, procId = proc.startDetached()
#pid = None
#started = proc.startDetached(pid)
# https://stackoverflow.com/q/31519215 : "overload" startDetached : give three arguments, get a tuple(boolean,PID)
# NB: we will get a failure `xterm: No absolute path found for shell: .` even if we give it an empty string as second argument; must be a proper abs path to a shell
started, procId = proc.startDetached(command, ["/bin/bash"], ".")
if not started:
QtWidgets.QMessageBox.critical(self, 'Command "{}" not started!'.format(command), "Eh")
return
attempts = 0
while attempts < 10:
screen = Wnck.Screen.get_default()
screen.force_update()
# do a bit of sleep, else window is not really found
time.sleep(0.1)
# this is required to ensure that newly mapped window get listed.
while Gdk.events_pending():
Gdk.event_get()
for w in screen.get_windows():
print(attempts, w.get_pid(), procId, w.get_pid() == procId)
if w.get_pid() == procId:
self.window = QtGui.QWindow.fromWinId(w.get_xid())
#container = QtWidgets.QWidget.createWindowContainer(window, self)
proc.setParent(self)
#self.scrollarea = QtWidgets.QScrollArea()
#self.container = QtWidgets.QWidget.createWindowContainer(self.window)
# via https://vimsky.com/zh-tw/examples/detail/python-method-PyQt5.QtCore.QProcess.html
#pid = proc.pid()
#win32w = QtGui.QWindow.fromWinId(pid) # nope, broken window
win32w = QtGui.QWindow.fromWinId(w.get_xid()) # this finally works
win32w.setFlags(QtCore.Qt.FramelessWindowHint)
widg = QtWidgets.QWidget.createWindowContainer(win32w)
#self.container.layout = QtWidgets.QVBoxLayout(self)
#self.addTab(self.container, command)
self.addTab(widg, command)
#self.scrollarea.setWidget(self.container)
#self.container.setParent(self.scrollarea)
#self.scrollarea.setWidgetResizable(True)
#self.scrollarea.setFixedHeight(400)
#self.addTab(self.scrollarea, command)
self.resize(500, 400) # set initial size of window
return
attempts += 1
QtWidgets.QMessageBox.critical(self, 'Window not found', 'Process started but window not found')
app = QtWidgets.QApplication(sys.argv)
w = Container()
w.show()
sys.exit(app.exec_())

How to run FORTRAN code with in a PYQT GUI application in parallel?

I am creating a small GUI where user clicks a push button so the numerical simulation code in FORTRAN starts and its execution is shown with busy indicator movement (front and back).
With pool.apply_async method I could able to run the FORTRAN code and busy indicator successfully. But I have a problem when I stop the process and restart it. When I restart the process, it is not running and throwing an assertion error. (assert self._state == RUN)
Note: I am not supposed to disturb the FORTRAN code at any moment. Because it is a fixed one written for some numerical simulation. Its execution takes more than 30 mins
Below is my code:
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4 import uic
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import FortranSimulation
import time
qtCreatorFile = "you_atmpt.ui" # Loading UI file
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
def pass_args(args):
b = 10.0
print b
b = b + 2
FortranSimulation.fortrancode(b)
return b
class MainClass(QtGui.QMainWindow,Ui_MainWindow): # MAin GUI class
def __init__(self):
super(MainClass,self).__init__()
self.setupUi(self) # 2 PB
self.StartCalculaltion_button.clicked.connect(self.Fortran_Calc)
self.Stop_button.clicked.connect(self.Stop_All)
self.pool = mp.Pool(None)
def Fortran_Calc(self):# Initiation of other process for 'Fortran Calculation'
self.label_2.setText('Calculation is in progress!')
self.progressBar.setRange(0,0)
index = 1
surface_id = 2
self.pool.apply_async(pass_args, [(surface_id, index)],
callback=self.callback)
def callback(self,b):
print b
self.label.setText('Calculation is completed!')
def Stop_All(self): # Termination of process
self.progressBar.setRange(0,1)
self.pool.close()
self.pool.terminate()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainClass()
window.show()
app.exec_()
I added the error message also:
I tried with self.pool.close(), self.pool.terminate() these commands are stopping the process. At the same time I can't restart the process again with the start button.
And also I would like to know whether restarting a process is possible or not?
Thanks in advance.

Loading python module with GUI

I have a organization setup question I need help with. Here is my current folder structure.
What i want to do is run the Main UI with the specified AppCommands module that contains functions. Based on which application i want to run the tool. Is there a way using another python file, where i can load the gui and the associated app commands moduel? So when users click the button it calls the corrects app command.
So say for example I create a python file like this pseudo code
main execute py file for Photoshop
Import photoshop.appcommands as cmds
Import GUI
Gui(cmds)
How do I then tell my main GUI tool to load the photoshop modules 'AppCommands' when it runs?
app #1 code:
def runTool():
msg = 'This is Notepad'
print msg
app #2 code:
def runTool():
msg = 'This is Photoshop'
print msg
Main ui code:
import sys
import os
from PySide import QtGui, QtCore
import AppCommands as cmds
class MainWindow(QtGui.QMainWindow):
def __init__(self,parent=None):
super(MainWindow, self).__init__(parent)
self.uiButton = QtGui.QPushButton('Button', self)
# layout
grid = QtGui.QGridLayout()
grid.addWidget(self.uiButton, 3, 1)
main_widget = QtGui.QWidget()
main_widget.setLayout(grid)
self.setCentralWidget(main_widget)
self.uiButton.clicked.connect(self.browse_clicked)
# actions
def browse_clicked(self):
print 'Execute Command'
cmds.runTool()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
Well, if I understood correctly, you need to choose the right module to load a function after then you started the program with some variable args?
In this case you may import both and try globals:
import notepad.AppCommands
import photoshop.AppCommands
...
# in your app init code:
x = 'photoshop' # an arg passed from config/CLI or from somewhere else
actual_module = globals()[x]
actual_module.AppComands.runTools()
++ But if you just don't know how to run a certain function from a certain module, follow #eyllanesc's answer.

How do I include scripts in a qt application?

I am trying to use four diferents python scripts in a GUI application.
Every script has around 500 code lines. Then I donĀ“t like to include the every full scrip as a function.
This is the skeleton of the application:
from FullConversor import * #this is the .py gui
import sys
import datetime
import os
import pandas as pd
import shapefile as shp
import csv
import tkinter.filedialog
class FullConversorGUI(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.pushButtonConvert, QtCore.SIGNAL ('clicked()') ,self.conversor)
QtCore.QObject.connect(self.ui.pushButtonClose, QtCore.SIGNAL ('clicked()') ,self.close)
def conversor(self):
if self.ui.radioButton1.isChecked()== True:
pass
if self.ui.radioButton2.isChecked()== True:
pass
if self.ui.radioButton3.isChecked()== True:
pass
if self.ui.radioButton4.isChecked()== True:
pass
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
myapp = FullConversorGUI()
myapp.show()
sys.exit(app.exec_())
Every radioButton must launch a python script, just a .py file who runs fine alone.
How do I do that?
Call import on your script, and run the main function of it when you need.
Pretend you have a script called myscript.py
# this is myscript.py
def main():
# this function will be called from your gui. all your logic should be here.
print "my script"
if __name__ == '__main__':
# this function is called when you invoke the script from the command line.
main()
Then in your gui....
import myscript
if self.ui.radioButton1.isChecked()== True:
myscript.main()
# continue after the script runs
This would result in the following output to stdout:
my script
Obviously, you wouldn't have just a print statement in myscript.py. You can have it run whatever logic you would like.

PyQt - is it possible to run two applications?

Two files. Each runs new window and works by itself. I need to run them both.
When I run first.pyw, only one (second) window is shown.
Is it possible two run them both?
first.pyw:
import sys
from PyQt4.QtGui import *
import second
class first(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setWindowTitle('first')
app = QApplication(sys.argv)
firstApp = first()
firstApp.show()
sys.exit(app.exec_())
second.pyw:
import sys
from PyQt4.QtGui import *
class second(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setWindowTitle('second')
app2 = QApplication(sys.argv)
secondApp = second()
secondApp.show()
sys.exit(app2.exec_())
How can I run two applications that are in different modules?
The accepted answer is essentially right, but there are cases where you want to run multiple QApplications one after the other, e.g. :
Unit tests
A command-line tool that shouldn't require a running X server (hence no QApplication on startup), but can optionally show a window if the user's system supports it
I ended up using the multiprocessing module to start each QApplication in a separate process, so that each one is independent from the others.
from multiprocessing import Queue, Process
class MyApp(Process):
def __init__(self):
self.queue = Queue(1)
super(MyApp, self).__init__()
def run(self):
app = QApplication([])
...
self.queue.put(return_value)
app1 = MyApp()
app1.start()
app1.join()
print("App 1 returned: " + app1.queue.get())
app2 = MyApp()
app2.start()
app2.join()
print("App 2 returned: " + app1.queue.get())
You can only run a single application at a time, although your application can have multiple top-level windows. The QCoreApplication docs say that:
...there should be exactly one QCoreApplication object.
This also holds true for QApplication as it derives from QCoreApplication. You can get access to that application through the QCoreApplication.instance() method or the qApp macro in C++.
What do you expect to get out of having two different applications running? Instead, you could have each module provide a top-level window that then gets displayed by the application launcher.
You import second. Hence it is interpreted before you even reach the definition of class first. As the last line of second.pyw is sys.exit, nothing behind it can be executed.

Categories