I am upgrading from Python 2.7 to Python 3.6 and from PySide to PySide2. I started by trying to get the "Hello World" from the "Getting Started" site (https://doc-snapshots.qt.io/qtforpython/gettingstarted.html) working. It displays the widget, its label and the push button, but the push button does not change the text of the label. I added a print() to verify that the button is indeed calling the method associated with the click signal, and even added an update() to try to "encourage" it a bit more. No luck.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copied from:
# https://doc-snapshots.qt.io/qtforpython/gettingstarted.html
#
# Mac OS X High Sierra (10.13.6)
#
# Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 05:52:31)
# [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
#
# PySide2 5.11.1
#
import sys
import random
from PySide2 import QtCore, QtWidgets, QtGui
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.hello = ["Hallo Welt", "你好,世界", "Hei maailma",
"Hola Mundo", "Привет мир"]
self.button = QtWidgets.QPushButton("Click me!")
self.text = QtWidgets.QLabel("Hello World")
self.text.setAlignment(QtCore.Qt.AlignCenter)
self.text.setFont(QtGui.QFont("Titillium", 30))
self.button.setFont(QtGui.QFont("Titillium", 20))
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.text)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.button.clicked.connect(self.magic)
def magic(self):
hi = random.choice(self.hello)
print(hi) # Prints when clicked
self.text.setText(hi) # Label text does not change when clicked
# self.update() # Didn't help
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = MyWidget()
widget.resize(800, 600)
widget.show()
sys.exit(app.exec_())
Installed with pipenv. And, the Pipfile:
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[[source]]
url = "http://download.qt.io/snapshots/ci/pyside/5.11/latest"
verify_ssl = false
name = "qt5"
[packages]
pyside2 = {version="*", index="qt5"}
[dev-packages]
[requires]
python_version = "3.6"
Fixed this problem on my Mac under python3.6 by adjusting the magic function:
def magic(self):
self.text.setText(random.choice(self.hello))
self.repaint()
The self.repaint() is needed for some reason, but at least works.
If you are running PySide2==5.15.0 then you need to upgrade it to 5.15.1 and thats what solved my problem.
Command to upgrade PySide2:
pip/pip3 install --upgrade PySide2
Related
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_())
PyQt 5.6.2, 5.9.1, Anaconda, vscode, Windows 10 x64.
Setting QtCore.Qt.WindowStaysOnTopHint in super().__init__() or via self.setWindowFlags() does nothing. With Qt 4.8.7 (PyQt 4.11.4) the flag works as expected.
I can fix this using Windows API (HWND_TOPMOST flag):
import sys
try:
from PyQt5 import QtCore, QtWidgets
except ImportError:
from PyQt4 import QtCore, QtGui as QtWidgets
class Form1(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.pushButton1 = QtWidgets.QPushButton("Push", self)
self.pushButton1.clicked.connect(self.clicked)
self.ontop = False
def clicked(self):
self.ontop = not self.ontop
self.windowStaysOnTopHint(self.ontop)
def windowStaysOnTopHint(self, b=True):
try:
import win32gui, win32con
flag = win32con.HWND_TOPMOST if b else win32con.HWND_NOTOPMOST
win32gui.SetWindowPos(self.winId(), flag, 0, 0, 0, 0, win32con.SWP_NOSIZE | win32con.SWP_NOMOVE)
except ModuleNotFoundError:
pass
if b:
flag = self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint
else:
flag = self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint
self.setGeometry(self.geometry()) # `setWindowFlags` resets size if setGeometry is never called
self.setWindowFlags(flag)
self.show()
print(QtCore.QT_VERSION_STR)
app = QtWidgets.QApplication(sys.argv)
form1 = Form1()
form1.show()
app.exec_()
I can use win32gui.SetWindowPos and remove setWindowFlags block at all. But I really want to know what's the reason of this behaviour.
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.
First off thanks for the help, I am a unix admin and don't know much about QT or windows 10
I have to display a qt popup menu/app on all displays on windows 10 using winexe to access a remote box, the behavior should be like the below winexe command:
winexe -U 'ad/user%xxxxxxx' --system --interactive=1 //10.14.6.133 'msg * hello jello'
below is my command line for the PyQt4 script:
winexe -U 'ad/user%xxxxxxx' --system --interactive=1 //10.14.6.133 'cmd /c c:\python26\python.exe c:\scripts\bobo3.py'
It just sits there and does nothing visible. My guess is it needs to be displayed to all active displays and I have no idea how to do this.
Here is the code, it works when run from a cmd prompt locally:
from optparse import OptionParser
from PyQt4 import QtGui, QtCore
class TopDialog(QtGui.QWidget):
def __init__(self, title='Message', width=960, height=640, ):
super(TopDialog, self).__init__()
self.setFixedSize(width, height)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
screen = QtGui.QDesktopWidget().screenGeometry()
self.move((screen.width() - self.width())/2, (screen.height() - self.height())/2)
if not isinstance(title, unicode):
title = title.decode('utf-8')
self.title_lb = QtGui.QLabel(title)
self.title_lb.setAlignment(QtCore.Qt.AlignCenter)
self.title_lb.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Sunken)
self.message_lb = QtGui.QLabel()
self.message_lb.setAlignment(QtCore.Qt.AlignCenter)
self.message_lb.setMinimumSize(self.width()/2, self.height()/2)
self.ok_bt = QtGui.QPushButton('OK')
self.ok_bt.clicked.connect(self._ok_bt_clicked)
self.main_lo = QtGui.QVBoxLayout()
self.main_lo.addWidget(self.title_lb)
self.main_lo.addStretch()
self.main_lo.addWidget(self.message_lb)
self.main_lo.addStretch()
self.main_lo.addWidget(self.ok_bt)
self.setLayout(self.main_lo)
def set_message(self, message):
if not isinstance(message, unicode):
message = message.decode('utf-8')
self.message_lb.setText(message)
def set_button_text(self, text):
if not isinstance(text, unicode):
text = text.decode('utf-8')
self.ok_bt.setText(text)
def _ok_bt_clicked(self):
# do something you want.
self.close() # close the window
client = None
def main():
global client
parser = OptionParser()
parser.add_option('-m', '--message', type=str, action="store", dest="message", help="The alert message")
parser.add_option('-s', '--sent_by', type=str, action="store", dest="sent_by", help='login of sender')
(options, args) = parser.parse_args()
app = QtGui.QApplication(sys.argv)
sent_by = options.sent_by
if sent_by is None:
sent_by = "Someone"
message = options.message
if message is None:
message = "we are not sure why"
print sent_by, message
client = TopDialog(' %s wants your attention' % (sent_by))
client.set_message('%s' % (message))
client.set_button_text('OK')
client.show()
app.exec_()
main()
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_())