I'm learning how to use PyQt correctly.
Trying to make a window Class with a button that prints a line.
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class Window:
def __init__(self, args):
#initializes PyQt
self.app = QApplication(args)
self.widget = QWidget()
self.widget.setWindowTitle("PyQt5 Example")
#create label
self.textLabel = QLabel(self.widget)
self.textLabel.setText("Hello World!")
self.textLabel.move(110,85)
#create button
self.button = QPushButton(self.widget)
self.button.setText("Click me!")
self.button.move(64,32)
#connect button to a method
self.button.clicked.connect(buttonClicked)
#set starting position on monitor (50, 50) and window size (320, 200)
self.widget.setGeometry(50,50,320,200)
#show window
self.widget.show()
sys.exit(app.exec_())
def buttonClicked(self):
print("Button clicked!")
if __name__ == '__main__':
print(sys.argv)
app = Window(sys.argv)
When I run the code I get, NameError: name 'buttonClicked' is not defined
Double checking all indentation and object syntax it's not apparent to me what is wrong. What's weirder is that such syntax works completely fine when outside of a class.
E.g the following code returns no NameError,
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
def window():
app = QApplication(sys.argv)
widget = QWidget()
button1 = QPushButton(widget)
button1.setText("Button1")
button1.move(64,32)
button1.clicked.connect(button1_clicked)
button2 = QPushButton(widget)
button2.setText("Button2")
button2.move(64,64)
button2.clicked.connect(button2_clicked)
widget.setGeometry(50,50,320,200)
widget.setWindowTitle("PyQt5 Button Click Example")
widget.show()
sys.exit(app.exec_())
def button1_clicked():
print("Button 1 clicked")
def button2_clicked():
print("Button 2 clicked")
if __name__ == '__main__':
window()
I wasn't properly referencing the method. when calling self.button.clicked.connect(buttonClicked).
What is required is self.button.clicked.connect(self.buttonClicked)
Related
I want to create an application with a system tray icon. This app should detect the hotkey and react to it, even if the app is not focused (icon tray).
Example: I want to show the window, then I press Alt + X. The window is showing.
I tried using some global hotkey libraries but whenever the window is showing by hotkey, it freezes. The Show option in my QMenu still working.
I'm new to programming, thanks!
Here is my code:
from PyQt5.QtCore import QSize, Qt
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit, QVBoxLayout, QHBoxLayout, QWidget, QSystemTrayIcon, QMenu
from PyQt5.QtGui import QIcon, QKeySequence
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('GTyperP5')
self.setWindowIcon(QIcon('GTyperP5.ico'))
self.setFixedSize(QSize(192, 64))
self.setWindowFlag(Qt.WindowMinimizeButtonHint, False)
self.textInput = QLineEdit()
self.typeButton = QPushButton('Type')
self.typeButton.setFixedSize(QSize(50, 25))
self.typeButton.clicked.connect(self.cut_all)
self.textInput.returnPressed.connect(self.typeButton.click)
self.cancelButton = QPushButton('Cancel')
self.cancelButton.setFixedSize(QSize(50, 25))
self.cancelButton.clicked.connect(self.hide_window)
layout = QVBoxLayout()
layout.addWidget(self.textInput, alignment = Qt.AlignHCenter | Qt.AlignTop)
layout_ex = QHBoxLayout()
layout_ex.addWidget(self.cancelButton, alignment = Qt.AlignRight)
layout_ex.addWidget(self.typeButton, alignment = Qt.AlignLeft)
layout.addLayout(layout_ex)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
self.trayIcon = QSystemTrayIcon(QIcon('GTyperP5.ico'), parent = app)
self.trayIcon.setToolTip('GTyperP5')
menu = QMenu()
showAction = menu.addAction('Show')
showAction.triggered.connect(self.show_window)
exitAction = menu.addAction('Exit')
exitAction.triggered.connect(app.quit)
self.trayIcon.setContextMenu(menu)
self.trayIcon.show()
self.trayIcon.showMessage('GTyperP5 has started!', 'Thank you for using! :D', QIcon('GTyperP5.ico'), msecs = 10000)
self.trayIcon.activated.connect(self.doubleClick)
def closeEvent(self, event):
self.hide_window()
event.ignore()
def doubleClick(self, reason):
if reason == QSystemTrayIcon.DoubleClick:
self.show_window()
def cut_all(self):
clip = QApplication.clipboard()
clip.clear(mode = clip.Clipboard)
clip.setText(self.textInput.text(), mode = clip.Clipboard)
self.textInput.setText('')
self.hide_window()
def hide_window(self):
self.trayIcon.show()
self.hide()
def show_window(self):
self.trayIcon.hide()
self.show()
self.raise_()
self.textInput.setFocus()
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
Keep gif running while GUI starts. Is that possible? I have read many reporitys but none with the true and understandable answer.
I have prepared a code example that shows the problem.
import sys
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget
from PyQt5 import QtWidgets
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QSize, QThread
class Main_Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(500, 500))
self.setWindowTitle("Main Window")
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
gridLayout = QGridLayout(self)
centralWidget.setLayout(gridLayout)
gif = QLabel(self)
gif.setGeometry(0,0,500,500)
self.movie = QMovie(r"C:\Users\...\Pictures\Icon_LOAD.gif")
gif.setMovie(self.movie)
self.movie.start()
# #Call event handler to process the queue, but it is shocking, dirty and unsuitable
#app.processEvents()
self.show()
for i in range(0,1000000):
print(i)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWin = Main_Window()
sys.exit(app.exec_())
Now it works smooth
import sys
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget
from PyQt5 import QtWidgets
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QSize
from threading import Thread
class Main_Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(500, 500))
self.setWindowTitle("Main Window")
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
gridLayout = QGridLayout(self)
centralWidget.setLayout(gridLayout)
gif = QLabel(self)
gif.setGeometry(0,0,500,500)
self.movie = QMovie(r"gif.gif")
gif.setMovie(self.movie)
self.movie.start()
self.show()
Thread(target=self.function_on_thread).start()
def function_on_thread(self):
for i in range(0,1000000):
print(i)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWin = Main_Window()
sys.exit(app.exec_())
Here's my attempt at making this work:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QTextEdit
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.on_click()
def initUI(self):
button = QPushButton('PyQt5 button', self)
button.move(100,200)
button.clicked.connect(self.on_click)
def on_click(self):
text = QTextEdit(self)
insideText = "Text"
text.document().setPlainText(insideText)
text.resize(100,25)
print('PyQt5 button click')
position = text.pos()
text.move(position.x()+50,position.y()+0)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
Maybe I need to make the QTextEdit go somewhere in the initUI() method, instead of being defined in on_click(). I already tried this but I get "text is not defined", and I'm not sure how to reference it inside another function/method. Any help is appreciated.
Edit: Here's the working code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton,QTextEdit
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.on_click()
def initUI(self):
self.button = QPushButton('PyQt5 button', self)
self.button.move(100,200)
self.button.clicked.connect(self.on_click)
self.text = QTextEdit(self)
insideText = "Text"
self.text.document().setPlainText(insideText)
self.text.resize(100,25)
def on_click(self):
print('PyQt5 button click')
position = self.text.pos()
self.text.move(position.x()+50,position.y()+0)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
There has been a small problem with a little project of mine using PyQt5. I tried to add a random QWidget (in this example a QPushbutton) to a custom QWidget. However, I don't understand the behavior of the "setParent" function. When I use it outside of the custom QWidget, the QPushButton is displayed. When I use it in a declared function of the custom Widget, the QPushButton is occluded and I have no chance of displaying it outside of adding a layout (which I don't want). Here an example of the source code:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class customWidget(QWidget):
def __init__(self):
super().__init__()
self.addButton()
def addButton(self):
button = QPushButton('not_showing')
button.setParent(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
button = QPushButton('showing')
button.setParent(w)
button.move(50,50)
w.resize(600,600)
w.move(1000,300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
There is no change, when adding the parent during the initialization of the QPushButton.
When the function addButton exits, the button is removed.
If you want to see the button, try this:
class customWidget(QWidget):
def __init__(self):
super().__init__()
self.addButton()
self.button = None
def addButton(self):
if self.button is None:
self.button = QPushButton('not_showing')
self.button.setParent(self)
You do not have this problem in the main function because this function does not return until the application is stopped.
EDIT: The comment was right, but you also missed some arguments. This will work
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class customWidget(QWidget):
def __init__(self, parent=None):
super(customWidget, self).__init__(parent)
self.addButton()
def addButton(self):
button = QPushButton('not_showing')
button.setParent(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = customWidget()
button = QPushButton('showing')
button.setParent(w)
button.move(50,50)
w.resize(600,600)
w.move(1000,300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
One day old in terms of experience with PyQT, I followed sample code HERE to do this, but I am clueless as to how I could separate the start download part from the start GUI part, so that I can instead start that when I press the OK (startBtn)button. Also, know the command I do doesn't do anything but give you an error, but I know that works.
Any help appreciated!
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QAction, qApp, QDesktopWidget, QPushButton, QHBoxLayout, QVBoxLayout, QTextEdit
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QThread, QProcess
import sys
class GUI(QProcess):
def __init__(self):
super().__init__()
# Create an instance variable here (of type QTextEdit)
startBtn = QPushButton('OK')
stopBtn = QPushButton('Cancel')
#startBtn.clicked.connect()
stopBtn.clicked.connect(qApp.exit)
self.hbox = QHBoxLayout()
self.hbox.addStretch(1)
self.hbox.addWidget(startBtn)
self.hbox.addWidget(stopBtn)
self.edit = QTextEdit()
self.edit.setWindowTitle("QTextEdit Standard Output Redirection")
self.vbox = QVBoxLayout()
self.vbox.addStretch(1)
self.vbox.addWidget(self.edit)
self.vbox.addLayout(self.hbox)
#setLayout(self.vbox)
self.central=QWidget()
#self.vbox.addWidget(self.edit)
self.central.setLayout(self.vbox)
self.central.show()
def readStdOutput(self):
self.edit.append(str(self.readAllStandardOutput()))
def main():
app = QApplication(sys.argv)
qProcess = GUI()
qProcess.setProcessChannelMode(QProcess.MergedChannels);
qProcess.start("youtube-dl")
qProcess.readyReadStandardOutput.connect(qProcess.readStdOutput);
return app.exec_()
if __name__ == '__main__':
main()
2 notes:
If you also know how to disable the OK button when you press it, until the process is finished, then I'd love to know.
Not all imports are used, but I can clean that later. PyCharm show which is used and not. Cleanup is for later.
To do what you ask you have to have some considerations:
youtube-dl requires parameters, like the url, for this I have placed a QLineEdit.
To know when the process starts and ends, we use the signal: stateChanged(newState)
Complete code:
import sys
from PyQt5.QtCore import QProcess
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QTextEdit, QLabel, QLineEdit
class GUI(QProcess):
def __init__(self, parent=None):
super(GUI, self).__init__(parent=parent)
# Create an instance variable here (of type QTextEdit)
self.startBtn = QPushButton('OK')
self.stopBtn = QPushButton('Cancel')
self.hbox = QHBoxLayout()
self.hbox.addStretch(1)
self.hbox.addWidget(self.startBtn)
self.hbox.addWidget(self.stopBtn)
self.label = QLabel("Url: ")
self.lineEdit = QLineEdit()
self.lineEdit.textChanged.connect(self.EnableStart)
self.hbox2 = QHBoxLayout()
self.hbox2.addWidget(self.label)
self.hbox2.addWidget(self.lineEdit)
self.edit = QTextEdit()
self.edit.setWindowTitle("QTextEdit Standard Output Redirection")
self.vbox = QVBoxLayout()
self.vbox.addStretch(1)
self.vbox.addLayout(self.hbox2)
self.vbox.addWidget(self.edit)
self.vbox.addLayout(self.hbox)
self.central = QWidget()
self.central.setLayout(self.vbox)
self.central.show()
self.startBtn.clicked.connect(self.startDownload)
self.stopBtn.clicked.connect(self.kill)
self.stateChanged.connect(self.slotChanged)
self.EnableStart()
def slotChanged(self, newState):
if newState == QProcess.NotRunning:
self.startBtn.setDisabled(False)
elif newState == QProcess.Running:
self.startBtn.setDisabled(True)
def startDownload(self):
self.start("youtube-dl", [self.lineEdit.text()])
def readStdOutput(self):
self.edit.append(str(self.readAllStandardOutput()))
def EnableStart(self):
self.startBtn.setDisabled(self.lineEdit.text() == "")
def main():
app = QApplication(sys.argv)
qProcess = GUI()
qProcess.setProcessChannelMode(QProcess.MergedChannels)
qProcess.readyReadStandardOutput.connect(qProcess.readStdOutput)
return app.exec_()
if __name__ == '__main__':
main()
Screenshot: