Pyside getting a widget inside a QtabWidget - python

I have a QtabWidget and I made a ListWidget inside that tabwidget
self.tabWidget = QtGui.QTabWidget(self.centralwidget)
self.listWidget = QtGui.QListWidget(self.tabWidget)
Then I made a tab:
self.tab1 = QtGui.QWidget()
self.tabWidget.addTab(self.tab1,"hi")
What I'm trying to do is get the listview for inside QTabWidget for tab1.
print self.tabWidget.currentWidget()
It prints out a pointer:
PySide.QtGui.QWidget object at 0x0000000004EA84A4
I want the QListWidget so I can call functions like addItem etc.
Edit: I also have another question. I'm using Pyside and theres a function called retranslateUI and setupUI. I want to add a signal for my QTabWidget,
self.tabWidget.currentChanged.connect(self.showStreamList(self.tabWidget.tabText(self.tabWidget.currentIndex())))
but I'm not sure where to put it. I'm putting it in retranslateUI because thats there button.clicked.connects are but when I run the program, I think it executes this command first. The GUI doesn't even display. In general, where should I group these signals/event listeners?

Taking your comment into account, you seem to want to dynamically add QListWidgets to a QTabWidget and want individual access to each QListWidget.
QTabWidget's addTab() method takes a QWidget and a string as its arguments. A QListWidget, as the name implies, is derived / subclassed from QWidget. Therefore, the addTab() method will accept a QListWidget, if you pass it one. So self.tabWidget.addTab(self.listWidget,"hi") should work just fine.
Next, accessing them. QTabWidget has a method to access any tab by its index, sensibly called widget(index). Therefore, if you want to access the n-th widget, you can get it by calling self.tabWidget.widget(n).
You could therefore get any list widget and do stuff with it:
lw = self.tabWidget.widget(0) # get the 0th widget
lw.addItem(...)

Related

Is it possible to destroy Tkinter widgets by pathname?

I have several widgets created in a loop, each given a sequential name, eg:
for item in itemlist:
myWidget=tk.Widget(root, name=item)
myWidget.pack()
Now I have widgets with pathnames like
.!mainapplication.!itemframe.item1
is it possible to use the pathname to destroy a widget?
I can't use myWidget.destroy() because then I can't target specific widgets.
If it's not possible I can create an array to hold the widgets and deal with them from there, but I was wondering if there was any way to do it this way.
If you save the return value of tk.Widget(...) to a list or dictionary, you can access any of the widgets without having to use the name. This is by far the most common and convenient way to manage widgets created in a loop.
Example:
widgets = []
for item in itemlist:
myWidget=tk.Widget(root, name=item)
myWidget.pack()
widgets.appen(widget)
...
for widget in widgets:
widget.destroy()
If you really need to convert a name to a widget, tkinter provides a method on every widget called nametowidget which accepts the name of a widget and returns the instance.
root.nametowidget('.!mainapplication.!itemframe.item1').destroy()

How to add to default context menu in Python textbox using PyQt5?

I'm trying to add a functionality to a text box where a user can highlight a word, right click and be able to choose whether to define or get synonyms for the highlighted word. I coded a context menu but it only appears when I click outside of the textbox. Is there anyway I can add functions to the default context menu that includes Copy, Paste, etc.? Here is my code for the context menu.
self.setContextMenuPolicy(Qt.ActionsContextMenu)
defineAction = QtWidgets.QAction("Define", self)
defineAction.triggered.connect(lambda: self.define(event))
self.addAction(defineAction)
synonymAction = QtWidgets.QAction("Find Synonyms", self)
synonymAction.triggered.connect(lambda: self.synonym(event))
self.addAction(synonymAction)
You'll need to subclass the text edit widget and override createStandardContextMenu(point).
In your overridden method, cal the base call implementation to get the standard context menu object (it returns QMenu). Modify this menu with custom actions and then return the menu.
The function will be called when the user requests a context menu.
See http://doc.qt.io/qt-5/qplaintextedit.html#createStandardContextMenu for more details
EDIT: You can subclass like this
class MyTextEdit(QLineEdit):
def createStandardContextMenu(self, menu):
#as above, reimplement this method
Then you use that class instead of QLineEdit when you make your GUI.
Alternatively I've remembered there is a signal called customContextMenuRequested. You use this instead like this
#assume you have the textbox in a variable called self.my_textbox
self.my_textbox.setContextMenuPolicy(Qt.CustomContextMenu)
self.my_textbox.customContextMenuRequested.connect(self.generate_context_menu)
and then add a method to the class that generates the GUI like:
def generate_context_menu(self, location):
menu = self.my_textbox.createStandardContextMenu()
# add extra items to the menu
# show the menu
menu.popup(self.mapToGlobal(location))

Odd behavior when trying to assign parentless widget in Qt

I have the following code:
from PyQt5 import QtWebEngineWidgets, QtWidgets
class Q(QtWebEngineWidgets.QWebEnginePage):
pass
app = QtWidgets.QApplication([])
l = QtWebEngineWidgets.QWebEngineView()
print(type(l.page()))
l.setPage(Q(l))
print(type(l.page()))
p = Q()
l.setPage(p)
print(type(l.page()))
l.setPage(Q())
print(type(l.page()))
app.exec_()
And here's the output:
<class 'PyQt5.QtWebEngineWidgets.QWebEnginePage'>
<class '__main__.Q'>
<class '__main__.Q'>
<class 'PyQt5.QtWebEngineWidgets.QWebEnginePage'>
First I create a new instance of the QWebEnginePage-derived Q-class, set the view as its parent and assign it as the view's page. It works as expected.
Next I do the same, but without giving the parent. Instead, I create a temporary variable that holds a new Q and assign it. It still works as expected.
Finally, I directly assign a dynamically created parentless Q. For some reason this doesn't work and the page resets to the default class.
Why does this happen?
The QWebEngineView does not take ownership of the QWebEnginePage, and it does not re-parent it.
For the third example, the page has no parent, but it's kept alive because python holds a global reference to it.
For the last example, there is no parent and no external reference, so the page gets garbage-collected before it is set. It is effectively equivalent to calling setPage(None), which will remove the previously set page and restore the default.

Getting multiple wx widget values with Event Handling

CODE: http://pastebin.com/W4uXmazw
I would like to memorize how to get values from any wx widget with event handling after clicking a wx.Button.
In my program i have two fields, the new filename and the contents.
What are the steps i have to take in order to get the values from each field?
From there, i can use pythons f.open and f.write methods to complete my application.
Thanks!
If you want to get value of a widget, then you need to make that widget accessible throughout the entire class. To do that, you need to make the variable for the widget into an instance variable. So instead of adding the text control directly to the sizer, you'll want to do something like this:
self.newfilename = wx.TextCtrl(panel,-1), 0, wx.TOP, 5)
self.contents = wx.TextCtrl(panel,-1,size=(390,150),style = wx.TE_MULTILINE|wx.TE_PROCESS_TAB)
Then in your button's event handler, you can just do something like this:
valueOne = self.newfilename.GetValue()
contents = self.contents.GetValue()
The other way to do it would be to use your panel. If you use "self.panel", then you could grab all its children via its GetChildren method and then iterate over the list and use Python's "isinstance" builtin to check what kind of widget you're accessing. If you have set the widget's name, you can check that too.

Refreshing panel contents in wxPython

What is the approach to update widgets in a wxPanel based on events from other controls on same panel?
Scenario 1 is updating the list of a comboBox based on what has been selected from another comboBox , where both are in same panel.
Scenario 2 is showing a new control/widget in a panel based on an event.
Basically creating new controls is easy but I dont know how to refresh/update my panel so immedialtly shows them.
Scenario 1
To change the choices of a combobox self.cbx you can use any of the following methods:
self.cbx.SetItems(choices) where choices is the full list of choices.
self.cbx.SetString(n, string) that sets the string at position n.
InsertItems(items, pos) Inserts the list of strings in the items argument into the list box before the position in the pos argument.
Note that the method Set(choices) of listboxes does not exist for the list in comboboxes. You must use SetItems(choices) instead (this is not clearly indicated in some textbooks).
If you want these changes to occur as a result of a selection in another combobox self.cbx_1 , just get the event (self.Bind(wx.EVT_COMBOBOX, on_combo_1, self.cbx_1)) of the first combobox, process your data as you like in the corresponding self.on_combo method and use one of the above methods to modify the second combobox.
For example:
def on_combo_1(self, evt):
"append cbx_1 selection to cbx if not already in cbx"
selection = self.cbx_1.GetStringSelection()
cbx_choices = self.cbx.GetItems()
if selection not in cbx_choices:
cbx_choices.append(selection)
self.cbx.SetItems(cbx_choices)
The fact the comboboxes are in the same or different panel is irrelevant for that.
Scenario 2
Normally you put your widgets inside sizers. To hide or made visible elements on the sizer you call the methods Show, Hide or Layout:
Show(self, item, show=True, recursive=false)
Shows or hides an item managed by the sizer. To make a sizer item disappear or reappear, use Show followed by Layout. The item parameter can be either a window, a sizer, or the zero-based index of the item. Use the recursive parameter to show or hide an item in a subsizer. Returns True if the item was found.
Hide(self, item, recursive)
A convenience method for Show (item, False, recursive).
Layout(self)
This method will force the recalculation and layout of the items controlled by the sizer using the current space allocated to the sizer. Normally this is called automatically from the owning window's EVT_SIZE handler, but it is also useful to call it from user code when one of the items in a sizer change size, or items are added or removed.
References: wxPython in Action, Noel Rappin and Robin Dunn
For scenario one, you'd do something like the following (assuming the first combobox is bound to its EVT_COMBOBOX:
value = self.cboOne.GetValue()
if value == "something":
self.cboTwo.SetItems(someList)
For showing a new widget, you could create it and then use Show()/Hide() as necessary. If the widget is in a sizer, then use the Sizer's Append or Insert methods. It also has a Detach method that can be used to hide widgets or you just call Hide itself. See the documentation for more information: http://www.wxpython.org/docs/api/wx.Sizer-class.html

Categories