How to close QMessageBox and its parent dialog in python - python-2.7

How can i make to close the pop-up Dialog and its QMessageBox.information when the ok button is pressed
I got this code from here
i am using it as pop-up dialogbox in my module. The dialog box opens another QMessageBox.information with its standard ok button
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import serial
from serial.serialutil import SerialException
from serialutils import full_port_name, enumerate_serial_ports
class ListPortsDialog(QDialog):
def __init__(self, parent=None):
super(ListPortsDialog, self).__init__(parent)
self.setWindowTitle('List of serial ports')
self.ports_list = QListWidget()
self.tryopen_button = QPushButton('Try to open')
self.connect(self.tryopen_button, SIGNAL('clicked()'),
self.on_tryopen)
layout = QVBoxLayout()
layout.addWidget(self.ports_list)
layout.addWidget(self.tryopen_button)
self.setLayout(layout)
self.fill_ports_list()
def on_tryopen(self):
cur_item = self.ports_list.currentItem()
if cur_item is not None:
fullname = full_port_name(str(cur_item.text()))
try:
ser = serial.Serial(fullname, 38400)
ser.close()
QMessageBox.information(self, 'Success',
'Opened %s successfully' % cur_item.text())
except SerialException, e:
QMessageBox.critical(self, 'Failure',
'Failed to open %s:\n%s' % (
cur_item.text(), e))
def fill_ports_list(self):
for portname in enumerate_serial_ports():
self.ports_list.addItem(portname)
if __name__ == "__main__":
app = QApplication(sys.argv)
form = ListPortsDialog()
form.show()
app.exec_()
i Wanted to close both window 3 and window 2 when the ok button is pressed

Since it is using QDialog after QMessageBox.information(self, 'Success', 'Opened %s successfully' % cur_item.text()) , you can simply say self.accept(). That should close 2 and 3 windows.

Related

Redirect stdout from multiprocessing to Tkinter text widget

I'm a little stuck here. I've read lots of stack overflows threads but not getting any further on that topic.
My goal is to have a tinter GUI that at some point launches a function in a new Process and redirects every print in that function to the Guis Text widget. There a Pipes and Queues but im not to familiar with how to use them correctly. I have found a working solution here but that only applies to Python 3. Unfortunately I have to use Python 2.7...
Can anybody help?
my sample code:
from Tkinter import *
import multiprocessing as mp
import time
import sys
class Gui(object):
def __init__(self):
self.a=Tk()
b1=Button(self.a, text="Process 1", command=self.func)
b1.grid(row=0, column=0, pady=10, padx=10, sticky=SE)
self.messages=Text(
self.a, height=2.5, width=30, bg="light cyan", state=NORMAL)
self.messages.grid(row=1, column=0, columnspan=3)
sys.stdout = self.StdoutRedirector(self.messages)
sys.stderr = self.StdoutRedirector(self.messages)
self.a.mainloop()
class StdoutRedirector(object):
def __init__(self, text_widget):
self.output = text_widget
def write(self, string):
self.output.config(state=NORMAL)
self.output.update_idletasks()
self.output.insert('end', string)
self.output.see('end')
self.output.config(state=DISABLED)
def flush(self):
pass
def func(self):
print("test")
proc=mp.Process(target=go)
proc.start()
def go():
for i in range(0,10):
time.sleep((1))
print(i)
if __name__ == "__main__":
Gui()
The issue in your code is that you are changing the content of a tkinter widget outside of the mainloop, which is not possible. One solution I have found is to redirect the stdout and stderr to Queues and periodically read the content of these queues in the mainloop to update the text widget. The periodical reading is done using the method .after(<delay in ms>, <callback>).
from Tkinter import *
import multiprocessing as mp
import time
import sys
class StdoutRedirector(object):
def __init__(self, queue):
self.output = queue
def write(self, string):
self.output.put(string)
def flush(self):
pass
class Gui(object):
def __init__(self):
self.a=Tk()
b1 = Button(self.a, text="Process 1", command=self.func)
b1.grid(row=0, column=0, pady=10, padx=10, sticky=SE)
self.messages=Text(self.a, height=2.5, width=30, bg="light cyan",
state=NORMAL)
self.messages.grid(row=1, column=0, columnspan=3)
self.messages.tag_configure('error', foreground='red')
# queues to get the stdout and stderr
self.stdout = mp.Queue()
self.stderr = mp.Queue()
self.proc = None # process
self.a.mainloop()
def stdout_update(self):
self.messages.configure(state=NORMAL)
while not self.stdout.empty(): # display stdout
self.messages.insert('end', self.stdout.get(False))
while not self.stderr.empty(): # display stderr
self.messages.insert('end', self.stderr.get(False), 'error')
self.messages.configure(state=DISABLED)
if self.proc.is_alive(): # the process is not finished, schedule the next display update
self.a.after(100, self.stdout_update)
def func(self):
self.proc = mp.Process(target=go, args=(self.stdout, self.stderr))
self.proc.start()
self.stdout_update() # launch display update
def go(queue_out, queue_err):
# redirect stdout and stderr to queues
sys.stdout = StdoutRedirector(queue_out)
sys.stderr = StdoutRedirector(queue_err)
for i in range(0, 2):
time.sleep(1)
print(i)
1/0 # to test the stderr
if __name__ == "__main__":
Gui()

How to get the item in list view using with enter key

In my program i want to use key press event in list view.When i press the enter key then only my selected text will print..Can any one please tell me how to use key press event for list view.I tried with installEventFilter but this method showing non type error.
Given below is my code:
import sys
from PyQt4 import QtCore,QtGui
class List_View(QtGui.QMainWindow):
def __init__(self, parent=None):
super(List_View, self).__init__(parent)
self.listview = QtGui.QListView()
model = QtGui.QStandardItemModel(self.listview)
for i in ['linux', 'windows', 'mac os']:
model.appendRow(QtGui.QStandardItem(i))
self.listview.setModel(model)
self.listview.entered.connect(self.add_items)
widget = QtGui.QWidget()
self.setCentralWidget(widget)
vlay = QtGui.QVBoxLayout(widget)
vlay.addWidget(self.listview)
self.resize(400, 200)
def add_items(self):
pass #here i need to print particular item in list view
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = List_View()
w.show()
sys.exit(app.exec_())
I think you have not read the docs of the entered signal or you have not understood it, that signal only triggers when the mouse is on the item but for this you must activate the mouse tracking with self.listview.setMouseTracking(True)
A simple solution is to use a QShortcut:
import sys
from PyQt4 import QtCore,QtGui
class List_View(QtGui.QMainWindow):
def __init__(self, parent=None):
super(List_View, self).__init__(parent)
shorcut = QtGui.QShortcut(
QtGui.QKeySequence(QtCore.Qt.Key_Return),
self
)
shorcut.activated.connect(self.on_enter_pressed)
self.listview = QtGui.QListView()
model = QtGui.QStandardItemModel(self.listview)
for i in ['linux', 'windows', 'mac os']:
model.appendRow(QtGui.QStandardItem(i))
self.listview.setModel(model)
widget = QtGui.QWidget()
self.setCentralWidget(widget)
vlay = QtGui.QVBoxLayout(widget)
vlay.addWidget(self.listview)
self.resize(400, 200)
#QtCore.pyqtSlot()
def on_enter_pressed(self):
print("enter press")
ix = self.listview.selectionModel().currentIndex()
print(ix.data())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = List_View()
w.show()
sys.exit(app.exec_())

SIGNALs are not working in PySide

I wrote a simple application in Python and PySide. When I run it, SIGNALs are not working.
The application starts without errors.
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
dial = QDial()
dial.setNotchesVisible(True)
spinbox = QSpinBox()
layout = QHBoxLayout()
layout.addWidget(dial)
layout.addWidget(spinbox)
self.setLayout(layout)
self.connect(dial, SIGNAL("valueChaged(int)"), spinbox.setValue)
self.connect(spinbox, SIGNAL("valueChaged(int)"), dial.setValue)
self.setWindowTitle("Signals and Slots")
# END def __init__
# END class Form
def main():
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
# END def main
if __name__ == '__main__':
main()
# END if
I am using:
Pyside 1.2.2;
Python 2.7.6;
OS Centos; Windows 7
I am running the application with:
Sublime Text 3 and Eclipse Luna;
How can I make SIGNALs working?
Your signal name is incorrect;
Incorrect :
valueChaged (int)
Correct :
valueChanged (int)
Test it, work fine;
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class QFormDialog (QDialog):
def __init__(self, parent = None):
super(QFormDialog, self).__init__(parent)
self.myQial = QDial()
self.myQSpinbox = QSpinBox()
self.myQHBoxLayout = QHBoxLayout()
self.myQial.setNotchesVisible(True)
self.myQHBoxLayout.addWidget(self.myQial)
self.myQHBoxLayout.addWidget(self.myQSpinbox)
self.setLayout(self.myQHBoxLayout)
self.connect(self.myQial, SIGNAL('valueChanged(int)'), self.myQSpinbox.setValue)
self.connect(self.myQSpinbox, SIGNAL('valueChanged(int)'), self.myQial.setValue)
self.setWindowTitle('Signals and Slots')
if __name__ == '__main__':
myQApplication = QApplication(sys.argv)
myQFormDialog = QFormDialog()
myQFormDialog.show()
myQApplication.exec_()
Note : PyQt4 & PySide is same way to implemented.

Mouse events in PyQt5 and PyQt4

I used to have an application in PyQt5, where user could move QGroupBox widget, by pressing mouse button and simply move it. In PyQt5 it works perfectly, but I had to refuse PyQt5 for some reasons and use PyQt4 instead of it. But now I can move my QgroupBox widget only after double click. I use the same piece of code, but in PyQt5 it worked after one click and now only after second one (after first click the whole application's window starts to move). What should I add/edit in order to reach the same result like in PyQt5?
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class WidgetPattern(QGroupBox):
def __init__(self, name, parent=None):
super(WidgetPattern, self).__init__(parent=parent)
self.layout = QVBoxLayout(self)
self._offset = 0
self.widgetLength = 280
self.name = QLabel(parent=self)
self.name.setText(name)
self.name.adjustSize()
self.name.move(self.widgetLength / 2 - self.name.width() / 2, 5)
self.close_button = QToolButton(parent=self)
self.close_button.resize(24, 24)
self.close_button.setAutoRaise(True)
self.close_button.move(self.widgetLength - self.close_button.width(), 0)
self.close_button.setIcon(QIcon.fromTheme("application-exit"))
self.setFixedSize(280, 60)
def mousePressEvent(self, event):
self._offset = event.pos()
def mouseMoveEvent(self, event):
self.move(
QGroupBox.mapToParent(
self,
event.pos() -
self._offset))
class ConcertGUI(QWidget):
def __init__(self, parent=None):
super(ConcertGUI, self).__init__(parent)
widget=WidgetPattern("test")
self._main_layout = QVBoxLayout()
self._main_layout.addWidget(widget, 0)
self.setLayout(self._main_layout)
self.resize(1024, 500)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
gui = ConcertGUI()
gui.show()
sys.exit(app.exec_())

Duplicate stdout, stderr in QTextEdit widget

I've been having difficulty with my PySide program for a few days now. I don't think the problem is incredibly difficult because there are answer out there. Problem I have is none of them seem to work for me.
I want to 'listen' to the file objects stdout and stderr and output the contents to QText Edit widget while my PySide program is running. Now, I already realise this question (or something similar) has been asked before on here but like I said, can't get it to work for me for some reason and most other solutions out there are based on the one that I can't get working, so a very frustrating last few days for me. This solution (OutLog), is included in my code snippet below, just in case one of you guys can see a botched implementation on my part.
Things to remember:
1 I'm doing this on Windows 7(duuuh, da, da, duh)
2 I'm using eclipse and running it from inside the IDE(duh, da, da, duh, DUUUUH: It would be really handy if the suggestions worked with either commandline or an IDE)
3 I really just want to duplicate the output of stdout and stderr to the widget while the program runs. For this to happen line-by-line would be a dream but even if it all comes out as a chunk at the end of a loop or something, that would be fab.
4 Oh, and also regarding OutLog, could somebody tell me how, if self.out is set to 'None' in the init, this class can actually work? I mean, self.out is always a NoneType object, right???
Any help would be appreciated, even if it's just pointers to where I could find more information. I've been trying to build my own solution (I'm a bit of a sadist that way) but I've found it hard to find relevant info on how these objects work to do that.
Anyway, whine over. Here's my code:
#!/usr/bin/env python
import sys
import logging
import system_utilities
log = logging.getLogger()
log.setLevel("DEBUG")
log.addHandler(system_utilities.SystemLogger())
import matplotlib
matplotlib.use("Qt4Agg")
matplotlib.rcParams["backend.qt4"] = "PySide"
import subprocess
import plot_widget
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PySide import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
"""This is the main window class and displays the primary UI when launched.
Inherits from QMainWindow.
"""
def __init__(self):
"""Init function.
"""
super(MainWindow, self).__init__()
self.x = None
self.y = None
self.data_plot = None
self.plot_layout = None
self.terminal = None
self.setup_plot()
self.setup_interface()
def setup_plot(self):
"""Member function to setup the graph window in the main UI.
"""
#Create a PlotWidget object
self.data_plot = plot_widget.PlotWidget()
#Create a BoxLayout element to hold PlotWidget
self.plot_layout = QtGui.QVBoxLayout()
self.plot_layout.addWidget(self.data_plot)
def setup_interface(self):
"""Member function to instantiate and build the composite elements of the
UI."""
#Main widget houses layout elements (Layout cannot be placed directly in a QMainWindow).
central_widget = QtGui.QWidget()
test_splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
button_splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
#UI BoxLayout elements
central_layout = QtGui.QVBoxLayout()
#button_layout = QtGui.QHBoxLayout()
#UI PushButton elements
exit_button = QtGui.QPushButton("Close")
run_button = QtGui.QPushButton("Run...")
#UI Text output
self.editor = QtGui.QTextEdit()
self.editor.setReadOnly(True)
self.terminal = QtGui.QTextBrowser()
self.terminal.setReadOnly(True)
#UI PushButton signals
run_button.clicked.connect(self.run_c_program)
run_button.clicked.connect(self.data_plot.redraw_plot)
exit_button.clicked.connect(QtCore.QCoreApplication.instance().quit)
#Build the UI from composite elements
central_layout.addLayout(self.plot_layout)
central_layout.addWidget(self.editor)
button_splitter.addWidget(run_button)
button_splitter.addWidget(exit_button)
test_splitter.addWidget(button_splitter)
test_splitter.addWidget(self.terminal)
test_splitter.setCollapsible(1, True)
central_layout.addWidget(test_splitter)
central_widget.setLayout(central_layout)
self.setCentralWidget(central_widget)
self.show()
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)
log.debug("this is m {}".format(m))
self.edit.insertPlainText( m )
if self.color:
self.edit.setTextColor(tc)
if self.out:
self.out.write(m)
def main():
app = QtGui.QApplication(sys.argv)
log.debug("Window starting.")
window = MainWindow()
sys.stdout = OutLog(window.terminal, sys.stdout)
sys.stderr = OutLog(window.terminal, sys.stderr, QtGui.QColor(255,0,0))
window.show()
sys.exit(app.exec_())
log.info("System shutdown.")
if __name__ == '__main__':
main()
"Help me Obi-Wan..."
Thanks in advance guys (and gals :-))
It seems that all you need to do is override sys.stderr and sys.stdout with a wrapper object that emits a signal whenever output is written.
Below is a demo script that should do more or less what you want. Note that the wrapper class does not restore sys.stdout/sys.stderr from sys.__stdout__/sys.__stderr__, because the latter objects may not be same as the ones that were orignally replaced.
PyQt5:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class OutputWrapper(QtCore.QObject):
outputWritten = QtCore.pyqtSignal(object, object)
def __init__(self, parent, stdout=True):
super().__init__(parent)
if stdout:
self._stream = sys.stdout
sys.stdout = self
else:
self._stream = sys.stderr
sys.stderr = self
self._stdout = stdout
def write(self, text):
self._stream.write(text)
self.outputWritten.emit(text, self._stdout)
def __getattr__(self, name):
return getattr(self._stream, name)
def __del__(self):
try:
if self._stdout:
sys.stdout = self._stream
else:
sys.stderr = self._stream
except AttributeError:
pass
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__( )
widget = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(widget)
self.setCentralWidget(widget)
self.terminal = QtWidgets.QTextBrowser(self)
self._err_color = QtCore.Qt.red
self.button = QtWidgets.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout.addWidget(self.terminal)
layout.addWidget(self.button)
stdout = OutputWrapper(self, True)
stdout.outputWritten.connect(self.handleOutput)
stderr = OutputWrapper(self, False)
stderr.outputWritten.connect(self.handleOutput)
def handleOutput(self, text, stdout):
color = self.terminal.textColor()
self.terminal.moveCursor(QtGui.QTextCursor.End)
self.terminal.setTextColor(color if stdout else self._err_color)
self.terminal.insertPlainText(text)
self.terminal.setTextColor(color)
def handleButton(self):
if QtCore.QTime.currentTime().second() % 2:
print('Printing to stdout...')
else:
print('Printing to stderr...', file=sys.stderr)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())
PyQt4:
import sys
from PyQt4 import QtGui, QtCore
class OutputWrapper(QtCore.QObject):
outputWritten = QtCore.pyqtSignal(object, object)
def __init__(self, parent, stdout=True):
QtCore.QObject.__init__(self, parent)
if stdout:
self._stream = sys.stdout
sys.stdout = self
else:
self._stream = sys.stderr
sys.stderr = self
self._stdout = stdout
def write(self, text):
self._stream.write(text)
self.outputWritten.emit(text, self._stdout)
def __getattr__(self, name):
return getattr(self._stream, name)
def __del__(self):
try:
if self._stdout:
sys.stdout = self._stream
else:
sys.stderr = self._stream
except AttributeError:
pass
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
self.setCentralWidget(widget)
self.terminal = QtGui.QTextBrowser(self)
self._err_color = QtCore.Qt.red
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout.addWidget(self.terminal)
layout.addWidget(self.button)
stdout = OutputWrapper(self, True)
stdout.outputWritten.connect(self.handleOutput)
stderr = OutputWrapper(self, False)
stderr.outputWritten.connect(self.handleOutput)
def handleOutput(self, text, stdout):
color = self.terminal.textColor()
self.terminal.moveCursor(QtGui.QTextCursor.End)
self.terminal.setTextColor(color if stdout else self._err_color)
self.terminal.insertPlainText(text)
self.terminal.setTextColor(color)
def handleButton(self):
if QtCore.QTime.currentTime().second() % 2:
print('Printing to stdout...')
else:
sys.stderr.write('Printing to stderr...\n')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())
NB:
Instances of the OutputWrapper should be created as early as possible, so as to ensure that other modules that need sys.stdout/sys.stderr (such as the logging module) use the wrapped versions wherever necessary.
self.out = None is probably a typo and should be self.out = out. That way, you can see anything that printed in the console as well. This is the first step to be sure that the code prints anything at all.
The next thing is that you need to realize which output you're redirecting. A subprocess get its own stdio, so no amount of redirection of the parent's stdout is going to have any effect.
Getting stdio correct with a subprocess isn't trivial. I suggest to start with subprocess.communicate() which gives you all the output as a single string. That's usually good enough.