I have connected a QPushButton to a method that call file dialog. The simplified code look like this:
def init_buttons(self):
self.browse_button = QPushButton('&Browse')
self.browse_button.clicked.connect(self.browse_file)
def browse_file(self):
file_name = QFileDialog.getExistingDirectory()
# Just for checking
print(file_name)
Sometimes QFileDialog won't showing up. The process is indeed running, since the main class/widget doesn't response to my clicking. Sometimes it's showing up.
If QFileDialog doesn't show up, with pycharm, I have to stop and kill process to end the program. If I run the program directly from terminal, I have to manually end the running process to end the program. I can't figure out what causing this, since terminal not showing any exception or warning.
So, what is this?
The parameters for the getExistingDirectory were wrong. Please try this. Also, I have added further information in my pull request.
import os
def browse_file(self):
self.save_dir = QFileDialog.getExistingDirectory(self,
"Open Save Directory", os.path.expanduser('~'))
print(self.save_dir)
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import (QMainWindow, QTextEdit,
QAction,QMessageBox, QFileDialog, QApplication,QPushButton,QInputDialog,QLineEdit)
from PyQt5.QtGui import QIcon
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.fileName=""
self.text=""
btn1 = QPushButton("Encrypt", self)
btn1.clicked.connect(self.onBtn1)
self.show()
def onBtn1(self):
self.fileName, _ = QFileDialog.getOpenFileName(self, 'Open file', '/Users/Jarvis/Desktop/')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Related
I'm trying to run a simple PyQt5 application on Linux, the code is as follows:
#!/usr/bin/python
import sys
from PyQt5.QtWidgets import QApplication, QWidget
def main():
app = QApplication(sys.argv)
w = QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
mime = app.clipboard().mimeData()
print(mime.hasImage()) # True
print(mime.imageData()) # None
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Before running it, I copied an image into the clipboard, so mime.hasImage() should return True. No problem, that's also the case. But what's weird is, mime.imageData() sometimes returns None. that shouldn't happen. mime.imageData() should contain the image that I copied instead of None. Is there anything wrong with the code?
By the way, this seems to only happen on Linux, mime.imageData() never returns None on Windows. I'm using python3
That hasImage() returns True does not imply that imageData() returns a QImage since it only indicates that the user copied an image to the clipboard, and in what format do I copy the image? Well, it could be png, jpg, etc or it could provide the url for the client application to download or html to insert it into the client application and then obtain the image by rendering the HTML.
So in general the application from which the image was copied is responsible for the sending format and that there is no restrictive standard for that format but there are common formats.
The following example shows the logic to handle the images that come from urls and HTML:
#!/usr/bin/python
import sys
from functools import cached_property
from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtGui import QGuiApplication, QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from bs4 import BeautifulSoup
class ImageDownloader(QObject):
finished = pyqtSignal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
self.manager.finished.connect(self.handle_finished)
#cached_property
def manager(self):
return QNetworkAccessManager()
def start_download(self, url):
self.manager.get(QNetworkRequest(url))
def handle_finished(self, reply):
if reply.error() != QNetworkReply.NoError:
print("error: ", reply.errorString())
return
image = QImage()
image.loadFromData(reply.readAll())
self.finished.emit(image)
class ClipboardManager(QObject):
imageChanged = pyqtSignal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
QGuiApplication.clipboard().dataChanged.connect(
self.handle_clipboard_datachanged
)
self.downloader.finished.connect(self.imageChanged)
#cached_property
def downloader(self):
return ImageDownloader()
def handle_clipboard_datachanged(self):
mime = QGuiApplication.clipboard().mimeData()
if mime.hasImage():
image = mime.imageData()
if image is not None:
self.imageChanged.emit(image)
elif mime.hasUrls():
url = mime.urls()[0]
self.downloader.start_download(urls[0])
elif mime.hasHtml():
html = mime.html()
soup = BeautifulSoup(html, features="lxml")
imgs = soup.findAll("img")
if imgs:
url = QUrl.fromUserInput(imgs[0]["src"])
self.downloader.start_download(url)
else:
for fmt in mime.formats():
print(fmt, mime.data(fmt))
def main():
app = QApplication(sys.argv)
label = QLabel(scaledContents=True)
label.resize(250, 150)
label.move(300, 300)
label.setWindowTitle("Simple")
label.show()
manager = ClipboardManager()
manager.imageChanged.connect(
lambda image: label.setPixmap(QPixmap.fromImage(image))
)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I am currently trying to create a GUI that allows users to type in whatever words they want to be translated into R2-D2's voice.
I have used Qt 5 Designer to create a user input using Line Edit and a button that will publish the user input to a specified topic in ROS. I have also converted it into a python file using pyuic5 -o as r2d2_sound_control.py and I have a main file called main_r2d2_sound_control.py that I will run to initialise the GUI.
How do you store and parse a string from Line Edit using python? I do not know how to declare the string from the user input and parse it when the button is clicked. I'm using pyqt 4
Thank you in advance.
Link to R2-D2 voice package in GitHub https://github.com/koide3/ros2d2
Contents of r2d2_sound_control.py:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'r2d2_sound_control.ui'
#
# Created by: PyQt5 UI code generator 5.10.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(505, 114)
self.pushButton_speak = QtWidgets.QPushButton(Dialog)
self.pushButton_speak.setGeometry(QtCore.QRect(300, 40, 89, 25))
self.pushButton_speak.setObjectName("pushButton_speak")
self.lineEdit_speak = QtWidgets.QLineEdit(Dialog)
self.lineEdit_speak.setGeometry(QtCore.QRect(60, 40, 201, 25))
self.lineEdit_speak.setObjectName("lineEdit_speak")
self.retranslateUi(Dialog)
self.pushButton_speak.clicked.connect(Dialog.clicked_speak)
self.lineEdit_speak.returnPressed.connect(Dialog.clicked_speak)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.pushButton_speak.setText(_translate("Dialog", "Say Out"))
Contents of main_r2d2_sound_control.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# GUI
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
#Import the automatically generated file
from r2d2_sound_control import Ui_Dialog
# ROS
import rospy
from std_msgs.msg import String
class Test(QDialog):
def __init__(self,parent=None):
# GUI
super(Test, self).__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
# ROS pub settings
self.r2d2_sound_controller_String = String()
self.pub_r2d2_sound_controller_speak = rospy.Publisher('/ros2d2_node/speak',String,queue_size=10)
def speak_content(self):
self.input = string(lineEdit_speak)
self.lineEdit_speak.setText(self.text())
def clicked_speak(self):
"""
The slot name specified in Qt Designer.
Write the process you want to execute with the "Say Out" button.
"""
self.r2d2_sound_controller_String.data = text()
self.pub_r2d2_sound_controller_speak.publish(self.r2d2_sound_controller_String)
self.r2d2_sound_controller_String.data = ''
if __name__ == '__main__':
rospy.init_node('r2d2_sound_talker')
app = QApplication(sys.argv)
window = Test()
window.show()
sys.exit(app.exec_())
I have managed to fix it thanks to my friend. I have made the following changes to main_r2d2_sound_control.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# GUI
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
#Import the automatically generated file
from r2d2_head_gripper_sound import Ui_Dialog
# ROS
import rospy
from std_msgs.msg import String
class Test(QDialog):
def __init__(self,parent=None):
# GUI
super(Test, self).__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
# ROS pub settings
self.r2d2_sound_controller_String = String()
self.pub_r2d2_sound_controller_speak = rospy.Publisher('/ros2d2_node/speak',String,queue_size=10)
self.r2d2_sound_controller_String.data = ''
def clicked_speak(self):
"""
The slot name specified in Qt Designer.
Write the process you want to execute with the "Say Out" button.
"""
text = self.ui.lineEdit_speak.text()
self.r2d2_sound_controller_String.data = text
self.pub_r2d2_sound_controller_speak.publish(self.r2d2_sound_controller_String)
if __name__ == '__main__':
rospy.init_node('r2d2_head_and_gripper_talker')
app = QApplication(sys.argv)
window = Test()
window.show()
sys.exit(app.exec_())
I'm working on an application that runs visualization using 3rd party software, and sending commands and doing other things through the PyQt5 GUI. At this point, both the PyQt5 window and the visualization run on separate windows, but I'd like to at some point merge them so that it looks like one window. To do that, I'm trying to make a simple example work. Here is code that spawns the calculator app on Mac and a simple window that has some text labels in it:
import os
import sys
import subprocess
import atexit
from PyQt5 import QtWidgets
class CalcWindow(QtWidgets.QMainWindow):
def __init__(self):
super(CalcWindow, self).__init__()
self.setWindowTitle("Embedded Calc")
lines_widget = LinesWidget()
self.setCentralWidget(lines_widget)
calc_path = "/Applications/Calculator.app/Contents/MacOS/Calculator"
print("Path = " + calc_path)
print("Exists: " + str(os.path.exists(calc_path)))
p = subprocess.Popen(calc_path)
atexit.register(self.kill_proc, p)
#staticmethod
def kill_proc(proc):
try:
proc.terminate()
except Exception:
pass
class LinesWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.vLayout = QtWidgets.QVBoxLayout()
self.line1 = QtWidgets.QHBoxLayout()
self.line1.addWidget(QtWidgets.QLabel("HELLO"))
self.line1.addWidget(QtWidgets.QLabel("WORLD"))
self.line2 = QtWidgets.QHBoxLayout()
self.line2.addWidget(QtWidgets.QLabel("SUP"))
self.line2.addWidget(QtWidgets.QLabel("DAWG"))
self.vLayout.addLayout(self.line1)
self.vLayout.addLayout(self.line2)
self.setLayout(self.vLayout)
def main():
app = QtWidgets.QApplication(sys.argv)
calc = CalcWindow()
calc.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I'd like the calculator to be apart of the same window as the Text Labels which are in the main window. I've seen it done on the Windows using hwnd , but I don't know how to directly translate that to OSX or if it's even possible. Maybe there's some PyQt magic of throwing the executable in a widget and adding it to the main window that would work. Not sure. Any help would be appreciated. Thanks.
I have a Qt Application with multiple main windows and want to get a signal when the App is about to quit while all windows are still up.
The QApplication.aboutToQuit signal doesn't work for me because it only fires after all windows have been closed.
All other answers I've seen suggest implementing the main window's closeEvent() function, but I have multiple main windows and I couldn't find any way to differentiate CloseEvents between a normal closeEvent of just a single window being closed and a close event from when the whole application is closing after QMD+Q or the using clicking quit in the task bar or for whatever reason it quits.
How do I get a signal for that only when the whole application is about to quit but before any windows are closed?
What happens when I press Cmd+Q or right click quit the task bar icon:
(I want a signal at this point) <-
all windows get a closeEvent()
aboutToQuit() fires
app quits
What I want is to get a signal before any of that happens and all the windows are still open.
Edit: minimal example
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget
class Win(QWidget):
def closeEvent(self, event: QCloseEvent):
# implementing a custom closeEvent doesn't help me
# this is called for every normal manual window close and when the application quits
# I only want to run something when the full application gets shut down, not just this window
# can't see any way to differentiate between the two
print("closeEvent", event.type(), event)
return super().closeEvent(event)
if __name__ == '__main__':
app = QApplication([])
app.aboutToQuit.connect(lambda :print("about to quit, this is too late, by now all windows are closed"))
#What I need
# app.beforeQuitSignal.connect(lambda :print("signal that it's gonna quit, before any of the windows are closed"))
#stuff I've tried that didn't work either
app.quit = lambda *args:print("quit, doesnt get called")
app.exit = lambda *args:print("exit, doesnt get called")
app.closeAllWindows = lambda *args:print("closeAllWindows, doesnt get called")
mainwins = []
for i in range(5):
win = Win()
win.setGeometry(100*i,100*i, 400,400), win.show(), win.raise_()
mainwins.append(win)
app.exec_()
If you want to fire a signal when the user close a window but do not exit the QApplication immediately after the last window is closed, you can use QApplication.setQuitOnLastWindowClosed()
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget
class Win(QWidget):
closing = pyqtSignal()
def closeEvent(self, event: QCloseEvent):
print("Window {} closed".format(self))
self.closing.emit()
return super().closeEvent(event)
class MyApp(QApplication):
def __init__(self, *args):
super(MyApp, self).__init__(*args)
self.setQuitOnLastWindowClosed(False)
self.lastWindowClosed.connect(self.onLastClosed)
self.mainwins = []
for i in range(5):
win = Win()
win.setGeometry(100 * i, 100 * i, 400, 400), win.show(), win.raise_()
self.mainwins.append(win)
def onLastClosed(self):
print("Last windows closed, exiting ...")
self.exit()
if __name__ == '__main__':
app = MyApp([])
app.exec_()
With this, when a window is closed, a closing() signal is emited
You can filter on the CloseEvent if it is spontaneous :
from PyQt5.QtCore import pyqtSignal, QEvent
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget
class Win(QWidget):
closing = pyqtSignal()
def closeEvent(self, event: QCloseEvent):
self.closing.emit()
if event.type() == QEvent.Close and event.spontaneous():
event.ignore()
else:
return super().closeEvent(event)
class MyApp(QApplication):
def __init__(self, *args):
super(MyApp, self).__init__(*args)
self.mainwins = []
for i in range(5):
win = Win()
win.closing.connect(self.beforeQuit)
win.setGeometry(100 * i, 100 * i, 400, 400), win.show(), win.raise_()
self.mainwins.append(win)
def beforeQuit(self):
print("Exiting")
# Do what you want here ...
self.exit() # Terminate the QApplication
if __name__ == '__main__':
app = MyApp([])
app.exec_()
Use installEventFilter to intercept the application's quit event before any windows are closed:
class AppManager(QObject):
def __init__(self):
super().__init__()
qApp.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QEvent.Quit:
self.saveWindowStates()
return False
def saveWindowStates(self):
# code to save the application's state
I tried to establish a network connection with PySide (Ubuntu 15.04, Python3.4, PySide 1.2.4). I used the example code from the documentation.
The QNetworkAccessManager does not send the request and I recieve no answer. I checked the Network state with QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State() but it says the State is invalid. This seems to make no sense since I am on a desktop pc with a network connection via ethernet cable.
My complete example for the test is the following code:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PySide.QtGui import QApplication
from PySide.QtCore import QUrl
from PySide.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkSession, QNetworkConfigurationManager
def replyFinished(reply):
print(reply)
if __name__ == "__main__":
app = QApplication(sys.argv)
manager = QNetworkAccessManager()
manager.finished.connect(replyFinished)
print(QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State())
print("Sending request")
print(manager.get(QNetworkRequest(QUrl("http://www.heise.de/ct/"))))
This prints
PySide.QtNetwork.QNetworkSession.State.Invalid
Sending request
<PySide.QtNetwork.QNetworkReply object at 0x7f4b59c9af08>
but it should display the PySide.QtNetwork.QNetworkReply object twice.
My example was too small. I realized this because of the comment of Pavel Strakhov. I extended it to display a window with a button. By clicking the button it connects successfully. QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State() is still invalid but it works.
This is the working code:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PySide.QtGui import QApplication, QWidget, QBoxLayout, QPushButton
from PySide.QtCore import QUrl
from PySide.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkSession, QNetworkConfigurationManager
class Window(QWidget):
def __init__(self):
super().__init__()
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.reply_finished)
layout = QBoxLayout(QBoxLayout.TopToBottom)
button = QPushButton("connect")
button.clicked.connect(self.network_connect)
layout.addWidget(button)
self.setLayout(layout)
self.setWindowTitle("Network test")
self.setGeometry(100, 100, 200, 150)
self.show()
def network_connect(self):
print(QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State())
request = QNetworkRequest(QUrl("http://example.org"))
print("Sending request")
self.manager.get(request)
def reply_finished(self, reply):
print(reply)
print(reply.readData(500))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
app.exec_()