I would like to display a ComboBox in my TreeView that contains some Icons. So I created a ListStore to hold the data.
# Initialize a list store for the combobox.
priorityModel = Gtk.ListStore(GObject.TYPE_INT, GdkPixbuf.Pixbuf)
priorityModel.append([0, self.loadPixbuf('./data/media/flag_blue.png')])
priorityModel.append([1, self.loadPixbuf('./data/media/flag_green.png')])
priorityModel.append([2, self.loadPixbuf('./data/media/flag_yellow.png')])
priorityModel.append([3, self.loadPixbuf('./data/media/flag_red.png')])
Then I created the Gtk.CellRendererCombo object and assigned the above Gtk.ListStore as model.
# Setup the priority cell renderer, ...
self.priorityRenderer = Gtk.CellRendererCombo()
self.priorityRenderer.set_property( 'editable', True )
self.priorityRenderer.set_property("model", priorityModel)
self.priorityRenderer.connect("edited", self.on_priority_changed, self.listStore, 3)
Finally I created a new Gtk.TreeViewColumn and assigned the CellRenderer and added it to the TreeView.
# ... setup the priority column ...
self.colPriority = Gtk.TreeViewColumn("Priority", self.priorityRenderer, text=3 )
# ... and add it to the treeview.
self.append_column( self.colPriority )
All nice but how could I display the Pixbuf icon instead of the integer and how do I initialize the ComboBox using the integer? And, second question, is it possible to show only the icon when the combobox is not active and show the icon and some text when the value is changed (ComboBox active)?
You need a Gtk.CellRendererPixbuf to display the icon. See for example the excellent Python 3 Gtk+ Tutorial.
Answering your second question: Yes. Just connect to the "changed" signal of the Gtk.CellRendererCombo and add/remove the cell renderer dynamically.
Related
There are some examples of removing specific items from a layout, but I can not find anything on simply deleting everything from a frame.
Using pyqt designer, I have created a frame. Then using pyuic4 the file is converted to python. In the main program, some layouts, items, and widgets are dynamically inserted to the frame. However, I dont actually keep track of all the items. On a button refresh, I want to delete everything contained in the frame and populate it again.
My question is, is there a simple way to delete everything that is contained in a frame, including layouts, widgets, and items.
As of now, I can do:
for i in range(len(MyResourceFrame.children())):
MyResourceFrame.children()[i].deleteLater()
However, I have code directly under that, and after the first qframe population, clicking on repopulate give the error that a frame is already there, which then removes all items. The second click on repopulate works. Does this have something to do with "Later" wanting to be out of scope first or is that just a name?
The deleteLater slot will just schedule the object for deletion. That is, the object won't be deleted until control returns to the event loop (which will usually mean after the currently executing function has returned).
If you want to delete an object immediately, use the sip module. This should allow you delete a layout and all its contained widgets like this:
PyQt5:
from PyQt5 import sip
...
class Window(QtWidgets.QMainWindow):
...
def populateFrame(self):
self.deleteLayout(self.frame.layout())
layout = QtWidgets.QVBoxLayout(self.frame)
...
def deleteLayout(self, layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
self.deleteLayout(item.layout())
sip.delete(layout)
PyQt4:
import sip
...
class Window(QtGui.QMainWindow):
...
def populateFrame(self):
self.deleteLayout(self.frame.layout())
layout = QtGui.QVBoxLayout(self.frame)
...
def deleteLayout(self, layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
self.deleteLayout(item.layout())
sip.delete(layout)
The problem was the layout predefined inside your QFrame. If you remove it in QtDesigner your frame will appear correctly with the first click.
I am using PyQt5 for my application and a QTreeWidget to display content. Since I need to display rich text (HTML) each item has its text property set to "" and I create individual QLabels with the desired text. I then use QTreeWidget.setItemWidget. My problem is that using that method, when the QTreeWidget is smaller (in width) than the items' width, the horizontal scrollbar is not displayed. Which is logical, since from the point of view of the QTreeWidget, each item has width 0 because its text is empty.
I tried using a custom helper method that I use instead of the QLabel.setText method by automatically calling QLabel.setFixedSize method afterwards, but it doesn't work very well (the size is off by 5 to 90 pixels each time).
How would it be possible to make the whole thing determine automatically when to show the scrollbar, and what width to use for them?
MCVE:
tree = QTreeWidget()
item = QTreeWidgetItem(tree)
label = QLabel()
label.setText("<b>some text here</b>")
tree.setItemWidget(item, 0, label)
I even struggled with the same problem and finally this solution works for me:
Set the treewidget header StretchLastSection to False
treeWidget.header()->setSectionResizeMode(column, QtWidgets.QHeaderView.ResizeToContents)
I want to create an auto-complete TextField.
I mean - when you type something in the field and see a prompt list below. The prompt list is an array with possible values. The best way to explain it show a similar picture.
I already have some experience in Pythonista 3 but it was not UI programming experience.
I understand this is complex and that maybe I should use an additional View and Delegate mechanism but I don't have any idea how to start. I have already spent several days in Google looking for a solution, but I can't, in the context of Pythonista.
Has anybody done this? Or could someone provide useful links for reading?
A drop-down list can be created in Pythonista using a TableView. TableViews are really just single-column lists, they’re not just for tables.
So the steps would be:
Create a tableview.
Align it with your text field.
Hide the tableview until typing starts.
Update the tableview’s item list with the auto-completion options whenever the typing changes.
Potentially hide the tableview again when typing ends.
You can hide any view by setting its .hidden property to True.
You can do things when typing starts in a TextField by creating a delegate object that implements textfield_did_change.
You set a TableView to have a list of items by giving the TableView a data_source, probably an implementation of ui.ListDataSource. Whenever the items property on the data source changes, the list of options will also automatically change.
You can react to the user choosing an option from the TableView by setting an action on the TableView’s delegate.
The documentation for TableView, TextField, ListDataSource, delegates, and actions, can be found at Pythonista’s Native GUI for iOS documentation.
Here is a basic example:
# documentation at http://omz-software.com/pythonista/docs/ios/ui.html
import ui
# the phoneField delegate will respond whenever there is typing
# the delegate object will combine phoneField delegate, tableview delegate, and data source, so that it can share data
class AutoCompleter(ui.ListDataSource):
def textfield_did_change(self, textfield):
dropDown.hidden = False
# an arbitrary list of autocomplete options
# you will have a function that creates this list
options = [textfield.text + x for x in textfield.text]
# setting the items property automatically updates the list
self.items = options
# size the dropdown for up to five options
dropDown.height = min(dropDown.row_height * len(options), 5*dropDown.row_height)
def textfield_did_end_editing(self, textfield):
#done editing, so hide and clear the dropdown
dropDown.hidden = True
self.items = []
# this is also where you might act on the entered data
pass
def optionWasSelected(self, sender):
phoneField.text = self.items[self.selected_row]
phoneField.end_editing()
autocompleter = AutoCompleter(items=[])
autocompleter.action = autocompleter.optionWasSelected
# a TextField for phone number input
phoneField = ui.TextField()
phoneField.delegate = autocompleter
phoneField.keyboard_type = ui.KEYBOARD_PHONE_PAD
phoneField.clear_button_mode = 'while_editing'
# the drop-down menu is basically a list of items, which in Pythonista is a TableView
dropDown = ui.TableView()
dropDown.delegate = autocompleter
dropDown.data_source = autocompleter
# hide the dropdown until typing starts
dropDown.hidden = True
# create interface
mainView = ui.View()
mainView.add_subview(phoneField)
mainView.add_subview(dropDown)
# present the interface before aligning fields, so as to have the window size available
mainView.present()
# center text field
phoneField.width = mainView.width*.67
phoneField.height = 40
phoneField.x = mainView.width/2 - phoneField.width/2
phoneField.y = mainView.height/3 - phoneField.height/2
# align the dropdown with the phoneField
dropDown.x = phoneField.x
dropDown.y = phoneField.y + phoneField.height
dropDown.width = phoneField.width
dropDown.row_height = phoneField.height
On my iPhone, this code creates an interface that looks like this:
I would like to create a hover box (or info box) which opens up when the user places the mouse cursor on top of a Pmw RadioSelect label. For example, when the cursor is placed on top of "Primary" the program opens an info box explaining what "Primary" means.
Problem: I don't know how to access the individual labels inside the RadioSelect object. I need to bind a method to the individual labels, but I don't know how to refer to them.
Extra: How could I have solved this myself? I tried looking at the RadioSelect attributes with dir() and I read the Pmw manual online, but couldn't find the information.
EDIT This is what I have found out thus far: The manual says that the labels only start to exist if their position is set explicitly:
labelpos
Initialisation option. Specifies where to place the label component.
If None, a label component is not created. The default is None
After setting it explicitly for example as so:
self.rs = Pmw.RadioSelect(parent, labelpos = 'w')
you can refer to it with
self.rs.component('label')
But I still don't know how to reach the individual labels.
EDIT 2: The trick was just to assign the RadioSelect "items" into variables like the accepted answer suggests:
self.cb1 = self.radio_select.add("text")
After assigning the "item" into a variable you can simply bind methods to the variable, like such:
self.balloon = Pmw.Balloon(self, initwait=500, relmouse='both')
self.balloon.bind(self.cb1, "Balloon text example")
If I understand well your problem, I think you are looking for:
To rely on Pmw to draw the widgets (unlike what I did with Tkinter previously)
when the cursor is placed on top of "Primary" the program opens an info box explaining what "Primary" means. (the effect I produced on the demo below)
Identify individual checkbuttons (or what you call in your own terms reaching the individual labels within the Pmw.RadioSelect)
Solution:
The solution for the first problem you know it already.
For the second problem, as I explained previously, you will need to instantiate Pmw.Balloon() and bind it to individual checkbuttons (or labels as you call them). I re-programmed that as you can see below but using an other method. I mean I relied mainly on add() which returns the component widget. Then I binded the instance of Pmw.Balloon() to the returned value from add(). Doing this, you already offer yourself a way to access individually the checkbuttons (and you play more with this if you want)
You can access individual checkbuttons (labels) by using getvalue() or getcurselection() methods which work similarly by returning the return the name of the currently selected button. But in practice, you will get tuples ( I mean these functions return the names of all selected checkbuttons, as I showed in the access_to_labels_individually() that I used as a callback method to display the names of the checkbuttons you select; of course you can play with that also depending on your needs)
Code
Here is an MVCE program:
'''
Created on Jun 18, 2016
#author: Billal BEGUERADJ
'''
# -*- coding: utf-8 -*-
import Pmw
import tkinter as tk
class Begueradj:
def __init__(self, parent):
self.balloon = Pmw.Balloon(parent)
# Create and pack a vertical RadioSelect widget, with checkbuttons.
self.checkbuttons = Pmw.RadioSelect(parent,
buttontype = 'checkbutton',
orient = 'vertical',
labelpos = 'w',
command = self.access_to_labels_individually,
hull_borderwidth = 2,
hull_relief = 'ridge',
)
self.checkbuttons.pack(side = 'left', expand = 1, padx = 10, pady = 10)
# Add some buttons to the checkbutton RadioSelect
self.cb1 = self.checkbuttons.add('Primary')
self.cb2 = self.checkbuttons.add('Secondary')
self.cb3 = self.checkbuttons.add('Tertiary')
# Bind the Balloon instance to each widget
self.balloon.bind(self.cb1, 'Primary:\n This is our primary service')
self.balloon.bind(self.cb2, 'Secondary:\n This is our primary service')
self.balloon.bind(self.cb3, 'Tertiary:\n This is our primary service')
# You can use getvalue() or getcurselection() to access individual labels
def access_to_labels_individually(self, tag, state):
print(self.checkbuttons.getvalue())
# Main program starts here
if __name__ =='__main__':
begueradj = Pmw.initialise(fontScheme = 'pmw1')
begueradj.title('Billal BEGUERADJ')
d = Begueradj(begueradj)
begueradj.mainloop()
Demo
(I am keeping the same screenshots because the above program produces the same results)
Here are screenshots of the running program related to the mouse hovering over each tkinter.Checkbutton() instance whether it is selected or not:
I am using pyQT 4.8.3 in order to create a proper GUI for a QGIS plugin
There are three widgets in the form
my_comboBox , my_lineEdit , my_spinBox
Assume that comboBox has three entries
'combo_first_item' , 'combo_second_item' , 'combo_third_item'
What exactly I want is;
if 'combo_second_item' is selected, then my_lineEdit toggles state to disabled
if 'combo_third_item' selected, then my_spinBox toggles state to disabled
So, how can I toggle enabled state of widgets in the form based on selected string (or index value) from the combobox?
What should be the proper signal -> slot assignment?
Unlike QbuttonBox, QcomboBox does not fire SetDisabled slot
Thanks.
Make a dictionary that maps the string to widget:
widgets = {'combo_first_item': my_comboBox,
'combo_second_item': my_lineEdit,
'combo_third_item': my_spinBox}
And a slot:
def disableWidget(currentIndex):
widget = widgets[currentIndex]
widget.setEnabled(False)
# or anything else you want to do on the widget
Then you can connect the currentIndexChanged[QString] signal to this:
comboBox.currentIndexChanged['QString'].connect(disableWidget)
Alternatively you can use currentIndexChanged[int] and a list instead of a dictionary.
PS: If this is inside a class instance, put self accordingly.