wxPython - Using filedialog to update a textctrl - python

I am trying to create a basic dialog to edit a settings file. I have some list of necessary files
options = ["dog", "cat", "chicken"]
I then have a column with the option names in StaticText and a column of TextCtrl to input the file location. Next to each TextCtrl is a "Select File" button.
How can I have each button select a file and then update the TextCtrl with the location? At the moment, I iterate through the options list to generate the text and buttons. I know if I were to define everything by hand, I could have a handler unique to each button which updates the the corresponding TextCtrl, but I feel like there is a better when. I would like it to be modifiable so I can just add an option to the options list, and another row of options is added.
Currently I am binding each button to self.OnSelect, but I'm not sure how to use that to update the correct TextCtrl. To reiterate I know how to use FileDialog, I am just having troubles tying each button to its corresponding TextCtrl.
selectBtn = wx.Button(self, wx.ID_FILE, label="Select File")
selectBtn.Bind(wx.EVT_BUTTON, self.OnSelect)
Should I have a unique ID instead of using wx.ID_FILE?
I am relatively new to GUI desgin so any help is appreciated. Even if you think what I'm doing may be ugly and something else would be better, please let me know

My answer was derived from here: wxPython how to put a text in TextCtrl with a button inserted by "Add" Button
By using lambda to create anonymous functions, the solution is quite simple. You can have a handler, like:
def OnSelect(self, event, ctrl):
name, path = selectFile() #gets name and path using FileDialog
ctrl.SetValue(path)
The buttons can be bound like
selectBtn = wx.Button(self, wx.ID_FILE, label="Select File")
selectEvent = lambda event, ctrl=txt: self.OnSelect(event, ctrl)
selectBtn.Bind(wx.EVT_BUTTON, selectEvent)
Works like a charm.

Related

Adding check button in dropdown menu/popup menu of tkinter with dynamic input coming in form of list

So I am using xlrd module to read names of excel files of a workbook that user selects. Now this comes up in form of a list, which I am able to put in popup menu drop down(able to select on name at a time) now I want to
So far I am able to use the drop down menu of tkinter with dynamic input but able to select just one name at a time, now I want to select multiple items at a time by adding checkbox to the same. Can anyone help.. Here is sample code on which you anyone may please try adding checkbox and select multiple names at a time.
from tkinter import *
root = Tk()
a = [] #creates a list to store the job names in
var = StringVar() # creates a stringvar to store the value of options
for i in range(20): # fills list with nonsense jobs for troubleshooting
a.append("Job Name "+str(i))
var.set(a[0]) # sets the default option of options
options = OptionMenu(root, var, *a)
# creates an option menu populated with every element of the list
button = Button(root, text="Ok", command=lambda:print(var.get()))
# prints the current value of options
options.pack()
button.pack()
The above code shows a drop down with single select able item, I just want multiple selection functionality enabled by adding checkbox in front of every option
Use the MenuButton feature in tkinter. It has a method called Menu. To this method you could add as many check buttons as you want. This will support multiselect dropdown option. Give a variable name to each checkbutton and access its state through the get command.

Trying to check multiple qt radio buttons with python

I need to check multiple radio buttons from a qt ui with python.
Up to now we are using something similar to:
if main.ui.radioButton_1.isChecked():
responses["q1"] = "1"
elif main.ui.radioButton_2.isChecked():
responses["q1"] = "2"
elif main.ui.radioButton_3.isChecked():
responses["q1"] = "3"
if main.ui.radioButton_4.isChecked():
responses["q2"] = "1"
elif main.ui.radioButton_5.isChecked():
responses["q2"] = "2"
elif main.ui.radioButton_6.isChecked():
responses["q2"] = "3"
...
Since there are very many buttons and many different categories (q1, q2, ...) I was thinking of optimizing it a bit. So this is what I hoped would work (adopted from How to get the checked radiobutton from a groupbox in pyqt):
for i, button in enumerate(["main.ui.radioButton_" + str(1) for i in range(1, 8)]):
if button.isChecked():
responses["q1"] = str(i - 1)
I get why this doesn't work but writing it I hoped it would.
So I tried to iterate through the buttons using something similar to (Is there a way to loop through and execute all of the functions in a Python class?):
for idx, name, val in enumerate(main.ui.__dict__.iteritems()):
and then use some modulo 3 and such to assign the results. But that doesn't work either. Not sure if it's because i used __ dict __ or something else. The error I got was:
TypeError: 'QLabel' object is not iterable
Now some people could say that implicit is better that explicit and also because of readability the if elif chain is good the way it is but there are 400+ lines of that. Also after reading this post, Most efficient way of making an if-elif-elif-else statement when the else is done the most?, I thought there must be a better and more efficient way of doing this (see examples 3.py and 4.py of the of the accepted answer). Because I need to check the Boolean value of main.ui.radioButton_1.isChecked() and then assign thevalue according to the Buttons group (q1, q2,...), I haven't managed to implement the solution using dictionaries as described in the post.
Am I stuck with the if elif chain or is there a way to not only reduce the LOC but also make the code more efficient (faster)?
It looks like you have used Qt Designer to create your ui, so I would suggest putting each set of radio buttons in a QButtonGroup. This will give you a simple, ready-made API for getting the checked button in a group without having to query each button individually.
In Qt Designer, buttons can be added to a button-group by selecting them, and then choosing Assign to button group > New button group from the context menu. The button IDs (which you will need to use later) are assigned in the order the buttons are selected. So use Ctrl+Click to select each button of a group in the correct order. The IDs start at 1 for each group and just increase by one for each button that is added to that group.
When a new button-group is added, it will appear in the Object Inspector. This will allow you to select it and give it a more meaningful name.
Once you've created all the groups, you can get the checked button of a group like this:
responses["q1"] = str(main.ui.groupQ1.checkedId())
responses["q2"] = str(main.ui.groupQ2.checkedId())
# etc...
This could be simplified even further to process all the groups in a loop:
for index in range(1, 10):
key = 'q%d' % index
group = 'groupQ%d' % index
responses[key] = str(getattr(main.ui, group).checkedId())
Another way to do it is using signals. If you had lots of radio button in an application, I suspect this kind of approach would be noticeably faster. For example:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MoodExample(QGroupBox):
def __init__(self):
super(MoodExample, self).__init__()
# Create an array of radio buttons
moods = [QRadioButton("Happy"), QRadioButton("Sad"), QRadioButton("Angry")]
# Set a radio button to be checked by default
moods[0].setChecked(True)
# Radio buttons usually are in a vertical layout
button_layout = QVBoxLayout()
# Create a button group for radio buttons
self.mood_button_group = QButtonGroup()
for i in xrange(len(moods)):
# Add each radio button to the button layout
button_layout.addWidget(moods[i])
# Add each radio button to the button group & give it an ID of i
self.mood_button_group.addButton(moods[i], i)
# Connect each radio button to a method to run when it's clicked
self.connect(moods[i], SIGNAL("clicked()"), self.radio_button_clicked)
# Set the layout of the group box to the button layout
self.setLayout(button_layout)
#Print out the ID & text of the checked radio button
def radio_button_clicked(self):
print(self.mood_button_group.checkedId())
print(self.mood_button_group.checkedButton().text())
app = QApplication(sys.argv)
mood_example = MoodExample()
mood_example.show()
sys.exit(app.exec_())
I found more information at:
http://codeprogress.com/python/libraries/pyqt/showPyQTExample.php?index=387&key=QButtonGroupClick
http://www.pythonschool.net/pyqt/radio-button-widget/

Python: Button widget in Tkinter

I have just now begun with GUI programming in Python 2.7 with Tkinter.
I want to have a button Browse, which when clicked opens the Windows File Explorer and returns the path of file selected to a variable. I wish to use that path later.
I am following the code given here. It outputs a window displaying 5 buttons, but the buttons do nothing. On clicking the first button, it doesn't open the selected file.
Likewise, on clicking the second button, the askopenfilename(self) function is called and it should return a filename. Like I mentioned, I need that filename later.
How to I get the value returned by the function into some variable for future use?
There is no point in using return inside a callback to a button. It won't return to anywhere. The way to make a callback save data is to store it in a global variable, or an instance variable if you use classes.
def fetchpath():
global filename
filename = tkFileDialog.askopenfilename(initialdir = 'E:')
FWIW (and unrelated to the question): you're making a very common mistake. In python, when you do foo=bar().baz(), foo takes the value in baz(). Thus, when you do this:
button = Button(...).pack()
button will take the value of pack() which always returns None. You should separate widget creation from widget layout if you expect to save an actual reference to the widget being created. Even if you're not, it's a good practice to separate the two.

Updating cell widgets changes in a Qtable without selecting the cell

well I'm using Pyqt4 in Maya2012 to make a reference editor alike ui. I working with a QtableWidget to make the reference list and i have subwidgets in each cell. One of the widgets is a checkbox that unload or reload the reference.
The problem i have is if a click directly in the checkbox without have the cell selected it doesn't do anything
this is my code:
def listConnections(self):
self.pos=self.sender().currentRow()
wid = self.list.ref_l.cellWidget(self.pos, 0).children()
self.text = self.list.list[self.pos]
self.ref()
for wt in wid:
if type(wt)== type(QCheckBox()):
wt.stateChanged.connect(self.changeState)
if type(wt)== type(QComboBox()):
wt.currentIndexChanged.connect(self.changeType)
I'm calling the function with a "itemSlectionChanged" signal because is the only way i knew i could detect the subwidgets.
All the subwidgets are made in the moment i fill the list.
Is there a way to make what i want?
Edit:
This is how i called the function
self.list.ref_l.itemSelectionChanged.connect(self.listConnections)
and this is how i create all the subwidgets in the cells
def fillList(self):
mayaRef = self.findRef()
if len(mayaRef)>0:
for count in range(0,len(mayaRef)):
self.ref_l.insertRow(count)
wid=QWidget()
cLayout=QHBoxLayout()
wid.setLayout(cLayout)
checkWid=QCheckBox()
nameWid=QLabel()
cLayout.addWidget(nameWid)
nameWid2=QLabel()
cLayout.addWidget(nameWid2)
comWid=QComboBox()
cLayout.addWidget(comWid)
self.ref_l.setCellWidget(count,0,wid)
self.ref_l is my QTable Widget, this is in another code that i'm calling with self.list in the original
You should set up all your connections right after you create the check boxes in fillList. Each item is associated with the path of the reference. You can use a QSignalMapper to map each check box to the path, then connect the signal mapper to your changeState slot. The signal mapper then calls that slot with the path you specified.

How to disable input to a Text widget but allow programmatic input?

How would i go about locking a Text widget so that the user can only select and copy text out of it, but i would still be able to insert text into the Text from a function or similar?
Have you tried simply disabling the text widget?
text_widget.configure(state="disabled")
On some platforms, you also need to add a binding on <1> to give the focus to the widget, otherwise the highlighting for copy doesn't appear:
text_widget.bind("<1>", lambda event: text_widget.focus_set())
If you disable the widget, to insert programatically you simply need to
Change the state of the widget to NORMAL
Insert the text, and then
Change the state back to DISABLED
As long as you don't call update in the middle of that then there's no way for the user to be able to enter anything interactively.
Sorry I'm late to the party but I found this page looking for the same solution as you.
I found that if you "disable" the Text widget by default and then "normal" it at the beginning of a function that gives it input and "disable" it again at the end of the function.
def __init__():
self.output_box = Text(fourth_frame, width=160, height=25, background="black", foreground="white")
self.output_box.configure(state="disabled")
def somefunction():
self.output_box.configure(state="normal")
(some function goes here)
self.output_box.configure(state="disable")
I stumbled upon the state="normal"/state="disabled" solution as well, however then you are unable to select and copy text out of it. Finally I found the solution below from: Is there a way to make the Tkinter text widget read only?, and this solution allows you to select and copy text as well as follow hyperlinks.
import Tkinter
root = Tkinter.Tk()
readonly = Tkinter.Text(root)
readonly.bind("<Key>", lambda e: "break")

Categories

Resources