Using PyQt4 + QT4.8.4, I'd like to drop (external) textual content to a widget defined as a PyQt4 plugin in QtDesigner
I use two python classes :
widgetlabelplugin.py inherited from QPyDesignerCustomWidgetPlugin
widgetlabel.py inherited from QLabel
Overriding the dropEvent in (widgetlabel.py), I'm able to retrieve "external textual content" and to set _model property.
I do the following steps :
Launch designer by previously setting PYQTDESIGNERPATH to the
.py path
Create a dialog without button
Drop a PyGMT/ WiddgetLabel on the dialog
Drop a "textual content" (from notepad) to the widgetlabel label
-> at this step, the label is updated on the dialog but not on the properties browser on the right
Save Dialog from Qt designer tool bar
-> ui file doesn't contain any "textual content" neither for QLabel/text, nor WidgetLabel/model
In Qt designer, If I select dialog background and reselect WidgetLabel, properties are updated in the browser, they are still not saved if I save the ui !
python class : widgetlabelplugin.py
# A demonstration custom widget plugin for PROJECT Qt Designer.
from PyQt4 import QtGui, QtDesigner
from widgetlabel import WidgetLabel
# This class implements the interface expected by Qt Designer to access the
# custom widget. See the description of the QDesignerCustomWidgetInterface
# class for full details.
class WidgetLabelPlugin(QtDesigner.QPyDesignerCustomWidgetPlugin):
# Initialise the instance.
def __init__(self, parent=None):
super(WidgetLabelPlugin, self).__init__(parent)
self._initialized = False
# Initialise the custom widget for use with the specified formEditor
# interface.
def initialize(self, formEditor):
if self._initialized:
return
self._initialized = True
# Return True if the custom widget has been intialised.
def isInitialized(self):
return self._initialized
# Return a new instance of the custom widget with the given parent.
def createWidget(self, parent):
return WidgetLabel(parent)
# Return the name of the class that implements the custom widget.
def name(self):
return "WidgetLabel"
# Return the name of the group to which the custom widget belongs. A new
# group will be created if it doesn't already exist.
def group(self):
return "PyGMT"
# Return the icon used to represent the custom widget in Designer's widget
# box.
def icon(self):
return QtGui.QIcon(":/designer/frame.png")
# Return a short description of the custom widget used by Designer in a
# tool tip.
def toolTip(self):
return "Satis demonstration widget"
# Return a full description of the custom widget used by Designer in
# "What's This?" help for the widget.
def whatsThis(self):
return "WidgetLabel is a demonstration custom widget written in Python " \
"using PyQt."
# Return True if the custom widget acts as a container for other widgets.
def isContainer(self):
return False
# Return the name of the module containing the class that implements the
# custom widget. It may include a module path.
def includeFile(self):
return "WidgetLabel"
python class : widgetlabel.py
#############################################################################
##
## This file is part of the examples of PROJECT Qt Designer.
##
#############################################################################
from PyQt4 import QtCore, QtGui
# This is the class that implements the custom widget for PROJECT.
class WidgetLabel(QtGui.QLabel):
#model changed signal
modelChanged = QtCore.pyqtSignal(QtCore.QString)
# Initialise the instance.
def __init__(self, parent=None):
super(WidgetLabel, self).__init__(parent)
self.setAcceptDrops(True)
self.setText("Label")
# Initialise the model property.
self._model = None
###########################################################################
# DRAG & DROP PART #
###########################################################################
def dragEnterEvent(self, e):
if e.mimeData().hasFormat("text/plain"):
e.setDropAction(QtCore.Qt.CopyAction)
e.accept()
else:
e.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat("text/plain"):
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
event.ignore
def dropEvent(self, e):
e.acceptProposedAction()
bStream = e.mimeData().retrieveData("text/plain",
QtCore.QVariant.ByteArray)
self.setModel(QtCore.QString(str(bStream.toByteArray())))
# The getter for the zoom property.
def getModel(self):
return self._model
# The setter for the model property. We also make define this as a Qt slot
# which can be connected to Qt signals in Qt Designer.
#QtCore.pyqtSlot(QtCore.QString)
def setModel(self, model):
# print "new model", model
# Set QLabel text
self.setText(model)
# Don't do anything if nothing has changed.
if self._model == model:
return
# Remember the new model level.
self._model = model
# Emit the Qt signal to say that the model level has changed.
self.modelChanged.emit(model)
# The resetter for the model property.
def resetModel(self):
self.setModel(None)
# Define the model property. Changing the value of this in Qt Designer's
# property editor causes the model to change dynamically.
model = QtCore.pyqtProperty(QtCore.QString, getModel, setModel, resetModel)
# Display the custom widget if the script is being run directly from the
# command line.
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
demo = WidgetLabel()
demo.show()
sys.exit(app.exec_())
Related
I'm using Qt Designer for design GUI to use in python, after designing my desired UI in Qt Designer, convert it to python code and then I changed generated code to do some action in my python code, but if I changed the UI with Qt Designer and convert it to python code again, I lost my previous changes on my code.
how can I solve the problem?
can we Spreading a Class Over Multiple Files in python to write code in other files?
To avoid having these problems it is advisable not to modify this file but to create a new file where we implement a class that uses that design.
For example, suppose you have used the MainWindow template in the design.ui file, then convert it to Ui_Design.py like to the following structure:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
[...]
def retranslateUi(self, MainWindow):
[...]
Then we will create a new file that we will call logic.py where we will create the file that handles the logic and that uses the previous design:
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
So even if you modify the design and generate the file again .py you will not have to modify the file of the logic.
To generalize the idea we must have the following rules but for this the logic class must have the following structure:
class Logic(PyQtClass, DesignClass):
def __init__(self, *args, **kwargs):
PyQtClass.__init__(self, *args, **kwargs)
self.setupUi(self)
PyQtClass: This class depends on the design chosen.
Template PyQtClass
─────────────────────────────────────────────
Main Window QMainWindow
Widget QWidget
Dialog with Buttons Bottom QDialog
Dialog with Buttons Right QDialog
Dialog with Without Buttons QDialog
DesignClass: The name of the class that appears in your design.
The advantage of this implementation is that you can implement all the logic since it is a widget, for example we will implement the solution closing pyqt messageBox with closeevent of the parent window :
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
def closeEvent(self, event):
answer = QtWidgets.QMessageBox.question(
self,
'Are you sure you want to quit ?',
'Task is in progress !',
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No)
if answer == QtWidgets.QMessageBox.Yes:
event.accept()
else:
event.ignore()
The easiest way is to use the *.ui file directly in the python code, you don't need convert to *.py file every time you change the ui.
you can use this pseudo code in your project.
# imports
from PyQt5 import uic
# load ui file
baseUIClass, baseUIWidget = uic.loadUiType("MainGui.ui")
# use loaded ui file in the logic class
class Logic(baseUIWidget, baseUIClass):
def __init__(self, parent=None):
super(Logic, self).__init__(parent)
self.setupUi(self)
.
.
.
.
def main():
app = QtWidgets.QApplication(sys.argv)
ui = Logic(None)
ui.showMaximized()
sys.exit(app.exec_())
I have a QLineEdit widget and I want to use the double click event on it. How can I do that?
def __init__(self):
#... other codes
self.title = QLineEdit()
self.title.returnPressed.connect(self.lockTitle)
#like this -> 'self.title.doubleClicked.connect(self.unlockTitle)'
#... other codes
def lockTitle(self):
self.title.setDisabled(True)
def unlockTitle(self):
self.title.setDisabled(False)
A possible solution is to create a custom QLineEdit by creating a new signal that is emitted in the mouseDoubleClickEvent method, but the problem in your case is that the QLineEdit is disabled and that method is not invoked so instead of using that method you should use the event method:
class LineEdit(QLineEdit):
doubleClicked = pyqtSignal()
def event(self, event):
if event.type() == QEvent.Type.MouseButtonDblClick:
self.doubleClicked.emit()
return super().event(event)
self.title = LineEdit()
self.title.returnPressed.connect(self.lockTitle)
self.title.doubleClicked.connect(self.unlockTitle)
I'm writing a custom TableModel in PyQt5, inheriting from QtCore.QAbstractTableModel. I want my Table to have one column with CheckBoxes only, no text, and one column with a PushButton in each row.
I tried to return a QPushButton object in the data method for the Qt.Display role, but apparently this is not possible.
So my question is: Can I implement in the Model itself that it returns certain widgets for certain cells? In my opinion, that is the job of the model, but how do I achieve this?
My second question would be how I have to assign the slots so that I know from which of the buttons (from which row) the action occurred?
Explanation:
Can I implement in the Model itself that it returns certain widgets for certain cells? In my opinion, that is the job of the model, but how do I achieve this?
It could be that the model provides the widgets but it is not common. In general, the model provides the information that will be displayed through a delegate, and is the delegate that can provide widgets. There is also the alternative of setting widgets on the unlinked view of the model using the setIndexWidget method.
Considering the first case, the solution is to implement a delegate.
how I have to assign the slots so that I know from which of the buttons (from which row) the action occurred?
In general the widgets that are used as editors so you must update the model, and then the model can notify other elements through the dataChanged signal as for example if the user wrote a text or changed the status of a checkbox, but in the case of the clicked not notify a permanent change but temporary. Considering this, the delegate could present a signal.
Solution:
from PyQt5 import QtCore, QtGui, QtWidgets
class PushButtonDelegate(QtWidgets.QStyledItemDelegate):
clicked = QtCore.pyqtSignal(QtCore.QModelIndex)
def paint(self, painter, option, index):
if (
isinstance(self.parent(), QtWidgets.QAbstractItemView)
and self.parent().model() is index.model()
):
self.parent().openPersistentEditor(index)
def createEditor(self, parent, option, index):
button = QtWidgets.QPushButton(parent)
button.clicked.connect(lambda *args, ix=index: self.clicked.emit(ix))
return button
def setEditorData(self, editor, index):
editor.setText(index.data(QtCore.Qt.DisplayRole))
def setModelData(self, editor, model, index):
pass
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QTableView()
model = QtGui.QStandardItemModel(0, 2)
for i in range(10):
it1 = QtGui.QStandardItem()
it1.setData(QtCore.Qt.Checked, QtCore.Qt.CheckStateRole)
it1.setFlags(it1.flags() | QtCore.Qt.ItemIsUserCheckable)
it2 = QtGui.QStandardItem()
it2.setData("text-{}".format(i), QtCore.Qt.DisplayRole)
model.appendRow([it1, it2])
# pass the view as parent
delegate = PushButtonDelegate(w)
w.setItemDelegateForColumn(1, delegate)
def on_clicked(index):
print(index.data())
delegate.clicked.connect(on_clicked)
w.setModel(model)
w.show()
sys.exit(app.exec_())
I'm tired about looking for the methods to deal with widgets within python class (Python 2.7 & PyQt4) who load file.ui (GUI QTDesigner)
CODE
form_class = uic.loadUiType("MyPythonProgram.ui")[0]
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.btn_buscar.clicked.connect(self.buscar)
def addingResultsToQListView(self):
for item in SomeList:
self.listView.addItem(item) ###It's not correct, but cannot find the right one
def onListItemClicked():
getItem = listView.currentItem().text() ###It's not correct, but cannot find the right one
def buscar(self):
getEditText = self.textEdit.toPlainText()
### Don't know how to do this function. I want to get the edittext to search on some website and retrieve the results into a list. then the list will be added to QlistView (just found C++ methods, not for python)
#Finally
getEditText = '' ###After click on 'btn_buscar', want to clear this field
app = QtGui.QApplication(sys.argv)
MyWindow = MyWindowClass(None)
MyWindow.show()
app.exec_()
It could be helper to get some DOC, or some help about making python apps hybrid (Android if its possible), keeping .ui and .py layers separately as I'm trying to show you.
This is my .ui for more information:
QUESTION'
How could I bind python functions with elements on .ui? I was trying too much methods but didn't find the right one. Need to know how to deal with QlistView and Qedittext... Thanks
This is the way I do it: separate the classes between construction (loading) of the UI and changing its content.
form_class = uic.loadUiType("MyPythonProgram.ui")[0]
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
class myGui:
def __init__(self):
self.gui = MyWindowClass() # that's the trick!
# self.addindResultsToQListView() # doesn't work, because I don't have your list items
self.gui.btn_buscar.clicked.connect(self.buscar)
self.editText = None
def show(self):
self.gui.show()
def addingResultsToQListView(self):
for item in SomeList: # you need to specify this `list` before this works!
self.gui.listView.addItems(item)
def buscar(self):
self.editText = self.gui.textEdit.text()
self.gui.textEdit.setText("")
app = QtGui.QApplication(sys.argv)
MyWindow = MyGui()
MyWindow.show()
app.exec_()
The trick is to reference the MyWindowClass, which is the constructor of your gui and hence the GUI itself, as an object within the class that controls the content of your GUI (myGui).
You call myGui on toplevel which then calls MyWindowClass as the object self.gui. From then on, whenever you want to address something in your GUI you name it self.gui. and add the QObject.
I also tried to understand what you want to do for the pushBotton. The content of your TextEdit (in PyQt they are called lineEdit btw) is stored in the variable self.editText which is initialized as None. Afterwards, the lineEdit is cleared from the user content.
I have created to .ui files using QtDesigner and I load them into two seperate windows as show below
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__()
# Set up the user interface from Designer.
uic.loadUi("interface/UI/main.ui", self)
# Connect up the buttons
self.button_classes.clicked.connect(self.open_classes)
self.w = []
def open_classes(self):
self.w.append(PopupWindow(self))
self.w[-1].show()
class PopupWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__()
# Set up the user interface from Designer.
uic.loadUi("interface/UI/newclass.ui", self)
When I run the code in PyCharm in debug mode, the following error occurs, however this does not happen when the code is run normally
TypeError: ('Wrong base class of toplevel widget', (<class 'controllers.GUI.PopupWindow'>, 'QDialog'))
You have QDialog in message 'Wrong base class of toplevel widget', (<class 'controllers.GUI.NewClassWindow'>, 'QDialog') so I think it expects QDialog to create second window but you use QMainWindow in class PopupWindowONE(QMainWindow):
In other words, check the class type of the .ui file you are going to initiate; if the class is a QDialog then your python class needs to receive a QDialog.
I had a similar problem when using QDialog but changed it to QMainWindow and it worked.