This question already has answers here:
QDataWidgetMapper; mapping TableWidget to model
(1 answer)
Implementing Undo - Redo Functionality in Qt?
(3 answers)
How to undo edit of QStandardItem in PySide/PyQt?
(2 answers)
Closed 3 years ago.
I've got a simple undo/redo framework done for a mainwindow, but the problem is that I want to be able to undo the last action, and not have to click undo till item changes.
What I have right now is pretty clunky, in which the QComboBoxes don't undo/redo correctly after you go to the top of the QUndoStack and then try to go back down with the redo button.
I am pretty sure its because I am not using the editingFinished and currentIndexChanged signals correctly, is there a more sophisticated way to do this?
You can connect the QLineEdit/QComboBox to a model/view and check for changes, what I would probably use is a QTableView so that I can set the first column as the old text and second as the new text and each row would be the QLineEdits. Can someone provide a small implementation of using a QTableView as a model for QUndoStack, as i'm not sure where to start?
from PyQt5 import QtCore,QtWidgets,QtGui
from PyQt5 import uic
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(366, 440)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.lineEdit1 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit1.setGeometry(QtCore.QRect(130, 150, 113, 20))
self.lineEdit1.setObjectName("lineEdit1")
self.lineEdit2 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit2.setGeometry(QtCore.QRect(130, 180, 113, 20))
self.lineEdit2.setObjectName("lineEdit2")
self.lineEdit3 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit3.setGeometry(QtCore.QRect(130, 210, 113, 20))
self.lineEdit3.setObjectName("lineEdit3")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setGeometry(QtCore.QRect(130, 270, 69, 22))
self.comboBox.setObjectName("comboBox")
self.comboBox2 = QtWidgets.QComboBox(self.centralwidget)
self.comboBox2.setGeometry(QtCore.QRect(130, 310, 69, 22))
self.comboBox2.setObjectName("comboBox2")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 366, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
class StoreCommand(QtWidgets.QUndoCommand):
def __init__(self, field, text):
QtWidgets.QUndoCommand.__init__(self)
# Record the field that has changed.
self.field = field
# Record the text at the time the command was created.
self.text = text
def undo(self):
# Remove the text from the file and set it in the field.
self.field.setText(self.text)
def redo(self):
# Store the text in the file and set it in the field.
self.field.setText(self.text)
class StoreComboCommand(QtWidgets.QUndoCommand):
def __init__(self, field, text):
QtWidgets.QUndoCommand.__init__(self)
# Record the field that has changed.
self.field = field
# Record the text at the time the command was created.
self.text = text
def undo(self):
# Remove the text from the file and set it in the field.
index = self.field.findText(self.text)
self.field.setCurrentIndex(index)
def redo(self):
# Store the text in the file and set it in the field.
index = self.field.findText(self.text)
self.field.setCurrentIndex(index)
class Example(QtWidgets.QMainWindow,Ui_MainWindow):
def __init__(self,parent = None):
super().__init__()
self.setupUi(self)
self.comboBox.addItem('')
self.comboBox.addItem('H')
self.comboBox.addItem('N')
self.comboBox2.addItem('')
self.comboBox2.addItem('H')
self.comboBox2.addItem('N')
#
self.undoStack = QtWidgets.QUndoStack()
self.stackview = QtWidgets.QUndoView(self.undoStack)
#
self.lineEdit1.editingFinished.connect(self.storeFieldText)
self.lineEdit2.editingFinished.connect(self.storeFieldText)
self.lineEdit3.editingFinished.connect(self.storeFieldText)
self.comboBox.currentIndexChanged.connect(self.storeFieldComboText)
self.comboBox2.currentIndexChanged.connect(self.storeFieldComboText)
#Item actions
self.undoAction = self.undoStack.createUndoAction(self, self.tr("&Undo"))
self.undoAction.setShortcuts(QtGui.QKeySequence.Undo)
self.redoAction = self.undoStack.createRedoAction(self, self.tr("&Redo"))
self.redoAction.setShortcuts(QtGui.QKeySequence.Redo)
self.itemToolbar = self.addToolBar("Item actions")
self.itemToolbar.addAction(self.undoAction)
self.itemToolbar.addAction(self.redoAction)
def storeFieldText(self):
command = StoreCommand(self.sender(),self.sender().text())
self.undoStack.push(command)
def storeFieldComboText(self):
command = StoreComboCommand(self.sender(),self.sender().currentText())
self.undoStack.push(command)
def main():
app = QtWidgets.QApplication(sys.argv)
gui = Example()
gui.show()
gui.stackview.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Related
I am taking a programming course now, CSE 111 programming with functions, and my last assignment is to find an interesting code and write some test functions, to test the correct working of the code. This is the code:
#Imported modules
import sys
import wave, contextlib, math, time
import speech_recognition as sr
from moviepy.editor import AudioFileClip
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog, QMessageBox
from PyQt5.QtCore import QThread, pyqtSignal
class Ui_MainWindow(object):
"""Main window GUI."""
def __init__(self):
"""Initialisation function."""
self.mp4_file_name = ""
self.output_file = ""
self.audio_file = "speech.wav"
def setupUi(self, MainWindow):
"""Define visual components and positions."""
# Main window
MainWindow.setObjectName("MainWindow")
MainWindow.resize(653, 836)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(50, 20, 161, 41))
# Selected video file label
font = QtGui.QFont()
font.setPointSize(14)
self.label.setFont(font)
self.label.setObjectName("label")
self.selected_video_label = QtWidgets.QLabel(self.centralwidget)
self.selected_video_label.setGeometry(QtCore.QRect(230, 20, 371, 41))
font = QtGui.QFont()
font.setPointSize(8)
self.selected_video_label.setFont(font)
self.selected_video_label.setFrameShape(QtWidgets.QFrame.Box)
self.selected_video_label.setText("")
self.selected_video_label.setObjectName("selected_video_label")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(50, 90, 161, 41))
# Transcribed text box
font = QtGui.QFont()
font.setPointSize(14)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.transcribed_text = QtWidgets.QTextBrowser(self.centralwidget)
self.transcribed_text.setGeometry(QtCore.QRect(230, 320, 381, 431))
self.transcribed_text.setObjectName("transcribed_text")
self.label_5 = QtWidgets.QLabel(self.centralwidget)
self.label_5.setGeometry(QtCore.QRect(230, 280, 161, 41))
font = QtGui.QFont()
font.setPointSize(14)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.transcribe_button = QtWidgets.QPushButton(self.centralwidget)
self.transcribe_button.setEnabled(False)
self.transcribe_button.setGeometry(QtCore.QRect(230, 150, 221, 81))
# Transcribe button
font = QtGui.QFont()
font.setPointSize(14)
self.transcribe_button.setFont(font)
self.transcribe_button.setObjectName("transcribe_button")
self.transcribe_button.clicked.connect(self.process_and_transcribe_audio)
# progeress bar
self.progress_bar = QtWidgets.QProgressBar(self.centralwidget)
self.progress_bar.setGeometry(QtCore.QRect(230, 250, 381, 23))
self.progress_bar.setProperty("value", 0)
self.progress_bar.setObjectName("progress_bar")
self.message_label = QtWidgets.QLabel(self.centralwidget)
self.message_label.setGeometry(QtCore.QRect(0, 760, 651, 21))
# Message label (for errors and warnings)
font = QtGui.QFont()
font.setPointSize(8)
self.message_label.setFont(font)
self.message_label.setFrameShape(QtWidgets.QFrame.Box)
self.message_label.setText("")
self.message_label.setObjectName("message_label")
self.output_file_name = QtWidgets.QPlainTextEdit(self.centralwidget)
self.output_file_name.setGeometry(QtCore.QRect(230, 90, 371, 41))
# Output file name
font = QtGui.QFont()
font.setPointSize(14)
self.output_file_name.setFont(font)
self.output_file_name.setObjectName("output_file_name")
# Menubar options
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 653, 21))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menuAbout = QtWidgets.QMenu(self.menubar)
self.menuAbout.setObjectName("menuAbout")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionOpen_mp4_video_recording = QtWidgets.QAction(MainWindow)
self.actionOpen_mp4_video_recording.setObjectName("actionOpen_mp4_video_recording")
self.actionOpen_mp4_video_recording.triggered.connect(self.open_audio_file)
self.actionAbout_vid2text = QtWidgets.QAction(MainWindow)
self.actionAbout_vid2text.setObjectName("actionAbout_vid2text")
self.actionAbout_vid2text.triggered.connect(self.show_about)
self.actionNew = QtWidgets.QAction(MainWindow)
self.actionNew.setObjectName("actionNew")
self.actionNew.triggered.connect(self.new_project)
self.menuFile.addAction(self.actionOpen_mp4_video_recording)
self.menuFile.addAction(self.actionNew)
self.menuAbout.addAction(self.actionAbout_vid2text)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuAbout.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
"""Translate UI method."""
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "Selected video file:"))
self.label_3.setText(_translate("MainWindow", "Output file name:"))
self.label_5.setText(_translate("MainWindow", "Transcribed text:"))
self.transcribe_button.setText(_translate("MainWindow", "Transcribe"))
self.output_file_name.setPlaceholderText(_translate("MainWindow", "interview1.txt"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.menuAbout.setTitle(_translate("MainWindow", "About"))
self.actionOpen_mp4_video_recording.setText(_translate("MainWindow", "Open mp4 video recording"))
self.actionAbout_vid2text.setText(_translate("MainWindow", "About video to speech"))
self.actionNew.setText(_translate("MainWindow", "New"))
def open_audio_file(self):
"""Open the audio (*.mp4) file."""
file_name = QFileDialog.getOpenFileName()
if file_name[0][-3:] == "mp4":
self.transcribe_button.setEnabled(True)
self.mp4_file_name = file_name[0]
self.selected_video_label.setText(file_name[0])
else:
self.message_label.setText("Please select an *.mp4 file")
def convert_mp4_to_wav(self):
"""Convert the mp4 video file into an audio file."""
self.message_label.setText("Converting mp4 to audio (*.wav)...")
self.convert_thread = convertVideoToAudioThread(self.mp4_file_name, self.audio_file)
self.convert_thread.finished.connect(self.finished_converting)
self.convert_thread.start()
def get_audio_duration(self, audio_file):
"""Determine the length of the audio file."""
with contextlib.closing(wave.open(audio_file,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = frames / float(rate)
return duration
def transcribe_audio(self, audio_file):
"""Transcribe the audio file."""
total_duration = self.get_audio_duration(audio_file) / 10
total_duration = math.ceil(total_duration)
self.td = total_duration
if len(self.output_file_name.toPlainText()) > 0:
self.output_file = self.output_file_name.toPlainText()
else:
self.output_file = "my_speech_file.txt"
# Use thread to process in the background and avoid freezing the GUI
self.thread = transcriptionThread(total_duration, audio_file, self.output_file)
self.thread.finished.connect(self.finished_transcribing)
self.thread.change_value.connect(self.set_progress_value)
self.thread.start()
def finished_converting(self):
"""Reset message text when conversion is finished."""
self.message_label.setText("Transcribing file...")
self.transcribe_audio(self.audio_file)
def finished_transcribing(self):
"""This run when transcription finished to tidy up UI."""
self.progress_bar.setValue(100)
self.transcribe_button.setEnabled(True)
self.message_label.setText("")
self.update_text_output()
def set_progress_value(self, val):
"""Update progress bar value."""
increment = int(math.floor(100*(float(val)/self.td)))
self.progress_bar.setValue(increment)
def process_and_transcribe_audio(self):
"""Process the audio into a textual transcription."""
self.transcribe_button.setEnabled(False)
self.message_label.setText("Converting mp4 to audio (*.wav)...")
self.convert_mp4_to_wav()
def update_text_output(self):
"""Update the text box with the transcribed file."""
f = open(self.output_file, "r")
self.transcribed_text.setText(f.read())
f.close()
def new_project(self):
"""Clear existing fields of data."""
self.message_label.setText("")
self.transcribed_text.setText("")
self.selected_video_label.setText("")
self.output_file_name.document().setPlainText("")
self.progress_bar.setValue(0)
def show_about(self):
"""Show about message box."""
msg = QMessageBox()
msg.setWindowTitle("About video to Speech")
msg.setText("Nelson Petro - CSE 111")
msg.setIcon(QMessageBox.Information)
msg.exec_()
class convertVideoToAudioThread(QThread):
"""Thread to convert mp4 video file to wav file."""
def __init__(self, mp4_file_name, audio_file):
"""Initialization function."""
QThread.__init__(self)
self.mp4_file_name = mp4_file_name
self.audio_file = audio_file
def __del__(self):
"""Destructor."""
self.wait()
def run(self):
"""Run video conversion task."""
audio_clip = AudioFileClip(self.mp4_file_name)
audio_clip.write_audiofile(self.audio_file)
class transcriptionThread(QThread):
"""Thread to transcribe file from audio to text."""
change_value = pyqtSignal(int)
def __init__(self, total_duration, audio_file, output_file):
"""Initialization function."""
QThread.__init__(self)
self.total_duration = total_duration
self.audio_file = audio_file
self.output_file = output_file
def __del__(self):
"""Destructor."""
self.wait()
def run(self):
"""Run transcription, audio to text."""
r = sr.Recognizer()
for i in range(0, self.total_duration):
try:
with sr.AudioFile(self.audio_file) as source:
audio = r.record(source, offset=i*10, duration=10)
f = open(self.output_file, "a")
f.write(r.recognize_google(audio))
f.write(" ")
self.change_value.emit(i)
except:
print("Unknown word detected...")
continue
f.close()
def main():
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
And I have written these two test functions. The first one to test the default values of the MainWindow constructor and the second one to test the correct functionality of the
open_audio_file() function (using the directions provided in a previous question). I am not pretty familiar with OOP, so I would really appreciate if you can help me getting the second function working correct.
These are the two test functions:
def test_MainWindowConstructor():
ui = Ui_MainWindow()
assert ui.mp4_file_name == ""
assert ui.output_file == ""
assert ui.audio_file == "speech.wav"
def test_that_providing_a_raw_file_is_rejected():
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
ui.actionOpen_mp4_video_recording.triggered
QFileDialog.getOpenFileName = "video.raw"
ui.open_audio_file
assert ui.message_label.text == "Please select an *.mp4 file"
Thank you so much before hand.
You wrote:
ui.actionOpen_mp4_video_recording.triggered
...
ui.open_audio_file
As written, that doesn't make sense.
You probably want to invoke a function,
rather than evaluate an expression for
side effects and then discard it.
There's a big difference between fn and fn() --
the latter actually calls the function.
You are successfully verifying that the triggered
attribute exists, and that can be helpful in a unit test.
To verify, try some typo, such as "fliggered",
and note the AttributeError that elicits.
Similarly you are verifying the open_audio_file
attribute exists. That's a simpler situation so
it is probably less useful for a unit test to do that.
Better to call the function, and verify it behaves
as intended.
Try running your tests in this way:
$ pytest --cov --cov-report=term-missing
That will help you to notice that there's two
kinds of audio files you should test:
with & without an ".mp4" suffix.
Note that the target code sometimes mentions
a function without calling it, without
tacking on (). What's going on there?
We name that situation a "callback".
That is, the target code's API accepts
a pointer (ok, a reference) to a function.
It remembers the function, and subsequently calls
it at the appropriate time, after some UI event
has happened.
That syntax was likely confusing you -- it explains
the absence of () in the target code.
Academic integrity
You are clearly learning some things
about unit tests in this assignment,
and have commendable curiosity.
Be sure to include a reference to
this SO question when you hand in your work.
Most schools are happy with use of external
resources as long as you cite them.
I tried to design a very basic GUI app that shows the entered height on a dialog, but after I press the ‘OK’ button on the main window, the whole program crashes and the process finishes with this exit code:
Process finished with exit code -1073740791 (0xC0000409)
Here’s the full code for the app, the UI files are below:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.uic import *
class My_Dialog(QDialog):
def __init__(self):
super(My_Dialog, self).__init__()
loadUi("dialog.ui", self)
self.mid_label.setText(My_Window.mid_label_nexttext)
class My_Window(QMainWindow):
def __init__(self):
super(My_Window, self).__init__()
loadUi("mainwindow.ui", self)
self.mid_label_nexttext = None
self.height_selecter_spinbox.textChanged.connect(lambda x: self.spin_changed(x))
self.pushButton.clicked.connect(self.onMyPushButtonClick)
def onMyPushButtonClick(self):
dlg = My_Dialog()
if dlg.exec_():
print("Success!")
else:
print("Cancel!")
def spin_changed(self, s):
self.mid_label_nexttext = s
self.update()
def main():
app = QApplication(sys.argv)
window = My_Window()
window.show()
app.exec_()
if __name__ == "__main__":
main()
The main window’s UI:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'mainwindow.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(513, 171)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(310, 80, 75, 23))
self.pushButton.setObjectName("pushButton")
self.layoutWidget = QtWidgets.QWidget(self.centralwidget)
self.layoutWidget.setGeometry(QtCore.QRect(90, 40, 209, 29))
self.layoutWidget.setObjectName("layoutWidget")
self.layout = QtWidgets.QHBoxLayout(self.layoutWidget)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setObjectName("layout")
self.label_firstpart = QtWidgets.QLabel(self.layoutWidget)
font = QtGui.QFont()
font.setPointSize(15)
self.label_firstpart.setFont(font)
self.label_firstpart.setObjectName("label_firstpart")
self.layout.addWidget(self.label_firstpart)
self.height_selecter_spinbox = QtWidgets.QSpinBox(self.layoutWidget)
font = QtGui.QFont()
font.setPointSize(13)
self.height_selecter_spinbox.setFont(font)
self.height_selecter_spinbox.setMinimum(100)
self.height_selecter_spinbox.setMaximum(250)
self.height_selecter_spinbox.setProperty("value", 175)
self.height_selecter_spinbox.setObjectName("height_selecter_spinbox")
self.layout.addWidget(self.height_selecter_spinbox)
self.label_lastpart = QtWidgets.QLabel(self.layoutWidget)
font = QtGui.QFont()
font.setPointSize(15)
self.label_lastpart.setFont(font)
self.label_lastpart.setObjectName("label_lastpart")
self.layout.addWidget(self.label_lastpart)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 513, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "OK"))
self.label_firstpart.setText(_translate("MainWindow", "My height is "))
self.label_lastpart.setText(_translate("MainWindow", "cm."))
The dialog’s UI:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.dialog_buttonbox = QtWidgets.QDialogButtonBox(Dialog)
self.dialog_buttonbox.setGeometry(QtCore.QRect(30, 240, 341, 32))
self.dialog_buttonbox.setOrientation(QtCore.Qt.Horizontal)
self.dialog_buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.dialog_buttonbox.setObjectName("dialog_buttonbox")
self.widget = QtWidgets.QWidget(Dialog)
self.widget.setGeometry(QtCore.QRect(80, 90, 151, 41))
self.widget.setObjectName("widget")
self.dialog_layout = QtWidgets.QHBoxLayout(self.widget)
self.dialog_layout.setContentsMargins(0, 0, 0, 0)
self.dialog_layout.setObjectName("dialog_layout")
self.left_label = QtWidgets.QLabel(self.widget)
font = QtGui.QFont()
font.setPointSize(12)
self.left_label.setFont(font)
self.left_label.setObjectName("left_label")
self.dialog_layout.addWidget(self.left_label)
self.mid_label = QtWidgets.QLabel(self.widget)
font = QtGui.QFont()
font.setPointSize(12)
self.mid_label.setFont(font)
self.mid_label.setObjectName("mid_label")
self.dialog_layout.addWidget(self.mid_label)
self.right_label = QtWidgets.QLabel(self.widget)
font = QtGui.QFont()
font.setPointSize(12)
self.right_label.setFont(font)
self.right_label.setObjectName("right_label")
self.dialog_layout.addWidget(self.right_label)
self.retranslateUi(Dialog)
self.dialog_buttonbox.accepted.connect(Dialog.accept)
self.dialog_buttonbox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.left_label.setText(_translate("Dialog", "You are"))
self.mid_label.setText(_translate("Dialog", "100"))
self.right_label.setText(_translate("Dialog", "cm tall."))
I would appreciate some help on fixing this error, and on why it occured.
You are trying to access an attribute that does not exist:
self.mid_label.setText(My_Window.mid_label_nexttext)
My_Window is a class, while mid_label_nexttext was assigned to the instance of that class (self is always the reference to the current instance).
If you want to set the text for that label from a "parent" window, you either add an extra argument to the __init__ that allows to get the text, or you set it from the main window.
Use the text as init argument
class My_Dialog(QDialog):
def __init__(self, text):
super(My_Dialog, self).__init__()
loadUi("dialog.ui", self)
# ensure that "text" is a valid string, you can't use setText(None)
if text:
self.mid_label.setText(text)
class My_Window(QMainWindow):
# ...
def onMyPushButtonClick(self):
dlg = My_Dialog(self.mid_label_nexttext)
# ...
Set the text from the parent
class My_Dialog(QDialog):
def __init__(self):
super(My_Dialog, self).__init__()
loadUi("dialog.ui", self)
# NO setText() here!!!
class My_Window(QMainWindow):
# ...
def onMyPushButtonClick(self):
dlg = My_Dialog()
if self.mid_label_nexttext:
dlg.mid_label.setText(self.mid_label_nexttext)
# ...
Note that the first method is usually better, mostly for modularity reasons: let's say that you create that dialog from different classes in different situations, whenever you need to change the object name of the label (or the whole interface structure) you can easily change its reference in the dialog subclass, otherwise you'll need to change every reference in your code.
Note: the call to self.update() is useless; if you want to update the label on the dialog whenever the spinbox value is changed, you need to directly access the label (like in the last example) or use signals. Also, you don't need to use lambda if you're using the same argument parameter, just connect to the function.
I'm trying to create a GUI program using PyQT5. I'm new to programming and Python so if I'm explaining things in the wrong way please correct me.
I have a main window that will contain multiple QLineEdit widgets and corresponding "Clear" buttons to clear the user entered data. The main window also contains "Edit" buttons to dispay specific dialog boxes where the data can also be edited. My example has a "User ID" QLineEdit widget/text box, "Clear" and "Edit" push buttons.
The dialog box that appears when "Edit' is clicked has it's own "Clear" button. If the Clear button in the dialog window is clicked, both the QLineEdit widget in the dialog and main window should be cleared.
Problem: When I inherit the main window class from the dialog class the method, clearUserID(), used to clear the User ID field fails to be invoked.
When I do not inherit from the main window class the clearUserID method works and I am able to clear the dialog QLineEdit (UserIDWin_UserID_lnedt) but not the corresponding widget on the main window (UserID_lnedt). All of the code I've tried to clear the main window QLineEdit widget using the dialog "Clear" button caused my program to crash.
Would somebody please help me to better understand the logic behind these principles and how to get my code working? Thank you.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(820, 611)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget.setGeometry(QtCore.QRect(0, 0, 801, 551))
self.tabWidget.setObjectName("tabWidget")
self.MainTab = QtWidgets.QWidget()
self.MainTab.setObjectName("MainTab")
self.UserID_Edit_pb = QtWidgets.QPushButton(self.MainTab)
self.UserID_Edit_pb.setGeometry(QtCore.QRect(210, 10, 31, 23))
self.UserID_Edit_pb.setObjectName("UserID_Edit_pb")
self.UserID_Edit_pb.clicked.connect(self.openUserIDWin)
self.UserID_Clear_pb_2 = QtWidgets.QPushButton(self.MainTab)
self.UserID_Clear_pb_2.setGeometry(QtCore.QRect(170, 9, 41, 23))
self.UserID_Clear_pb_2.setObjectName("UserID_Clear_pb_2")
self.UserID_le = QtWidgets.QLineEdit(self.MainTab)
self.label = QtWidgets.QLabel(self.MainTab)
self.label.setGeometry(QtCore.QRect(10, 10, 47, 13))
self.UserID_le.setGeometry(QtCore.QRect(50, 10, 113, 20))
self.UserID_le.setObjectName("UserID_le")
self.tabWidget.addTab(self.MainTab, "")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 820, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.UserID_Clear_pb_2.clicked.connect(self.UserID_le.clear)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.UserID_Edit_pb.setText(_translate("MainWindow", "Edit"))
self.UserID_Clear_pb_2.setText(_translate("MainWindow", "Clear"))
self.label.setText(_translate("MainWindow", "User ID"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.MainTab), _translate("MainWindow", "Tab"))
def openUserIDWin(self):
UserID_value = self.UserID_le.text()
UserIDWin = QtWidgets.QDialog()
ui = Ui_UserIDWin(UserID_value)
ui.setupUi(UserIDWin)
UserIDWin.exec_();
class Ui_UserIDWin(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, userID):
print("The User ID is:" + userID)
self.userID = userID
def setupUi(self, UserIDWin):
UserIDWin.setObjectName("UserIDWin")
UserIDWin.resize(400, 124)
self.UserIDWin_UserID_lnedt = QtWidgets.QLineEdit(UserIDWin)
self.UserIDWin_UserID_lnedt.setText(self.userID)
self.UserIDWin_UserID_lnedt.setGeometry(QtCore.QRect(20, 50, 113, 20))
self.UserIDWin_UserID_lnedt.setObjectName("UserIDWin_UserID_lnedt")
self.UserIDWin_UserID_lbl = QtWidgets.QLabel(UserIDWin)
self.UserIDWin_UserID_lbl.setGeometry(QtCore.QRect(20, 30, 47, 13))
self.UserIDWin_UserID_lbl.setObjectName("UserIDWin_UserID_lbl")
self.UserIDWin_UserIDClear_pushb = QtWidgets.QPushButton(UserIDWin)
self.UserIDWin_UserIDClear_pushb.setGeometry(QtCore.QRect(140, 50, 41, 23))
self.UserIDWin_UserIDClear_pushb.setObjectName("UserIDWin_UserIDClear_pushb")
self.UserIDWin_Cancel_pushb = QtWidgets.QPushButton(UserIDWin)
self.UserIDWin_Cancel_pushb.setGeometry(QtCore.QRect(110, 80, 75, 23))
self.UserIDWin_Cancel_pushb.setObjectName("UserIDWin_Cancel_pushb")
self.UserIDWin_Next_pushb = QtWidgets.QPushButton(UserIDWin)
self.UserIDWin_Next_pushb.setGeometry(QtCore.QRect(190, 80, 75, 23))
self.UserIDWin_Next_pushb.setObjectName("UserIDWin_Next_pushb")
self.retranslateUi(UserIDWin)
QtCore.QMetaObject.connectSlotsByName(UserIDWin)
#If I do not inherit from "QtWidgets.QMainWindow, Ui_MainWindow" the code below works and invokes clearUserId(). However, I then am having problems using SetText on the main window UserId_le text box and the program crashes.
self.UserIDWin_UserIDClear_pushb.clicked.connect(self.clearUserID)
def retranslateUi(self, UserIDWin):
_translate = QtCore.QCoreApplication.translate
UserIDWin.setWindowTitle(_translate("UserIDWin", "Dialog"))
self.UserIDWin_UserID_lbl.setText(_translate("UserIDWin", "User ID"))
self.UserIDWin_UserIDClear_pushb.setText(_translate("UserIDWin", "Clear"))
self.UserIDWin_Cancel_pushb.setText(_translate("UserIDWin", "Cancel"))
self.UserIDWin_Next_pushb.setText(_translate("UserIDWin", "Next"))
def clearUserID(self):
self.UserIDWin_UserID_lnedt.setText('')
# The line below crashes my program if I am able to invoke this method.
#self.Ui_MainWindow.UserID_le.setText('')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
It seems that they have certain clear concepts about inheritance and good practices but others do not like the following:
PyQt recommends not modifying the code generated by Qt Designer because you probably want to modify the GUI in the future and when using pyuic the initial code is overwritten. Another problem is that beginners do not understand that the class generated by Qt Designer is not a widget but an interface that serves to fill another widget, and consequently can not overwrite the methods of the widgets in addition to other problems.
You only have to modify the object of a class from the object of another class if both have the same scope, in your case when wanting to clean the QLineEdit of the main window from the other window is a dangerous task, instead you should do that logic where both windows have the same scope and that is in the openUserIDWin method.
QLineEdit already has the clear() method that allows to clean, it fulfills the same function as setText("") but the first method is more readable.
Considering the above, the solution is:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(820, 611)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget.setGeometry(QtCore.QRect(0, 0, 801, 551))
self.tabWidget.setObjectName("tabWidget")
self.MainTab = QtWidgets.QWidget()
self.MainTab.setObjectName("MainTab")
self.UserID_Edit_pb = QtWidgets.QPushButton(self.MainTab)
self.UserID_Edit_pb.setGeometry(QtCore.QRect(210, 10, 31, 23))
self.UserID_Edit_pb.setObjectName("UserID_Edit_pb")
self.UserID_Clear_pb_2 = QtWidgets.QPushButton(self.MainTab)
self.UserID_Clear_pb_2.setGeometry(QtCore.QRect(170, 9, 41, 23))
self.UserID_Clear_pb_2.setObjectName("UserID_Clear_pb_2")
self.UserID_le = QtWidgets.QLineEdit(self.MainTab)
self.label = QtWidgets.QLabel(self.MainTab)
self.label.setGeometry(QtCore.QRect(10, 10, 47, 13))
self.UserID_le.setGeometry(QtCore.QRect(50, 10, 113, 20))
self.UserID_le.setObjectName("UserID_le")
self.tabWidget.addTab(self.MainTab, "")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 820, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.UserID_Edit_pb.setText(_translate("MainWindow", "Edit"))
self.UserID_Clear_pb_2.setText(_translate("MainWindow", "Clear"))
self.label.setText(_translate("MainWindow", "User ID"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.MainTab), _translate("MainWindow", "Tab"))
class Ui_UserIDWin(object):
def setupUi(self, UserIDWin):
UserIDWin.setObjectName("UserIDWin")
UserIDWin.resize(400, 124)
self.UserIDWin_UserID_lnedt = QtWidgets.QLineEdit(UserIDWin)
self.UserIDWin_UserID_lnedt.setGeometry(QtCore.QRect(20, 50, 113, 20))
self.UserIDWin_UserID_lnedt.setObjectName("UserIDWin_UserID_lnedt")
self.UserIDWin_UserID_lbl = QtWidgets.QLabel(UserIDWin)
self.UserIDWin_UserID_lbl.setGeometry(QtCore.QRect(20, 30, 47, 13))
self.UserIDWin_UserID_lbl.setObjectName("UserIDWin_UserID_lbl")
self.UserIDWin_UserIDClear_pushb = QtWidgets.QPushButton(UserIDWin)
self.UserIDWin_UserIDClear_pushb.setGeometry(QtCore.QRect(140, 50, 41, 23))
self.UserIDWin_UserIDClear_pushb.setObjectName("UserIDWin_UserIDClear_pushb")
self.UserIDWin_Cancel_pushb = QtWidgets.QPushButton(UserIDWin)
self.UserIDWin_Cancel_pushb.setGeometry(QtCore.QRect(110, 80, 75, 23))
self.UserIDWin_Cancel_pushb.setObjectName("UserIDWin_Cancel_pushb")
self.UserIDWin_Next_pushb = QtWidgets.QPushButton(UserIDWin)
self.UserIDWin_Next_pushb.setGeometry(QtCore.QRect(190, 80, 75, 23))
self.UserIDWin_Next_pushb.setObjectName("UserIDWin_Next_pushb")
self.retranslateUi(UserIDWin)
QtCore.QMetaObject.connectSlotsByName(UserIDWin)
def retranslateUi(self, UserIDWin):
_translate = QtCore.QCoreApplication.translate
UserIDWin.setWindowTitle(_translate("UserIDWin", "Dialog"))
self.UserIDWin_UserID_lbl.setText(_translate("UserIDWin", "User ID"))
self.UserIDWin_UserIDClear_pushb.setText(_translate("UserIDWin", "Clear"))
self.UserIDWin_Cancel_pushb.setText(_translate("UserIDWin", "Cancel"))
self.UserIDWin_Next_pushb.setText(_translate("UserIDWin", "Next"))
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.UserID_Edit_pb.clicked.connect(self.openUserIDWin)
self.UserID_Clear_pb_2.clicked.connect(self.UserID_le.clear)
def openUserIDWin(self):
UserID_value = self.UserID_le.text()
w = UserIDWin(UserID_value)
w.UserIDWin_UserIDClear_pushb.clicked.connect(self.UserID_le.clear)
w.exec_()
class UserIDWin(QtWidgets.QDialog, Ui_UserIDWin):
def __init__(self, userID, parent=None):
super(UserIDWin, self).__init__(parent)
self.setupUi(self)
self.userID = userID
self.UserIDWin_UserID_lnedt.setText(self.userID)
self.UserIDWin_UserIDClear_pushb.clicked.connect(self.UserIDWin_UserID_lnedt.clear)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I recommend you read:
http://pyqt.sourceforge.net/Docs/PyQt5/designer.html
QtDesigner changes will be lost after redesign User Interface
I'm still trying to figure out PyQt and running into another issue that I've been beating my head over for the last several hours. When I use pyuic5 to convert the .ui file into the .py file, part of the output (in the class Ui_MainWindow) connects signals to slots:
self.browseButton.clicked.connect(MainWindow.browseSlot)
self.importButton.clicked.connect(MainWindow.importSlot)
self.lineEdit.returnPressed.connect(MainWindow.returnPressedSlot)
This is called from the main function:
def main():
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = MainWindowUI()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Above this, in the same file, I have the following code:
class MainWindowUI(Ui_MainWindow):
def __init__(self):
super().__init__()
self.model = Model()
def setupUi(self, mainWindow):
super().setupUi(mainWindow)
def debugPrint(self, msg):
self.textEdit.append(msg)
def refreshAll(self):
self.lineEdit.setText(self.model.getFileName())
self.textEdit.setText(self.model.getFileContents())
def returnPressedSlot(self):
self.debugPrint('Return key pressed')
def importSlot(self):
self.debugPrint('Import button pressed')
def browseSlot(self):
self.debugPrint('Browse button pressed')
The exact error I'm getting is this:
AttributeError: 'QMainWindow' object has no attribute 'browseSlot'
This actually makes perfect sense, because there's no reason QtWidgets.QMainWindow() should know anything about the custom slots I defined in the MainWindowUI class. So it makes sense that it doesn't work, but I'm confused about what I should be doing differently. This is how every tutorial I've seen sets it up, so clearly I have a fundamental misunderstanding somewhere. Any help in clarifying this issue would be appreciated!
Many thanks in advance.
Edited to add the full code of my Ui_MainWindow class:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'test.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(798, 593)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.verticalLayout = QtWidgets.QVBoxLayout(self.tab)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.frame_2 = QtWidgets.QFrame(self.tab)
self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_2.setObjectName("frame_2")
self.debugTextBrowser = QtWidgets.QTextBrowser(self.frame_2)
self.debugTextBrowser.setGeometry(QtCore.QRect(10, 10, 351, 461))
self.debugTextBrowser.setObjectName("debugTextBrowser")
self.horizontalLayout_2.addWidget(self.frame_2)
self.frame = QtWidgets.QFrame(self.tab)
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.browseButton = QtWidgets.QPushButton(self.frame)
self.browseButton.setGeometry(QtCore.QRect(80, 30, 51, 20))
self.browseButton.setObjectName("browseButton")
self.lineEdit = QtWidgets.QLineEdit(self.frame)
self.lineEdit.setGeometry(QtCore.QRect(80, 10, 281, 20))
self.lineEdit.setObjectName("lineEdit")
self.importButton = QtWidgets.QPushButton(self.frame)
self.importButton.setGeometry(QtCore.QRect(310, 30, 51, 20))
self.importButton.setObjectName("importButton")
self.textEdit = QtWidgets.QTextEdit(self.frame)
self.textEdit.setGeometry(QtCore.QRect(80, 130, 281, 81))
self.textEdit.setObjectName("textEdit")
self.horizontalLayout_2.addWidget(self.frame)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.setupProgressBar = QtWidgets.QProgressBar(self.tab)
self.setupProgressBar.setProperty("value", 0)
self.setupProgressBar.setTextVisible(False)
self.setupProgressBar.setInvertedAppearance(False)
self.setupProgressBar.setTextDirection(QtWidgets.QProgressBar.TopToBottom)
self.setupProgressBar.setObjectName("setupProgressBar")
self.verticalLayout.addWidget(self.setupProgressBar)
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = QtWidgets.QWidget()
self.tab_3.setObjectName("tab_3")
self.tabWidget.addTab(self.tab_3, "")
self.tab_4 = QtWidgets.QWidget()
self.tab_4.setObjectName("tab_4")
self.tabWidget.addTab(self.tab_4, "")
self.tab_5 = QtWidgets.QWidget()
self.tab_5.setObjectName("tab_5")
self.tabWidget.addTab(self.tab_5, "")
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 798, 18))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
self.browseButton.clicked.connect(MainWindow.browseSlot)
self.importButton.clicked.connect(MainWindow.importSlot)
self.lineEdit.returnPressed.connect(MainWindow.returnPressedSlot)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.browseButton.setText(_translate("MainWindow", "Browse"))
self.importButton.setText(_translate("MainWindow", "Import"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Setup"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Production Forecast"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Production Forecast"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("MainWindow", "Page"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_5), _translate("MainWindow", "Page"))
PyQt recommends in your docs that you should inherit from the appropriate widget, in this case QMainWindow, and use Ui_MainWindow as an interface, it is also recommended that you use the decorator #QtCore.pyqtSlot() since you save resources and avoid having problems with overloaded signals.
class MainWindowUI(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.model = Model()
def setupUi(self, mainWindow):
super().setupUi(mainWindow)
def debugPrint(self, msg):
self.textEdit.append(msg)
def refreshAll(self):
self.lineEdit.setText(self.model.getFileName())
self.textEdit.setText(self.model.getFileContents())
#QtCore.pyqtSlot()
def returnPressedSlot(self):
self.debugPrint('Return key pressed')
#QtCore.pyqtSlot()
def importSlot(self):
self.debugPrint('Import button pressed')
#QtCore.pyqtSlot()
def browseSlot(self):
self.debugPrint('Browse button pressed')
def main():
app = QtWidgets.QApplication(sys.argv)
w = MainWindowUI()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__': main()
The PyQt 5 Designer generates files with .ui extension. The file contains xml code which can be converted to Python code with the pyuic5 command. The pyuic5 command produces a file with .py extension.
But each time the pyuic5 command is run, it overwrites any changes made to the Python file. To prevent this we need to copy the "main" code in the generated Python file to a separate file and then use this file as our main program file. In this file we can import the Python class generated by the pyuic5 command. We can also add our custom slots in this file
This practice of separating Qt Desginer code from custom code is good Qt programming practice. It has been suggested in Section 3 of the video ebook: "Python GUI Programming Recipes using PyQt5".
"Create a program in PyQt that is aimed towards benefiting a local community library. The program should be able to add, edit and delete book entries to and from the inventory list.
Include one list widget, one line edit box, one label, four push buttons."
I can drag and drop all these things into PyQt5, save it as .ui, convert to .py and open in IDLE, but how do I make the buttons do anything? Also, how do I get my IDLE to display this box?
This is what the program should look like
This is the code I am left with after saving the .ui file and converting to .py:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'listoper.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 450)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(260, 180, 256, 192))
self.listWidget.setObjectName("listWidget")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(10, 230, 171, 91))
self.lineEdit.setObjectName("lineEdit")
self.Enterbookdetails = QtWidgets.QLabel(self.centralwidget)
self.Enterbookdetails.setGeometry(QtCore.QRect(20, 200, 101, 16))
self.Enterbookdetails.setObjectName("Enterbookdetails")
self.Add = QtWidgets.QPushButton(self.centralwidget)
self.Add.setGeometry(QtCore.QRect(10, 330, 75, 23))
self.Add.setObjectName("Add")
self.Delete = QtWidgets.QPushButton(self.centralwidget)
self.Delete.setGeometry(QtCore.QRect(110, 330, 75, 23))
self.Delete.setObjectName("Delete")
self.Edit = QtWidgets.QPushButton(self.centralwidget)
self.Edit.setGeometry(QtCore.QRect(10, 370, 75, 23))
self.Edit.setObjectName("Edit")
self.Deleteall = QtWidgets.QPushButton(self.centralwidget)
self.Deleteall.setGeometry(QtCore.QRect(110, 370, 75, 23))
self.Deleteall.setObjectName("Deleteall")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.Enterbookdetails.setText(_translate("MainWindow", "Enter Book Details:"))
self.Add.setText(_translate("MainWindow", "Add"))
self.Delete.setText(_translate("MainWindow", "Delete"))
self.Edit.setText(_translate("MainWindow", "Edit"))
self.Deleteall.setText(_translate("MainWindow", "Delete All"))
Apparently I am supposed to use the following code, but my buttons still don't work:
The kilogram to pound button:
def btn_KtoP_clicked(self):
kilo = float(self.edt_Kilo.text())
pound = (kilo * 2.2)
self.edt_Pounds.setText(str(pound))
The pound to kilogram button:
def btn_PtoK_clicked(self):
pound = float(self.edt_Pounds.text())
kilo = (pound/2.2)
self.edt_Kilo.setText(str(kilo))
Based on your above code, this should work
import sys
from PyQt5 import QtCore, QtWidgets
from YOUR_UI_MODULE import Ui_MainWindow
class sow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.main_ui=Ui_MainWindow()
self.main_ui.setupUi(self)
self.main_ui.Add.clicked.connect(self.doAdd)
self.main_ui.Edit.clicked.connect(self.doEdit)
def doAdd(self):
print("do add")
def doEdit(self):
print("do edit")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
myapp = sow()
myapp.show()
sys.exit(app.exec_())
checkout the examples which came with your PyQT5 installation and start from there.
mine is located here C:\Python34\Lib\site-packages\PyQt5\examples
PyQT has a reference doc, mine is located here C:/Python32/Lib/site-packages/PyQt4/doc/html/classes.html
I will recommend reading this tutorial before coding further, so you know what you are doing.
http://zetcode.com/gui/pyqt5/firstprograms/