How do I require that a user hit <RETURN>, when editing a Trait value, before change notifications are sent out? - enthought

I find myself waiting for the GUI of my Traits/UI application to update, with each backspace and/or digit entry in a particular field. How can I get the Traits/UI notification system to wait until I press RETURN before it sends out change notifications?

You want to use the auto_set and enter_set attributes of a TextEditor. auto_set=False stops updating the trait on every keystroke, and enter_set=True causes it to update on Enter. See here for docs.
For example:
from traits.api import HasTraits, Str
from traitsui.api import View, TextEditor, Group, Item
class Foo(HasTraits):
my_str = Str()
traits_view = View(
Item('my_str',
style='custom',
editor=TextEditor(
auto_set=False,
enter_set=True,
),
),
Item('my_str',
style='readonly'
),
)
if __name__ == '__main__':
f = Foo()
f.configure_traits()

Related

Scala swing wait for a button press after pressing a button

So What I want to do is to press a button and inside the ButtonClicked Event I want to wait completing the event, until I press a specific button/one of the specified buttons.
I also know that there are some similar questions to this topic but I wasnt able to find a fix out of the answers
So basically this:
reactions += {
case event.ButtonClicked(`rollDice`) =>
some code ...
wait for one of the specified buttons to be pressed and then continue
some code ...
Is there an easy way to solve this problem without the use of threads?
There are certainly some abstractions you could set up around the event layer, but you asked for "easy", so my suggestion is to set a flag/state when the dice are rolled, and then check for that flag in the other buttons' event handler.
private var postDiceRollState: Option[InfoRelatedToRollDice] = None
reactions += {
case event.ButtonClicked(`rollDice`) =>
// capture whatever info you need to pass along to the later button handler
val relevantInfo = /* some code... */
// store that info in the "state"
postDiceRollState = Some(relevantInfo)
case event.ButtonClicked(other) if isPostDiceRollButton(other) =>
// when the other button gets clicked, pull the relevant info from your "state"
postDiceRollState match {
case Some(relevantInfo) =>
postDiceRollState = None // clear state
doInterestingStuff(relevantInfo) // "some code..."
case None =>
// nothing happens if you didn't roll the dice first
}
}
Note: I represented the "flag" as an Option, under the assumption that you might have some information you want to capture about the rollDice event. If you don't actually have anything to put in there, you could represent your state as private var didRollDice: Boolean = false and set/clear would be setting it to true/false respectively.

Trying to get user's latest mouse click in scalafx

I'm trying to get user's latest mouse click in order to display the right table. However, I can't find any way to implement this idea. How do i get user's latest mouse click by using mouseEvent function?
I tried using if else statements but it doesn't work when there is still value in the monstersTable1
def handleEditMonster(action : ActionEvent) = {
val selectedMonster1 = monstersTable1.selectionModel().selectedItem.value
val selectedMonster2 = monstersTable2.selectionModel().selectedItem.value
if (selectedMonster1 != null){
val okClicked = MainApp.showMonsterEditDialog(selectedMonster1)
if (okClicked) showMonstersDetails(Some(selectedMonster1))
} else if (selectedMonster2 != null) {
val okClicked = MainApp.showMonsterEditDialog(selectedMonster2)
if (okClicked) showMonstersDetails(Some(selectedMonster2))
} else {
// Nothing selected.
val alert = new Alert(Alert.AlertType.Warning){
initOwner(MainApp.stage)
title = "No Selection"
headerText = "No monsters Selected"
contentText = "Please select a monsters in the table."
}.showAndWait()
}
}
I want it to be able to access the second table even though selectedMonster1 is still != null
It's not entirely clear from your question what it is you're trying to do, so please bear with me... (For future reference, it's best if you can create a ''minimal, complete and verifiable example'' that illustrates your problem.)
I'm assuming that you have two scalafx.scene.control.TableView instances, referenced via monstersTable1 and monstersTable2. You want to allow the user to select either one of the monsters in the first table, or one of the monsters in the second table, but not to be able to select one monster from each table simultaneously.
I'm unclear when your handleEditMonster function is called, so I'm guessing that it's invoked when the user clicks, say, an Edit Monster button, as that button's clicked event handler.
Do I have that right?
Assuming the above is accurate, you should listen for changes in table selection, and clear the selection in the other table when a new selection is made. The currently selected item in each table is a property that we can add a listener to, so we can achieve this with the following code (in your scene's initialization):
// In the onChange handlers, the first argument references the observable property
// that has been changed (in this case, the property identifying the currently
// selected item in the table), the second is the property's new value and the third
// is its previous value. We can ignore the first and the third arguments in this
// case. If the newValue is non-null (that is, if the user has made a
// selection from this table), then clear the current selection in the other
// table.
monstersTable1.selectionModel.selectedItem.onChange {(_, newValue, _) =>
if(newValue ne null) monstersTable2.selectionModel.clearSelection()
}
monstersTable2.selectionModel.selectedItem.onChange {(_, newValue, _) =>
if(newValue ne null) monstersTable1.selectionModel.clearSelection()
}
This should do the trick for you, and your handleEditMonster function should now work. You might want to add an assertion to guard against both tables having a current selection, which would indicate a bug in the selection handler logic.

GtkAppChooser Content type for all applications and/or audio mixers

I'd like to have a GtkAppChooserButton which allows the user to select a program to run which will most likely want to be an audio mixer such as pavucontrol. Despite vague documentation on the matter I gather the app chooser's content type is meant to be a MIME type, however I cannot find a suitable MIME type for an audio mixer, or more generally just "all applications".
Some types such as application/ will give two Other Application... options if Other... items are enabled, both of which are identical and neither of which contain half the applications I have, including any audio mixers. Aside from that, nothing else I do gets me remotely close to what I'm after.
Is there a MIME type and/or GtkAppChooser content type (they seem to be the same thing?) for audio mixers or just all programs in general? (I.e. Any program that would have an icon in the likes of the Gnome app launcher/xfce4-whisker-menu/etc.)
Alright so I've come up with a solution, thought it may not be as clean as you hoped.
This thread mentioned a way to get the GtkAppChooser to "show all", but it doesn't actually show all applications you have installed. However from that I was able to work out how the GtkAppChooser is using Gio.AppInfo, which has Gio.AppInfo.get_all() (this is for PyObject) which returns a full list of Gio.AppInfos for all applications I have installed, provided they have a .desktop file.
So, my "solution" is to write my own app chooser which gets a list of apps from Gio.AppInfo.get_all().
I've cleaned up my previous solution to this and written a class 'AllAppChooser' to inherrit Gtk.Dialog, giving much greater customisation. The completed dialog looks like so:
And the code used:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio
class AllAppChooser(Gtk.Dialog):
"""Provide a dialog to select an app from all those installed.
The regular Gtk.AppChooserDialog does not seem to provide any way to allow
selection from all installed apps, so this dialog serves as a replacement.
"""
def __init__(self, parent=None):
super().__init__(self)
self.set_default_size(350, 400)
self.set_icon_name('gtk-search')
self.set_title('App Chooser')
if parent:
self.set_parent(parent)
self.content_box = self.get_content_area()
self.content_box.set_margin_left(8)
self.content_box.set_margin_right(8)
self.content_box.set_margin_top(8)
self.content_box.set_margin_bottom(8)
self.content_box.set_spacing(8)
self.button_box = self.get_action_area()
self.button_box.set_margin_left(4)
self.button_box.set_margin_right(4)
self.button_box.set_margin_top(4)
self.button_box.set_margin_bottom(4)
self.label = Gtk.Label('Choose An Application')
self.content_box.pack_start(self.label, False, False, 0)
self.list_store = Gtk.ListStore(str, str, int)
pixbuf_renderer = Gtk.CellRendererPixbuf()
text_renderer = Gtk.CellRendererText()
icon_column = Gtk.TreeViewColumn('icon', pixbuf_renderer, icon_name=1)
text_column = Gtk.TreeViewColumn('text', text_renderer, text=0)
self.tree_view = Gtk.TreeView()
self.tree_view.set_model(self.list_store)
self.tree_view.set_headers_visible(False)
self.tree_view.append_column(icon_column)
self.tree_view.append_column(text_column)
self.view_port = Gtk.Viewport()
self.view_port.add(self.tree_view)
self.scroll_window = Gtk.ScrolledWindow()
self.scroll_window.add(self.view_port)
self.content_box.pack_start(self.scroll_window, True, True, 0)
self.ok_button = self.add_button(Gtk.STOCK_OK, 1)
self.ok_button.connect('clicked', self.on_ok)
self.cancel_button = self.add_button(Gtk.STOCK_CANCEL, 0)
self.selected_app = None
self.app_list = []
def populate_app_list(self):
"""Populate the list of apps with all installed apps.
Icons are provided by icon-name, however some apps may return a full
path to a custom icon rather than a themed-icon name, or even no name
at all. In these cases the generic 'gtk-missing-icon' icon is used.
"""
self.app_list = Gio.AppInfo.get_all()
for i in range(len(self.app_list)):
gio_icon = self.app_list[i].get_icon()
app_icon = 'gtk-missing-icon'
if gio_icon:
app_icon = gio_icon.to_string()
app_name = self.app_list[i].get_display_name()
self.list_store.append([app_name, app_icon, i])
self.list_store.set_sort_column_id(0, Gtk.SortType.ASCENDING)
def run(self):
"""Run the dialog to get a selected app."""
self.populate_app_list()
self.show_all()
super().run()
self.destroy()
return self.selected_app
def set_label(self, text):
"""Set the label text, \"Choose An App\" by default."""
self.label.set_text(text)
def on_ok(self, button):
"""Get Gio.AppInfo of selected app when user presses OK."""
selection = self.tree_view.get_selection()
tree_model, tree_iter = selection.get_selected()
app_index = tree_model.get_value(tree_iter, 2)
self.selected_app = self.app_list[app_index]
This is then run similar to a regular dialog:
app_chooser = AllAppChooser()
application = app_chooser.run()
If the user exits the dialog box or presses cancel then the result will be None, but if they selected an application then run() will return a Gio.AppInfo object for the application which you can then do with as you please. For example, to launch your newly selected application:
application.launch()
I feel this is now a relatively solid solution, but I still welcome additional suggestions. Additionally, if there is a way to do this in Gtk without having to do all this stuff I would still love to hear it.

Open contact activity when a specified input-field is pressed

I want to launch an contact-activity when a input-field (text) in a webview is pressed. Not sure how I can do this. Have tried to search the web but the only results I get is how to launch a filebrowser when a "input-file-field" is pressed.. And it seems like Android have an option that is exactly for that purpose, but not when another input-type is pressed?
What I actually want to do is;
when the input field is pressed, I want to give the user an option to launch the contact manager (or something similar) to choose one or more contacts from its contactlist.
Then fill the input-field with the contacts emails, separated with commas.
Is it possible to do this? Any idea what I should search the web for?
Solved! Added a new class:
import android.content.Context;
import android.content.Intent;
public class JSFromPage {
Context mContext;
JSFromPage(Context c) {
mContext = c;
}
public void openContact() {
Intent intent = (Intent) new Intent( mContext, ListContacts.class );
mContext.startActivity( intent );
}
}
And uses this is my WebView class:
mWebView.addJavascriptInterface(new JSFromPage(this), "Android");
And this is my html on the input:
onclick="Android.openContact();"
Then i created the class "ListContacts.class" with a new activity.

Receiving keystrokes in an opened wx.ComboCtrl

Coming from this question, I have a wxComboCtrl with a custom popup made of a panel with a bunch of radiobuttons.. My problem is that when I open the popup the combo doesn't get keystrokes, because the events get handled by the panel itself.. I'd like to redirect those KeyEvents to the textctrl of the combo, but I can't find a way to get it to work :/
Am I going the wrong way? Should I manually handle the textctrl value as the user presses keys? I think that would be a bit cumbersome though.. Since supposedly the textctrl already knows how to handle those events..
Here's my testcase (wxPython 2.8 on Linux), the "on_key" method should be the culprit:
import wx
import wx.combo
class CustomPopup(wx.combo.ComboPopup):
def Create(self, parent):
# Create the popup with a bunch of radiobuttons
self.panel = wx.Panel(parent)
sizer = wx.GridSizer(cols=2)
for x in range(10):
r = wx.RadioButton(self.panel, label="Element "+str(x))
r.Bind(wx.EVT_RADIOBUTTON, self.on_selection)
sizer.Add(r)
self.panel.SetSizer(sizer)
# Handle keyevents
self.panel.Bind(wx.EVT_KEY_UP, self.on_key)
def GetControl(self):
return self.panel
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.Size(200, 150)
def on_key(self, evt):
if evt.GetEventObject() is self.panel:
# Trying to redirect the key event to the combo.. But this always returns false :(
print self.GetCombo().GetTextCtrl().GetEventHandler().ProcessEvent(evt)
evt.Skip()
def on_selection(self, evt):
self.Dismiss()
wx.MessageBox("Selection made")
class CustomFrame(wx.Frame):
def __init__(self):
# Toolbar-shaped frame with a ComboCtrl
wx.Frame.__init__(self, None, -1, "Test", size=(800,50))
combo = wx.combo.ComboCtrl(self)
popup = CustomPopup()
combo.SetPopupControl(popup)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(combo, 0)
self.SetSizer(sizer)
self.Layout()
if __name__ == '__main__':
app = wx.PySimpleApp()
CustomFrame().Show()
app.MainLoop()
Edit:
I found these (unresolved) discussions on the same topic..
"ComboCtrl loses keyboard focus when ComboPopup is shown "
"issue using wx.ComboCtrl"
I had the same problem with a custom ComboPopup control using a panel as the basis of the control.
EmulateKeypress just entered a never-ending loop. I found binding the panel to the EVT_KEY_DOWN and EVT_CHAR to the overridden OnComboKeyEvent function and skipping the event down the event handling chain was all that was required to enable entering text into the textbox of the ComboCtrl with the popup open. I think this makes sense. As the Popup isn't strictly a control its events won't belong anywhere until bound into the the wx.app event loop through the Bind() method.
I've included the overridden OnComboKeyEvent function below so that anyone else can explore this problem.
def create(self, *args, **kwds):
...
self.panel.Bind(wx.EVT_CHAR, self.OnComboKeyEvent)
self.panel.Bind(wx.EVT_KEY_DOWN, self.OnComboKeyEvent)
...
def OnComboKeyEvent(self, evt):
evt_dict = {wx.EVT_TEXT.typeId: "TextEvent",
wx.EVT_CHAR.typeId:"CharEvent",
wx.EVT_CHAR_HOOK.typeId:"CharHookEvent",
wx.EVT_KEY_UP.typeId:"KeyUpEvent",
wx.EVT_KEY_DOWN.typeId:"KeyDownEvent"}
if evt.GetEventType() in evt_dict:
thelogger.debug("oncombokeyevet:%s" % evt_dict[evt.GetEventType()])
super().OnComboKeyEvent(evt)
evt.Skip()
I've got an answer from Robin Dunn himself on wxpython-users:
Sending low-level events to native
widgets like this does not work the
way that most people expect it to.
You are expecting this to result in
the character being added to the text
ctrl,but it can't do that because all
that ProcessEvent does is send the
wx.Event to any bound event handlers
and then it stops. It does not go to
the next step and convert that event
to the equivalent native message and
send it on to the native widget. See
the "Simulating keyboard events"
thread.
It doesn't work especially well on
non-Windows platforms, but you could
try calling EmulateKeypress instead.