Looks like there's no built-in option to disable/remove the InputBox/TextCtrl part in wx.lib.filebrowsebutton.FileBrowseButton, well I did come up with a workaround which is simply set labelText to blank and then size it down to fit only the button itself, this way visually you can tell no difference from a normal button, but I don't think it's nice enough to go with.
So is there a way to fully disable/remove the InputBox part? Or maybe a way to bind normal button with file browser function?
If you don't need a textctrl, then you don't really need wx.lib.FileBrowseButton. You can just have a normal wx.Button that launches a wx.FileDialog instance. In fact, that's all that wx.lib.FileBrowsBbutton does. Here's the relevant source code, the whole thing can be viewed here: https://github.com/wxWidgets/wxPython/blob/master/wx/lib/filebrowsebutton.py
def OnBrowse (self, event = None):
""" Going to browse for file... """
current = self.GetValue()
directory = os.path.split(current)
if os.path.isdir( current):
directory = current
current = ''
elif directory and os.path.isdir( directory[0] ):
current = directory[1]
directory = directory [0]
else:
directory = self.startDirectory
current = ''
dlg = wx.FileDialog(self, self.dialogTitle, directory, current,
self.fileMask, self.fileMode)
if dlg.ShowModal() == wx.ID_OK:
self.SetValue(dlg.GetPath())
dlg.Destroy()
Related
I'm trying to create a gradio User Interface which does the following
on the left panel I have a File control, that allows the selection of a local file (eg. a .csv)
when a file is selected a "Process" button should be made visible
when the "Process" button is pressed, a function is called, reading the contents of the file, and processing it in some ways, resulting in a string
the resulting string is shown in a TextArea in the right column
I'm stuck implementing point 2. I can select the file, but can't make the Process button become visible.
This is my code so far (not yet implementing points 3. a:
import gradio as gr
def file_selected(file_input):
print("yes, file_selected is invoked")
print(process_button)
process_button.visible=True
demo.render()
return process_button
with gr.Blocks() as demo:
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Data")
file_input = gr.File(label="Select File")
process_button = gr.Button("Process", visible=False)
with gr.Column(scale=2, min_width=600):
gr.Markdown("### Output")
result_display = gr.TextArea(default="", label="Result", lines=10, visible=False)
file_input.change(fn=file_selected, inputs=file_input, outputs=process_button)
if __name__ == "__main__":
demo.launch()
I see that at file selection the message is printed (and print(process_button) prints "button" so I'm sure this variable is not None), but the button doesn't appear on the page.
edited: fixed some errors not directly related to the problem.
There were many problems with the code (I fixed those not related with the main issue in the original post), but in the end what solved my problem (making the button visible) was that instead to rerender,
def file_selected():
...
process_button.visible=True
demo.render()
I just had to return the process_button.update
def file_selected(file_input):
...
return gr.update(visible=True)
(Actually this was documented in gradio's online docs; sorry, I didn't notice it before)
This is the complete working code:
import gradio as gr
def file_selected(file_input):
print("yes, file_selected is invoked")
print(process_button)
return gr.update(visible=True)
with gr.Blocks() as demo:
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Data")
file_input = gr.File(label="Select File")
process_button = gr.Button("Process", visible=False)
with gr.Column(scale=2, min_width=600):
gr.Markdown("### Output")
result_display = gr.TextArea(default="", label="Result", lines=10, visible=False)
file_input.change(fn=file_selected, inputs=file_input, outputs=process_button)
if __name__ == "__main__":
demo.launch()
I'm learning Python while trying to develop helper tools for Maya using Python and PySide. (Quite ambitious but there is no progress without challenges, right)
Basically, I'm writing a tool that would help me export animation as fbx files to a folder I set in any given location on my PC.
I've finished to write the export process. There should be no issue here, I'm stuck with the UI part of it.
Currently this is how my UI is looking. I want to be able to set the path to the place where I want the script to export the files.
When I select the path and press "Select Folder", I want the path of the selected folder to display in the text line of the UI. And "Remember" it so that when I press Export FBX animation or Export rig it would use that path and would save files there.
But I have no clue how to do that. Can anyone help the clueless me how to make this happen?
I would appreciate any help. Thank you )
Here is my current code:
`
import CreatureAnimBakeProcess
reload(CreatureAnimBakeProcess)
from maya import cmds
import os
from PySide2 import QtWidgets, QtCore, QtGui
class CreatureAnimBakeUI(QtWidgets.QDialog):
def __init__(self):
super(CreatureAnimBakeUI, self).__init__()
self.setWindowTitle('Creature Exporter')
self.library = CreatureAnimBakeProcess.CreatureExport()
self.buildUI()
def buildUI(self):
print 'building ui'
layout = QtWidgets.QVBoxLayout(self)
setPathWidget = QtWidgets.QWidget()
setPathLayout = QtWidgets.QVBoxLayout(setPathWidget)
layout.addWidget(setPathWidget)
self.setPathField = QtWidgets.QLineEdit()
setPathLayout.addWidget(self.setPathField)
setBtn = QtWidgets.QPushButton('Set Export Folder')
setBtn.clicked.connect(self.setPath)
setPathLayout.addWidget(setBtn)
#============================================
btnWidget = QtWidgets.QWidget()
btnLayout = QtWidgets.QVBoxLayout(btnWidget)
layout.addWidget(btnWidget)
ExportFBXBtn = QtWidgets.QPushButton('Export FBX Animation')
ExportFBXBtn.clicked.connect(self.exportFbxAnim)
btnLayout.addWidget(ExportFBXBtn)
ExportRIGBtn = QtWidgets.QPushButton('Export RIG')
ExportRIGBtn.clicked.connect(self.exportRIG)
btnLayout.addWidget(ExportRIGBtn)
return
def getDirectory(self):
directory = os.path.join(cmds.internalVar(userAppDir=True), 'Animation')
if not os.path.exists(directory):
os.mkdir(directory)
return
def setPath(self):
directory = self.getDirectory()
pathName = QtWidgets.QFileDialog.getExistingDirectory(self, directory, "Creature Exporter")
return
def exportFbxAnim(self):
pass
def exportRIG(self):
pass
def showUI():
ui = CreatureAnimBakeUI()
ui.show()
return ui
`
You can use self.setPathField.setText(pathName) to show the value, you can also assign it to self.exportPath = pathName so you can re use it.
I am only showing code snippets where I changed, the remainder of your code needs no changes.
def __init__(self):
super(CreatureAnimBakeUI, self).__init__()
# have it assigned to None
self.exportPath = None # changed here
self.setWindowTitle("Creature Exporter")
self.buildUI()
def setPath(self):
directory = self.getDirectory()
pathName = QtWidgets.QFileDialog.getExistingDirectory(
self, directory, "Creature Exporter"
)
if pathName: # changed here
# "remember the value"
self.exportPath = pathName
# show it in the text line of the UI
self.setPathField.setText(pathName)
return
self.exportPath will be None initially, once you select a folder it will have that value saved. So this if checks if the value is not set and forces the user to set a value, if it is already set that value will be used.
def exportFbxAnim(self): # changed here
if self.exportPath is None:
# call self.setPath if self.exportPath is None
self.setPath()
# self.exportPath should have the value you expect
def exportRIG(self): # changed here
if self.exportPath is None:
# call self.setPath if self.exportPath is None
self.setPath()
# self.exportPath should have the value you expect
In my PyQt4 application, there is a functionality that allows users to save a avi file.
To this aim, a saveMovie method has been implemented in the main window:
def saveMovie(self):
""" Let the user make a movie out of the current experiment. """
filename = QtGui.QFileDialog.getSaveFileName(self, "Export Movie", "",
'AVI Movie File (*.avi)')
if filename != "":
dialog = QtGui.QProgressDialog('',
QtCore.QString(),
0, 100,
self,
QtCore.Qt.Dialog |
QtCore.Qt.WindowTitleHint)
dialog.setWindowModality(QtCore.Qt.WindowModal)
dialog.setWindowTitle('Exporting Movie')
dialog.setLabelText('Resampling...')
dialog.show()
make_movie(self.appStatus, filename, dialog)
dialog.close()
My idea is to use a QProgressDialog to show how the video encoding work is proceeding.
Nevertheless, after the selection of the filename, the QFileDialog won't disappear and the entire application stays unresponsive until the make_movie function has completed.
What should I do to avoid this?
Lesson learned: if you have some long-running operations to do -- for example, reading or writing a big file, move them to another thread or they will freeze the UI.
Therefore, I created a subclass of QThread, MovieMaker, whose run method encapsulates the functionality previosly implemented by make_movie:
class MovieMaker(QThread):
def __init__(self, uAppStatus, uFilename):
QtCore.QThread.__init__(self, parent=None)
self.appStatus = uAppStatus
self.filename = uFilename
def run(self):
## make the movie and save it on file
Let's move back to the saveMovie method. Here, I replaced the original call to make_movie with the following code:
self.mm = MovieMaker(self.appStatus,
filename)
self.connect(self.mm, QtCore.SIGNAL("Progress(int)"),
self.updateProgressDialog)
self.mm.start()
Note how I defined a new signal, Progress(int).
Such a signal is emitted by the MovieMaker thread to update the QProgressDialog used to show the user how the movie encoding work is progressing.
If I set the current folder via the method Gtk.FileChooserWidget.set_current_folder(), the first time I open the file chooser, it opens on the location used as argument for set_current_folder()
But, if I select a file, the I re-open the file-chooser, it opens on the "most_recent_used_files".
I'd like it opens on the last selected file's folder path.
How to do it?
Thank you.
From the docs:
Old versions of the file chooser's documentation suggested using gtk_file_chooser_set_current_folder() in various situations, with the intention of letting the application suggest a reasonable default folder. This is no longer considered to be a good policy, as now the file chooser is able to make good suggestions on its own. In general, you should only cause the file chooser to show a specific folder when it is appropriate to use gtk_file_chooser_set_filename() - i.e. when you are doing a File/Save As command and you already have a file saved somewhere.
You may or may not like the reasoning for this behavior. If you're curious about how it came about, see File chooser recent-files in the mailing list and Help the user choose a place to put a new file on the GNOME wiki.
Setting the current folder each time works for me, but it is a little tricky. I'm using Gtk 3.14 and Python 2.7.
You have to get the filename before resetting the directory, or it's lost, and the current directory may be None, so you have to check for that.
This code is tested on Debian jessie and Windows 7.
import os.path as osp
from gi.repository import Gtk
class FileDialog(Gtk.FileChooserDialog):
def __init__(self, parent, title):
Gtk.FileChooserDialog.__init__(self, title, parent)
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
self.set_current_folder(osp.abspath('.'))
def __call__(self):
resp = self.run()
self.hide()
fname = self.get_filename()
d = self.get_current_folder()
if d:
self.set_current_folder(d)
if resp == Gtk.ResponseType.OK:
return fname
else:
return None
class TheApp(Gtk.Window):
def on_clicked(self, w, dlg):
fname = dlg()
print fname if fname else 'canceled'
def __init__(self):
Gtk.Window.__init__(self)
self.connect('delete_event', Gtk.main_quit)
self.set_resizable(False)
dlg = FileDialog(self, 'Your File Dialog, Sir.')
btn = Gtk.Button.new_with_label('click here')
btn.connect('clicked', self.on_clicked, dlg)
self.add(btn)
btn.show()
if __name__ == '__main__':
app = TheApp()
app.show()
Gtk.main()
I am searching for a simple dialog with a text entry widget asking the user for some input. The dialog should be easy to run (like the gtk.MessageDialog variants) and as flexible.
There are of course some examples but they are either not flexible enough or too complicated to construct for my taste.
I hate re-inventing the wheel... or a dialog.
Based on an example I found (thanks Ardoris!), I came up with a dialog subclass... hope it helps someone out there!
#!/usr/bin/env python
import gtk
class EntryDialog(gtk.MessageDialog):
def __init__(self, *args, **kwargs):
'''
Creates a new EntryDialog. Takes all the arguments of the usual
MessageDialog constructor plus one optional named argument
"default_value" to specify the initial contents of the entry.
'''
if 'default_value' in kwargs:
default_value = kwargs['default_value']
del kwargs['default_value']
else:
default_value = ''
super(EntryDialog, self).__init__(*args, **kwargs)
entry = gtk.Entry()
entry.set_text(str(default_value))
entry.connect("activate",
lambda ent, dlg, resp: dlg.response(resp),
self, gtk.RESPONSE_OK)
self.vbox.pack_end(entry, True, True, 0)
self.vbox.show_all()
self.entry = entry
def set_value(self, text):
self.entry.set_text(text)
def run(self):
result = super(EntryDialog, self).run()
if result == gtk.RESPONSE_OK:
text = self.entry.get_text()
else:
text = None
return text
The run() method returns either the text entered in the entry box if the user presses <Enter> or clicks Ok. If Cancel is clicked or <Esc> pressed, the run() method returns None.
Except for that, the dialog should behave as any other gtk.MessageDialog instance.
Maybe that is not very general as it assumes you will always have Ok as an option, but that is what I need in 99% of my use cases anyway.
There isn't one available in GTK+. You've got two options:
Create a dialog, pack the Entry and any other content you need (probably the best way in my opinion)
Retrieve the content_area of the MessageDialog and append an Entry to it.
Something along the lines of:
#!/usr/bin/env python
import gtk
messagedialog = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK, message_format="Hello")
action_area = messagedialog.get_content_area()
entry = gtk.Entry()
action_area.pack_start(entry)
messagedialog.show_all()
messagedialog.run()
messagedialog.destroy()
Though it does probably need more refinement to get the Entry to display nicely.
Here's the function I wrote, based on the previous answers here. It's a function instead of a class, which means you can use it in one line.
def get_text(parent, message, default=''):
"""
Display a dialog with a text entry.
Returns the text, or None if canceled.
"""
d = gtk.MessageDialog(parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_OK_CANCEL,
message)
entry = gtk.Entry()
entry.set_text(default)
entry.show()
d.vbox.pack_end(entry)
entry.connect('activate', lambda _: d.response(gtk.RESPONSE_OK))
d.set_default_response(gtk.RESPONSE_OK)
r = d.run()
text = entry.get_text().decode('utf8')
d.destroy()
if r == gtk.RESPONSE_OK:
return text
else:
return None