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.
Related
Following the examples outlined in "Create simple GUI" I have tried to create Custom Widget, but nothing seems to by shown. It seems to be the simplest widget that I can imagine but still something is missing and I have no idea what.
from PyQt5.QtWidgets import *
import sys
class customWidget(QWidget):
def __init__(self, *args, **kwargs):
super(customWidget, self).__init__(*args, **kwargs)
layout = QHBoxLayout()
label = QLabel("Que chinga")
layout.addWidget(label)
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("Esta locura")
label = customWidget()
self.setCentralWidget(label)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Do any of the following in the __init__ of customWidget (they are the conceptually the same thing):
add self.setLayout(layout) after creating the layout;
change layout = QHBoxLayout() to layout = QHBoxLayout(self);
The reason is that you're not setting the layout for the widget, so the QLabel has no parent and won't be shown on its own.
In practice, the customWidget instance could know anything about those objects, so there's no reason for which it should show them.
I also suggest you to:
read about classes and instances: while it's not directly related to this issue, it has lots of aspects in common, since the parenthood relation of Qt objects (such as QWidget subclasses) is closely related to the OOP aspects of instances.
always use capitalized names for classes (CustomWidget, not customWidget), as lower cased names should only be used for variable and function names.
I have a moderately complex GUI that I'm building for interacting with and observing some simulations. I would like to be able to continue to refactor and add features as the project progresses. For this reason, I would like as loose as possible a coupling between different widgets in the application.
My application is structured something like this:
import tkinter as tk
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance_a = ClassA(self)
self.instance_b = ClassB(self)
# ... #
class ClassA(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# ... #
class ClassB(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# ... #
def main():
application = Application()
application.mainloop()
if __name__ == '__main__':
main()
I would like to be able to perform some action in one widget (such as selecting an item in a Treeview widget or clicking on part of a canvas) which changes the state of the other widget.
One way to do this is to have the following code in class A:
self.bind('<<SomeEvent>>', self.master.instance_b.callback())
With the accompanying code in class B:
def callback(self): print('The more that things change, ')
The problem that I have with this approach is that class A has to know about class B. Since the project is still a prototype, I'm changing things all the time and I want to be able to rename callback to something else, or get rid of widgets belonging to class B entirely, or make instance_a a child of some PanedWindow object (in which case master needs to be replaced by winfo_toplevel()).
Another approach is to put a method inside the application class which is called whenever some event is triggered:
class Application(tk.Tk):
# ... #
def application_callback():
self.instance_b.callback()
and modify the bound event in class A:
self.bind('<<SomeEvent>>', self.master.application_callback())
This is definitely easier to maintain, but requires more code. It also requires the application class to know about the methods implemented in class B and where instance_b is located in the hierarchy of widgets. In a perfect world, I would like to be able to do something like this:
# in class A:
self.bind('<<SomeEvent>>', lambda _: self.event_generate('<<AnotherEvent>>'))
# in class B:
self.bind('<<AnotherEvent>>', callback)
That way, if I perform an action in one widget, the second widget would automatically know to to respond in some way without either widget knowing about the implementation details of the other. After some testing and head-scratching, I came to the conclusion that this kind of behavior is impossible using tkinter's events system. So, here are my questions:
Is this desired behavior really impossible?
Is it even a good idea?
Is there a better way of achieving the degree of modularity that I want?
What modules/tools can I use in place of tkinter's built-in event system?
My code in answer avoids the issue of class A having to know about internals of class B by calling methods of a handler object. In the following code methods in class Scanner do not need to know about the internals of a ScanWindow instance. The instance of a Scanner class contains a reference to an instance of a handler class, and communicates with the instance of ScannerWindow through the methods of Handler class.
# this class could be replaced with a class inheriting
# a Tkinter widget, threading is not necessary
class Scanner(object):
def __init__(self, handler, *args, **kw):
self.thread = threading.Thread(target=self.run)
self.handler = handler
def run(self):
while True:
if self.handler.need_stop():
break
img = self.cam.read()
self.handler.send_frame(img)
class ScanWindow(tk.Toplevel):
def __init__(self, parent, *args, **kw):
tk.Toplevel.__init__(self, master=parent, *args, **kw)
# a reference to parent widget if virtual events are to be sent
self.parent = parent
self.lock = threading.Lock()
self.stop_event = threading.Event()
self.frames = []
def start(self):
class Handler(object):
# note self and self_ are different
# self refers to the instance of ScanWindow
def need_stop(self_):
return self.stop_event.is_set()
def send_frame(self_, frame):
self.lock.acquire(True)
self.frames.append(frame)
self.lock.release()
# send an event to another widget
# self.parent.event_generate('<<ScannerFrame>>', when='tail')
def send_symbol(self_, data):
self.lock.acquire(True)
self.symbols.append(data)
self.lock.release()
# send an event to another widget
# self.parent.event_generate('<<ScannerSymbol>>', when='tail')
self.stop_event.clear()
self.scanner = Scanner(Handler())
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_())
I am designing a gui using python Tkinter. I try to wrap my code in classes. I use different class for each frame. The code is more or less like this.
Class GetEntry():
"""This class will acquire the text in entry widget"""
Class Frame1():
"""Consist of all entry widget"""
Class Frame2():
"""Consist of all button widget"""
Class Main_App()
"""All classes are called here"""
However, I want to call GetEntry class when one of the button in class Frame2 is clicked to acquire the text in entry widget located in class Frame1. Any suggestion to do that?
Thanks in advance.
I Finally get it works. I simply instantiate the GetEntry command inside Frame2, and add attribute entry from Frame1 when calling Frame2. My code is as follows.
class CommadCallback():
def __init__(self, object)
self.object = object
def function(self):
self.object.get()
class Frame1():
#Entry instantiation
class Frame2():
def __init__(self, object):
self.object = object
self.function1()
def function1(self):
self.function = CommandCallback(self.object).function()
Class MainApp():
.....
self.frame1 = Frame1(self)
self.frame2 = Frame2(self, self.frame1.entry)
I have an instance..
groupCell = QtGui.QGroupBox()
print groupCell.title() #this class has a method title()
I am not able to change anything of this instance, it comes how it is...
I need to extend this instance (add some methods etc.)
class GroupBoxWithCheckbox (QtGui.QGroupBox):
def __init__(self, basegroupbox, checkbox):
#something like self = basegroupbox ?
self.checkbox = checkbox
def method(self):
pass
and finally
groupCellWithCheckBox = GroupBoxWithCheckbox(groupCell, checkbox)
print groupCellWithCheckBox.title()
I have to get the same title as with groupCell.
You can define a new class extending QtGui.QGroupBox that looks like this:
class GroupBoxWithCheckbox(QtGui.QGroupBox):
def __init__(self, checkbox):
super(GroupBoxWithCheckbox, self).__init__()
self.checkbox = checkbox
def method(self):
pass
Then you can simply make groupCell an instance of this class, and pass in a checkbox when you initialise it:
groupCell = GroupBoxWithCheckbox(checkbox)
That will have the same effect as what you are trying to do here.
Edit, since new information has been provided:
Since we're talking Python here, you can dynamically add things to any instance you want. It's totally possible to do this:
groupCell.checkbox = checkbox
Even if the groupCell doesn't have a checkbox property. The property will be added when you set it, as in my snippet above. You could use that to do what you want. It's kind of a weird thing to do, and I don't recommend it, but it would work. The alternative is to make a wrapper class of some sort:
class GroupBoxWithCheckbox(object):
def __init__(self, groupbox, checkbox):
self.groupbox = groupbox
self.checkbox = checkbox
groupCell = GroupBoxWithCheckbox(groupCell, checkbox)
And then any time you want to access a method of the original GroupBox, you can do something like
groupCell.groupbox.title()
groupCell.groupbox will contain all of the methods that the original GroupBox did, but you'll also have access to groupCell.checkbox.
The latter solution is what I would implement if I were coding this.
Call base class constructor using super:
class GroupBoxWithCheckbox(QtGui.QGroupBox):
def __init__(self, basegroupbox, checkbox):
super(GroupBoxWithCheckbox, self).__init__()
self.checkbox = checkbox
self.basegroupbox = basegroupbox
def title(self):
return self.basegroupbox.title()