I just started with envisage framework. In the 4.x version I saw a few example, but I need a good documentation: link.
How can I add custom buttons to the envisage workbench, or how can I create a similar one?
The best place look for documentation is the Acmelab example in the Envisage source tree.
I'm assuming when you talk about custom buttons you mean buttons on a toolbar. First you need to create a WorkbenchActionSet, add your toolbar there, and then define your actions and assign them a button image. Here is the (slightly modified) Acmelab example with non-relevant parts taken out:
test_action_set.py
# Enthought library imports.
from envisage.ui.action.api import Action, Group, Menu, ToolBar
from envisage.ui.workbench.api import WorkbenchActionSet
class TestActionSet(WorkbenchActionSet):
""" An action test useful for testing. """
#### 'ActionSet' interface ################################################
tool_bars = [
ToolBar(name='Fred', groups=['AToolBarGroup']),
ToolBar(name='Wilma'),
ToolBar(name='Barney')
]
actions = [
Action(
path='ToolBar',
class_name='acme.workbench.action.new_view_action:NewViewAction'
),]
new_view_action.py
""" An action that dynamically creates and adds a view. """
# Enthought library imports.
from pyface.api import ImageResource
from pyface.action.api import Action
from pyface.workbench.api import View
class NewViewAction(Action):
""" An action that dynamically creates and adds a view. """
#### 'Action' interface ###################################################
# A longer description of the action.
description = 'Create and add a new view'
# The action's name (displayed on menus/tool bar tools etc).
name = 'New View'
# A short description of the action used for tooltip text etc.
tooltip = 'Create and add a new view'
image = ImageResource(Your Image File Name Goes Here)
###########################################################################
# 'Action' interface.
###########################################################################
def perform(self, event):
""" Perform the action. """
# You can give the view a position... (it default to 'left')...
view = View(id='my.view.fred', name='Fred', position='right')
self.window.add_view(view)
# or you can specify it on the call to 'add_view'...
view = View(id='my.view.wilma', name='Wilma')
self.window.add_view(view, position='top')
return
#### EOF ######################################################################
Related
Assume I have a code with a button coded in ipyvuetify
v_btn_load = vue.Btn(class_='mx-2 light-red darken-1',
children=[vue.Icon(left=True, children=['get_app']),'Load data'])
def on_click_load(widget, event, data):
#pseudo code: load file
print("button run")
v_btn_load.on_event('click', on_click_load)
How do I run (click) programmatically the v_btn_load button?
v_btn_load.click() does not work
Thanks
the "on_click_load" is still a local python function, so you can simple access it in your script. Just fill out the variables you do not need with dummies (probably widget and event) and fill the data variable according to your needs.
If you need some input from the client, than it is more difficult. I know no way to remote control the client side. The only thing I got working so far is to extend VuetifyTemplate with a private class and specify some JS code to be run when 'Mounted'. This will run the code on display, but is not the same as triggering a click act:
Here is a simple example which directly copies the content of a variable to the local clipboard without any display element:
import ipyvuetify as v
from traitlets import Unicode, observe
class toClipboard(v.VuetifyTemplate):
"""Copies a given string directly to the users clipboard.
Parameters:
clipboardValue - The value to offer for copying to the clipboard
Example:
tunnel = toClipboard(clipboardValue='VALUE_TO_COPY')
Upon change of the variable 'clipboardValue', it's content will be automatically pushed to the clipboard
"""
clipboardValue = Unicode('').tag(sync=True)
template = Unicode('''
<script>
export default {
mounted () {
var tmpElement = $('<textarea>').val(this.clipboardValue).appendTo('body').select();
document.execCommand('copy');
$(tmpElement).remove();
},
}
</script>''').tag(sync=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.observe(self.pushToClipboard, names=('clipboardValue'))
display(self)
def pushToClipboard(self, change):
display(self)
As an additional bonus this example uses the observe function of traitlets to redisplay the JS as soon as the value of the variable changes. This is a cheap workaround to create a bit similar behaviour.
I use the example above not in real GUIs, but as a lazy way in a Jupyther Notebook to automatically copy the result of a calculation to my local clipboard.
looking at the description of the v.Btn class I found this :
| ----------------------------------------------------------------------
| Methods inherited from ipyvue.VueWidget.Events:
|
| fire_event(self, event, data)
|
| on_event(self, event_and_modifiers, callback, remove=False)
I then assume that
v_btn_load.fire_event('click', None)
should do trick
I have a field in a property browser where the user sets a file path. I would like for them to get a file browser when the click on the line to edit it.
the file browser is
class TargetPropertiesBrowser(QtTreePropertyBrowser):
def __init__(self):
self._variantManager = QtVariantPropertyManager()
general_group = self._variantManager.addProperty(QtVariantPropertyManager.groupTypeId(), "General")
self._outputPath = self._variantManager.addProperty(QVariant.String, Target.OUTPUT_PATH)
self._outputPath.setToolTip("Output Directory")
general_group.addSubProperty(self._outputPath)
Now lets say I have some class PopUpBrowser that defines the popup I want displayed when they click in the property browser on the file path line. I can't find an example or documentation on how to alter the behavior or the QtTreePropertyBrowser.
Edit:
If there is a signal I can connect to for when a user clicks on the line that would be fine, however I don't see such a signal in the docs. I'm also not seeing any Enum for a variant manager (or any alternate managers) that supports a widget or button that could link a widget. Sorry if I was unclear.
Connect one of the Widget's Signals (e.g. clicked()) to a slot method in your class: http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html
NOTE: This question concerns the "first generation" Bokeh server, which has been deprecated and removed for several years. Nothing in this question or its answers is relevant to any version of Bokeh >= 0.11
For detailed information about using the modern, supported Bokeh Server, see the Running a Bokeh Server chapter of the User's Guide.
I'm trying to understand Bokeh for an interactive app that I'm building. I'm looking at the Bokeh examples, and I see that most of the examples are written all in the global namespace, but the ones in the "app" subdirectory are written in a nice, object-oriented style, where the main class inhereits from a Property class like HBox.
This is going to be a mish-mash of questions because I don't think this way of programming Bokeh was very well-documented. The first thing I encountered was that the plot didn't draw unless I included extra_generated_classes.
What does extra_generated_classes do?
Secondly, it looks like the event loop setup_events is called on startup before create and subsequently every time the plot triggers an event.
Why does setup_events need to register callbacks each time an event is triggered? And why doesn't it wait for create to finish before attempting to register them the first time?
The last thing I'm unsure about is how to force a redraw of a Glyph here. The slider demo works for me, and I'm trying to do basically the same thing, except with a scatterplot instead of a line.
I set a pdb trace at the very end of my update_data, and I can guarantee that self.source matches self.plot.renderers[-1].data_source and that both of them have been tweaked from the start. However, self.plot itself doesn't change.
What is the object-oriented approach's equivalent to calling store_objects to update the plot?
I'm especially confused by this third one, because it doesn't look like the sliders_app example needs anything like that. For clarification, I'm trying to make a variable number of widgets/sliders, so this is what my code looks like:
class attributes:
extra_generated_classes = [['ScatterBias', 'ScatterBias', 'HBox']]
maxval = 100.0
inputs = Instance(bkw.VBoxForm)
outputs = Instance(bkw.VBoxForm)
plots = Dict(String, Instance(Plot))
source = Instance(ColumnDataSource)
cols = Dict(String, String)
widgets = Dict(String, Instance(bkw.Slider))
# unmodified source
df0 = Instance(ColumnDataSource)
initialize method
#classmethod
def create(cls):
obj = cls()
##############################
## load DataFrame
##############################
df = pd.read_csv('data/crime2013_tagged_clean.csv', index_col='full_name')
obj.cols = {'x': 'Robbery',
'y': 'Violent crime total',
'pop': 'Population'
}
cols = obj.cols
# only keep interested values
df2= df.ix[:, cols.values()]
# drop empty rows
df2.dropna(axis=0, inplace=True)
df0 = df2.copy()
df0.reset_index(inplace=True)
# keep copy of original data
obj.source = ColumnDataSource(df2)
obj.df0 = ColumnDataSource(df0)
##############################
## draw scatterplot
##############################
obj.plots = {
'robbery': scatter(x=cols['x'],
y=cols['y'],
source=obj.source,
x_axis_label=cols['x'],
y_axis_label=cols['y']),
'pop': scatter(x=cols['pop'],
y=cols['y'],
source=obj.source,
x_axis_label=cols['pop'],
y_axis_label=cols['y'],
title='%s by %s, Adjusted by by %s'%(cols['y'],
cols['pop'], cols['pop'])),
}
obj.update_data()
##############################
## draw inputs
##############################
# bokeh.plotting.scatter
## TODO: refactor so that any number of control variables are created
# automatically. This involves subsuming c['pop'] into c['ctrls'], which
# would be a dictionary mapping column names to their widget titles
pop_slider = obj.make_widget(bkw.Slider, dict(
start=-obj.maxval,
end=obj.maxval,
value=0,
step=1,
title='Population'),
cols['pop'])
##############################
## make layout
##############################
obj.inputs = bkw.VBoxForm(
children=[pop_slider]
)
obj.outputs = bkw.VBoxForm(
children=[obj.plots['robbery']]
)
obj.children.append(obj.inputs)
obj.children.append(obj.outputs)
return obj
update_data
def update_data(self):
"""Update y by the amount designated by each slider"""
logging.debug('update_data')
c = self.cols
## TODO:: make this check for bad input; especially with text boxes
betas = {
varname: getattr(widget, 'value')/self.maxval
for varname, widget in self.widgets.iteritems()
}
df0 = pd.DataFrame(self.df0.data)
adj_y = []
for ix, row in df0.iterrows():
## perform calculations and generate new y's
adj_y.append(self.debias(row))
self.source.data[c['y']] = adj_y
assert len(adj_y) == len(self.source.data[c['x']])
logging.debug('self.source["y"] now contains debiased data')
import pdb; pdb.set_trace()
Note that I am sure that the event handler gets setup and triggered correctly. I just don't know how to make the changed source data reflect in the scatterplot.
I'm searching for the same answers (lack of documentation makes it difficult).
In answer, to question #1, what is the utility of "extra_generated_classes":
tl;dr extra_generated_classes defines a modulename, classname, and parentname used in template generating js/html code, and extends the parent class passed into the app class (usually HBox or VBox in the examples).
Longer answer. Look at the source code in bokeh/server/utils/plugins.py, this is the code that is run on code passed to bokeh-server using the --script command line argument. At the end of plugins.py, you can see that extra_generated_classes is passed to the flask method render_template, which renders a Jinja2 template. Looking inside the template, oneobj.html, extra_generated_classes is an array of arrays of three things: modulename, classname, and parentname, which are passed into bokeh.server.generatejs:
{% block extra_scripts %}
{% for modulename, classname, parentname in extra_generated_classes %}
<script
src="{{ url_for('bokeh.server.generatejs', modulename=modulename, classname=classname, parentname=parentname) }}"
></script>
{% endfor %}
{% endblock %}
bokeh.server.generatejs is a Python code in bokeh/server/views/plugins.py, and only calls render_template for a template app.js, which you can find in bokeh/server/templates. This template takes the modulename, classname, and parentname, and basically creates js code which extends the parentname (e.g. HBox or VBox) to the classname (your app).
I have an add-on configuration page/form generated by plone.app.registry.browser.controlpanel
Using these Docs:
http://plone.org/documentation/kb/how-to-create-a-plone-control-panel-with-plone.app.registry
https://pypi.python.org/pypi/plone.app.registry#control-panel-widget-settings
On this form, I have an integer field:
from zope import schema
from plone.app.registry.browser import controlpanel
class MyAddonSettings(Interface):
partnerId = schema.Int(title=u"Partner Id",
description=u"enter your Partner ID",
required=True,
default=54321)
class SettingsEditForm(controlpanel.RegistryEditForm):
schema = MyAddonSettings
label = u"My settings"
description = u""""""
def updateFields(self):
super(SettingsEditForm, self).updateFields()
def updateWidgets(self):
super(SettingsEditForm, self).updateWidgets()
class SettingsControlPanel(controlpanel.ControlPanelFormWrapper):
form = SettingsEditForm
When the form renders, I get the integer field auto-filled with '54,321' I don't want the comma.
How to I specify "Don't do that!"
So, I think I went pretty deep down the rabbit hole, but here is what I came up with.
1) The default widget for zope.schema.Int is the TextWidget
2) z3c.form.converter.IntegerDataConverter Adapts itself to zope.schema.interfaces.IInt and ITextWidget
3) the IntegerDataConverter calls upon the locale to 'format the integer' for you, giving you a nice pretty representation of an int - with commas.
My choice was to create a new widget 'IntWidget' and a new converter 'NoFormatIntegerDataConverter', adapt these. Then manually set the field in question to my new widget:
I'm sure there is a less 'rabbit hole' way to do this, but I found myself at the bottom, so I completed the journey. I'll let a zope guru follow up with the 'right' way to do it.
=========================
create the new widget based on TextWidget
so we don't tie our new converter to everyone's TextWidget and break someone else's stuff
import zope.interface
import zope.component
import zope.schema.interfaces
import z3c.form.interfaces
from z3c.form.widget import FieldWidget
from z3c.form.browser.text import TextWidget
from z3c.form import converter
class IIntWidget(z3c.form.interfaces.ITextWidget):
"""Int Widget"""
class IntWidget(TextWidget):
zope.interface.implementsOnly(IIntWidget)
klass = u'int-widget'
value = u''
#zope.component.adapter(zope.schema.interfaces.IField,
z3c.form.interfaces.IFormLayer)
#zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
def IntFieldWidget(field, request):
"""IFieldWidget factory for IntWidget."""
return FieldWidget(field, IntWidget(request))
zope.component.provideAdapter(IntFieldWidget)
Create the 'dumb' converter, and adapt it to our new widget 'IntWidget'
class NoFormatIntegerDataConverter(converter.IntegerDataConverter):
""" data converter that ignores the formatter,
simply returns the unicode representation of the integer value
The base class for this calls upon the locale for a formatter.
This completely avoids calling the locale.
"""
zope.component.adapts(zope.schema.interfaces.IInt, IIntWidget)
def toWidgetValue(self, value):
if value is self.field.missing_value:
return u''
#go look at z3c.form.converter.IntegerDataConverter
#to see what it used to return here.
return unicode(value)
zope.component.provideAdapter(NoFormatIntegerDataConverter)
Finally, update the field widget factory to use our new widget
class SettingsEditForm(controlpanel.RegistryEditForm):
...
def updateFields(self):
super(SettingsEditForm, self).updateFields()
self.fields['partnerId'].widgetFactory = IntFieldWidget #<----- Here
...
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
### BEGIN LICENSE
# Copyright (C) 2012 Marios Papachristou mrmarios97#gmail.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
### END LICENSE
import gettext
from gettext import gettext as _
gettext.textdomain('quickbrowser')
from gi.repository import Gtk, WebKit # pylint: disable=E0611
import logging
logger = logging.getLogger('quickbrowser')
from quickbrowser_lib import Window
from quickbrowser.AboutQuickbrowserDialog import AboutQuickbrowserDialog
from quickbrowser.PreferencesQuickbrowserDialog import PreferencesQuickbrowserDialog
# See quickbrowser_lib.Window.py for more details about how this class works
class QuickbrowserWindow(Window):
__gtype_name__ = "QuickbrowserWindow"
def finish_initializing(self, builder): # pylint: disable=E1002
"""Set up the main window"""
super(QuickbrowserWindow, self).finish_initializing(builder)
self.AboutDialog = AboutQuickbrowserDialog
self.PreferencesDialog = PreferencesQuickbrowserDialog
self.goBack = self.builder.get_object('goBack')
self.homeButton = self.builder.get_object('homeButton')
self.refreshButton = self.builder.get_object('refreshButton')
self.goButton = self.builder.get_object('goButton')
self.currentaddresslabel = self.builder.get_object('currentaddresslabel')
self.addressbar = self.builder.get_object('addressbar')
self.viewwindow = self.builder.get_object('viewwindow')
self.goForward = self.builder.get_object('goForward')
self.zoomIn = self.builder.get_object('zoomIn')
self.zoomOut = self.builder.get_object('zoomOut')
self.webview = WebKit.WebView()
self.viewwindow.add(self.webview)
self.webview.show()
def on_addressbar_activate(self, widget):
address = widget.get_text()
self.webview.open(address)
def on_refreshButton_clicked(self, widget):
self.webview.reload()
def on_goBack_clicked(self,widget):
self.webview.go_back();
def on_goForward_clicked(self,widget):
self.webview.go_forward();
def on_zoomIn_activate(self,widget):
self.webview.zoom_in();
def on_zoomOut_activate(self,widget):
self.webview.zoom_out();
def on_goButton_clicked(self,widget):
self.webview.open(self.addressbar.get_text())
I am currently developing a web browser in Python using python-webkit.
The source code above is written in order to manage the main window.
How can I constantly display current URL using webview.get_uri() method return value inside a label?
Thanks in advance
while True:
updateLocationBar() # ;)
Surely the location bar already provides this? You could call your label modifying function wherever self.webview.open() is called by writing a wrapper like
def self.webview.mysuperawesomeopen(uri):
updateLocationBar()
self.webview.open(uri)
and then modifying the calls appropriately. Or if you don't want to change the calls, just move the existing .open() function like
self.webview.openit = self.window.open
def self.window.open(uri):
updateLocationBar()
self.webview.openit(uri)
Edit: You could also use a function decorator on self.window.open() like
def labelled(f):
def labelgo(*args, **kwargs):
updateLocationBar(*args, **kwargs)
self.webview.open(*args, **kwargs)
return labelgo
#labelled # You add this and leave the rest of self.window.open alone
def self.window.open(uri):
...
This would be a better solution if you want to generalise (and you would make the labelled decorator more general - I left this simple as an example).