I have this: (there are class methods, which inherience from QWizard)
def getForms(self):
return [
(
QtWidgets.QLabel("Name"),
QtWidgets.QLineEdit()
),
(
QtWidgets.QLabel("Roll"),
QtWidgets.QDoubleSpinBox()
)
]
def registerFields(self, page, forms):
page.registerField("name*", forms[0][1])
page.registerField("roll", forms[1][1])
And in other place in code
id = self.currentId()
if id == 1:
print self.field("name") # this rightly give me a name from LineEdit
print self.field("roll") # but this give me just None, why?
When I changed
QtWidgets.QDoubleSpinBox()
to
QtWidgets.QSpinBox()
Line:
print self.field("roll")
works fine.
Why do I get None instead double value?
EDIT
I've just noticed that when I'm trying make 'roll' field as a mandatory.
page.registerField("roll*", forms[1][1])
And I fill this 'spinbox' in program, I can not click 'next' (next is disabled). I have spinbox in my form in program. I can set the value there. But this looks like this field is not connected with QWizard(?)?
The QWizardPage class only has internal knowledge of a few widget types. When registering a widget it does not know about, you need to specify the property for reading the value, along with the signal that is emitted when a value is changed, as a string.
For QDoubleSpinBox this would be:
page.registerField("roll", forms[1][1], "value", "valueChanged")
The list of widget types QWizardPage knows about is listed in the c++ documentation here.
You can also register this information globally using a method of QWizard, so that you don't have to specify it each time you call registerField(). To do this, call:
my_wizard.setDefaultProperty("QDoubleSpinBox", "value", "valueChanged")
Note: This is a method of the wizard, not the page.
Related
I have a QTreeView widget on a QWizardPage (say, qwpage2) where the flags for one of the columns of information depends on the state of a QRadioButton on another QWizardPage (say, qwpage1).
The original properties of the treeview column are set in the initializePage method of qwpage2. If I Next > through the pages, everything works fine. However, if I < Back from qwpage2, change the radiobutton on qwpage1, and Next > to qwpage2, the flags are not updated.
I have something like the following (in PyQt5) on qwpage1:
rbtn1 = QRadioButton()
rbtn2 = QRadioButton()
self.registerField ("remove_checkbox", rbtn1)
self.registerField ("add_checkbox", rbtn2)
self.checked_choice.addButton(rbtn1)
self.checked_choice.addButton(rbtn2)
and on qwpage2
add_checkbox = self.field("add_checkbox")
p = QStandardItem()
if add_checkbox:
p.setFlags(p.flags() | Qt.ItemIsUserCheckable)
else:
p.setFlags(Qt.NoItemFlags)
How can I change the flag state (essentially the presence of checkboxes) associated with my treeview column if the page has been initialized previously?
Thank you for your help
The problem is not about whenever you go back to the page after going backward from the next one, as initializePage() is always called, no matter if the "previous" page was the previous or the next in the page index (which makes sense, as page order doesn't always have to follow the "plain index order", since it's returned from QWizard.nextId(), which by default calls QWizardPage.nextId() of the current page and could even return an index that's less than the current).
That said, there's one case for which initializePage() is only called once, and that happens if you set the QWizard.IndependentPages option. If you really need to use that, I think that the only alternative is to set values by overriding the page's showEvent(event), and only if not event.spontaneous() (otherwise it will always process everything even when the page is shown again after being minimized).
What really matters here is that the checkbox of an item delegate is usually shown if a Qt.CheckStateRole is set for its index, because setting Qt.ItemIsUserCheckable only means that the user can set the item state, not that the checkbox is visible.
In fact, unless some specific OS/QtStyle comes in action, setting that flag won't have any effect at all, and even if that happens, disabling it once the check state is set to any of the three states (Unchecked, PartiallyChecked or Checked) won't make any difference: it will be shown anyway.
While this might seem a bit counterintuitive, its behavior is clear from the source code, where the StyleOptionViewItem feature HasCheckIndicator is set to True when and only the data(role=CheckStateRole) is not "Null", as in None for Python.
Slighlty unrelated note: be aware that if you're using more advanced models (such as QSql ones), an "unset" value (as in a "Null" QVariant, eg. the field has no data set) is not always Python's None, but a "QPyNullVariant".
Considering the aforementioned notions, you should set the model and its items in the __init__ of the QWizardPage, then use initializePage only to set its flags.
class Page2(QtWidgets.QWizardPage):
def __init__(self, parent=None):
QtWidgets.QWizardPage.__init__(self, parent)
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.tree = QtWidgets.QTreeView()
layout.addWidget(self.tree)
self.model = QtGui.QStandardItemModel()
self.tree.setModel(self.model)
self.model.dataChanged.connect(self.setCurrentState)
self.addCheckItem = QtGui.QStandardItem('item')
self.model.appendRow(self.addCheckItem)
# remember the default flags
self.defaultFlags = self.addCheckItem.flags()
# set the current "add_checkbox" value to None, which means that it
# has *no* state set at all, not even an Unchecked one
self.currentState = None
def setCurrentState(self, topLeft, bottomRight):
# remember the new check state
self.currentState = self.addCheckItem.checkState()
def initializePage(self):
if self.field('add_checkbox'):
# apply the new flags to allow the user to set the check state
self.addCheckItem.setFlags(
self.defaultFlags | QtCore.Qt.ItemIsUserCheckable)
# set the state if it has been previously set
if self.currentState is None:
self.addCheckItem.setCheckState(QtCore.Qt.Unchecked)
else:
self.addCheckItem.setCheckState(self.currentState)
else:
# prevent notifying setCurrentState() slot that we're changing the
# value, while still remembering the check state;
# note that blogking model signals is not a good practice, as it
# prevents the view to receive model changes, which usually results
# in painting, size, scrolling and mouse interaction issues, but we
# can ignore that in this case, since those changes are only taken
# into account once the view is shown, assuming that the view will
# update once it will be shown, and that will only happen *after*
# initializePage returns
self.model.blockSignals(True)
self.addCheckItem.setData(None, QtCore.Qt.CheckStateRole)
self.model.blockSignals(False)
I would like to replace a dialog's object dynamically but didn't find a way. The dialog is created with the help of a xrc definition file.
All what I found is https://www.blog.pythonlibrary.org/2012/05/05/wxpython-adding-and-removing-widgets-dynamically/: needs to access a sizer, but xrc does not provide access to sizer objects as far as I know.
Can anybody help?
My system: python 2.7, wxpython 3.0.2.0, win7
Best regards
Humbalan
With the help of Goyo's post I found the following solution:
def exchange_control( self, control, coded ) :
"""
Exchanges a wx.TextCtrl (where wx.TE_PASSWORD is set) with a wx.TextCtrl (where
wx.TE_PASSWORD is not set) and vice versa. A similar code could be used to exchange
for exampe a DirPickerCtrl with a FilePickerCtrl.
:param wx.TextCtrl control: Contains a coded or encoded text dependend on `coded`.
:param bool coded: True if the text in `control` is coded, False if it is encoded.
:returns:
A new wx.TextCtrl with the coded (`coded` was False) or encoded Text (`coded` was
True) of `control`.
:rtype: wx.TextCtrl
"""
containing_sizer = control.GetContainingSizer() # get the sizer containing `control`
# Compute arguments for a new TextCtrl.
# - The value of style is arbitrary except the TE_PASSWORD part.
# - a_validator is a validator checking correctness of the value of `control`, is
# optional.
kwargs = { 'parent' : control.Parent,
'id' : wx.ID_ANY,
'value' : control.GetValue(),
'size' : control.Size,
'style' : wx.TE_NO_VSCROLL|wx.TE_PASSWORD if coded else wx.TE_NO_VSCROLL,
'name' : control.Name,
'validator': a_validator
}
# Setup a new TextCtrl with the arguments kwargs
new_text_ctrl = wx.TextCtrl( **kwargs )
containing_sizer.Replace( control, new_text_ctrl )
containing_sizer.Layout()
return new_text_ctrl
# In the calling code:
# The returned `the_text_ctrl` is the TextCtrl Field with the (en)coded Text and `coded`
# is the boolean value which shows if the text whithin this field is coded or encoded.
the_text_ctrl = self.exchange_control( the_text_ctrl, coded )
EDIT:
I changed the code since I found out that the method Replace() is much easier to use than my first attempt.
i'm making a sort of android lock thing on kivy, and to draw the line, I need to get the id of the widget the mouse is on, so I assing an id to each one like this in the .kv file:
ClickableImage:
id: one
source: 'button.png'
etc.
and I know I can get all the ids (I have 9, of course), with the
self.parent.ids.id
or
self.parent.ids['id']
but is there a way to get the ID the mouse is in? or the one I click? I have a hoverable class so it detects when it enters in a Widget, but I don't really know how to get its position, or change its source.
Is there any:
self.parten.ids.current
or something like that?
thanks for the help
You can use collide_widget or collide_point and in the widget set a method that will change a variable in the parent, let's say selected_widget to the current widget's like this:
if self.collide_point(*Window.mouse_pos):
self.parent.selected_widget = self # or its id
Then you can do with it anything. Maybe it'd be even better to put your logic into the widget itself and handle collision directly there. Obviously you'll need to bind a method you create with that if block above to an event such as on_release or on_press so run the method, otherwise it won't do a thing.
You can also get a hoverable behavior from this PR or even from this snippet.
Edit:
Please note that the id will not be available in the widget instance
Which means self.ids.my_id.id == None and therefore to actually get id you need to do this:
def find(self, parent, widget):
for id, obj in parent.ids.items():
if obj == widget:
print id
return id
I am trying to code the following: Two Columns. One contains a itemId, the other one contains a typeId. I want to render the itemId only when the typeId equals a specific value.
class IDRenderer(gtk.CellRendererText):
def __init__(self):
gtk.CellRendererText.__init__(self)
def do_render(self,window, widget, background_area, cell_area, expose_area, flags):
if ----} Condition to ask for value of the typeId - Cell {-----:
gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area,
expose_area, flags)
gobject.type_register(IDRenderer)
I don't know how to get the iter of the currently rendered row which i need to determine the value of the typeId. Is this even possible?
I now found out, thanks to a nice guy on #pygtk on gimpIRC:
You can do that, with binding so called cell data functions to the corresponding gtk.TreeViewColumn as done here in this example
def renderId(celllayout, cell, model, iter):
if model.get_value(iter,1) == 3:
cell.set_property('visible',True)
else:
cell.set_property('visible',False)
treeviewcolumn = gtk.TreeViewColumn()
renderer = gtk.CellRendererText()
treeviewcolumn.add_attribute(renderer,'text',0)
treeviewcolumn.set_cell_data_func(renderer,renderId)
I ommited some code relevant to render a complete treeview, but i think it shows what i wanted to do and how to do it.
The column renderes the value in the first column (0) of the model only if the value in the second modelcolumn (1) equals 3
I hope this could help someone some time.
It's not possible as far as I know. You need to use properties of the custom renderer which will be set automatically by the code calling the rendering function. (Like the text property of CellRendererText -- the rendering code doesn't get the text from the tree model, but the tree model sets the text property of the renderer before calling the rendering code.)
I need to be able to know what item I've clicked in a dynamically generated menu system. I only want to know what I've clicked on, even if it's simply a string representation.
def populateShotInfoMenus(self):
self.menuFilms = QMenu()
films = self.getList()
for film in films:
menuItem_Film = self.menuFilms.addAction(film)
self.connect(menuItem_Film, SIGNAL('triggered()'), self.onFilmSet)
self.menuFilms.addAction(menuItem_Film)
def onFilmRightClick(self, value):
self.menuFilms.exec_(self.group1_inputFilm.mapToGlobal(value))
def onFilmSet(self, value):
print 'Menu Clicked ', value
Instead of using onFilmSet directly as the receiver of your connection, use a lambda function so you can pass additional parameters:
receiver = lambda film=film: self.onFilmSet(self, film)
self.connect(menuItem_Film, SIGNAL('triggered()'), receiver)
I found this answer here for dealing with this issue in PyQt5, python3. I don't like it, the bVal variable to be precise, as I don't fully understand it but it took a long time to find so I thought I'd share it here. The bVal picks up the boolean value from triggered and allows the taskType to be passed.
self.taskMenu = QtGui.QMenu("Task")
self.tasks = self.getTasks() #FETCHES A LIST OF LIST
self.menuTasks = QtGui.QMenu()
for item in self.tasks:
menuItem_Task = self.taskMenu.addAction(item[1])
receiver = lambda: bVal, taskType=item: self.setTask(bVal, taskType)
menuItem_Task.triggered.connect(receiver)
self.taskMenu.addAction(menuItem_Task)
def setTask(self, ignore_bVal, taskType):
print taskType
Take a look at the Qt's property system. You can dynamically add a property containing a string or anything you desire, which defines the action. Then you can use sender() method in the slot to obtain the QObject calling the slot. Then, query the property you set and do whatever you want accordingly.
But, this is not the best method to do this. Using sender() is not advised because it violates the object oriented principle of modularity.
The best method would be using QSignalMapper class. This class maps signals from different objects to the same slot with different arguments.
I haven't used PyQt therefore i cannot give you exact syntax or an example, but it shouldn't be hard to find with a little research.
I was trying to figure out a similar issue and after looking at the code above this is what worked for me. Thought it would be good to show it all together. =)
self.taskMenu = QtGui.QMenu("Task")
self.tasks = self.getTasks() #FETCHES A LIST OF LIST
self.menuTasks = QtGui.QMenu()
for item in self.tasks:
menuItem_Task = self.taskMenu.addAction(item[1])
receiver = lambda taskType=item[0]: self.setTask(taskType)
self.connect(menuItem_Task, QtCore.SIGNAL('triggered()'), receiver)
self.taskMenu.addAction(menuItem_Task)
def setTask(self,taskType):
print taskType
Just some additional information,
I don't know why, but lambda function doesn't work with new pyqt syntax for connections :
Example of code not working :
self.contextTreeMenuAssignTo = QtGui.QMenu(self)
actionAssign = contextMenu.addMenu( self.contextTreeMenuAssignTo )
actionAssign.setText("Assign to : ")
for user in self.whoCanBeAssignated() :
actionAssignTo = QtGui.QAction( user[0] ,self)
self.contextTreeMenuAssignTo.addAction( actionAssignTo )
actionAssignTo.triggered.connect( lambda userID = user[1] : self.assignAllTo( userID ) )
But if you subsitute the last line with the old style connection syntax :
self.connect(actionAssignTo, QtCore.SIGNAL('triggered()'), lambda userID = user[1] : self.assignAllTo( userID ) )
Everything is fine.
With the new connection syntax, you only get the last element of the loop :(