Reactivity conundrum in shiny; dynamically translating text going into selectInput() - internationalization

I am working on a multilingual shiny app using the shiny.i18n package to translate different parts of the UI. It works really well until I tried to dynamically translate the text inside a specific selectInput menu. Some reproducible code below:
# Translating text of a menu item from data
library(shiny)
library(shiny.i18n)
library(tidyverse)
# Create translation file and save in a directory called trans
system("mkdir trans")
translation_key <- data.frame(es = c("Cambiar de lenguaje", "Seleccione un proyecto", "Todos"),
en = c("Change language", "Select a project", "All")
)
write.csv(translation_key, "trans/translation_key.csv",
row.names = FALSE,
quote = FALSE)
# Create data object
data <- data.frame(project_name =c("Todos","proj1", "proj2"),
n = c(20,30,40)
)
# Setup translator
trans <- Translator$new(translation_csvs_path = "trans")
trans$set_translation_language("es")
# Define UI
ui <- fluidPage(
usei18n(trans),
# Application title
titlePanel("Reactivity test"),
mainPanel(
selectInput('selected_language',
trans$t("Cambiar de lenguaje"),
choices = trans$get_languages()
),
selectInput("project_name",
trans$t("Seleccione un proyecto"),
choices = NULL
),
textOutput("n")
)
)
# server part
server <- function(session, input, output) {
# update languae
observeEvent(input$selected_language, {
update_lang(session, input$selected_language)
print(paste("Language change!", input$selected_language))
})
# change the first element of data$project and translate it! It does not do it!
data_rv <- eventReactive(input$selected_language, {
data$project_name[1] <- trans$t("Todos")
data
})
# update the selection for project name depending on the language
observeEvent(data_rv(), {
updateSelectInput(session, inputId = "project_name",
choices = unique(data_rv()$project_name))
})
# print the n associated with project_name selected
output$n <- renderText({
req(input$project_name)
res <- data_rv() %>% filter(project_name == input$project_name)
res$n
})
}
# Run the application
shinyApp(ui = ui, server = server)
I attempt the translation of the first element of data$project_name inside an eventReactive call that returns the modified translated object. The problem is that the translation does not happen until after the menu for project_name is rendered (see screenshot below).
The application starts by default in Spanish (es) and when changed to English (en), everything is translated except the first item in the Select a project menu (it should say All instead of Todos).
I have found a solution using uiOutput and renderUI but the application I am working on has a very complex layout of panels and tabs and I rather not rework all the code if I can help it. Can someone recommend a workaround that works from the browser side. Thanks,

An alternative in this case could be making data_rv a reactive expression with a req() instead of an eventReactive. It may also keep it simple for your purpose.
I tried it like this with your example:
# change the first element of data$project and translate it!
data_rv <- reactive({
req(input$selected_language)
data$project_name[1] <- trans$t(data$project_name[1])
data
})
Seems it checks out!
reactivity question image here
Note: I translated reactivity test to prueba de reactividad for clarity but is not in the original reproducible example

Related

PyQGIS, custom processing algorithm: How to use selected features only?

I want to create a custom processing algorithm with PyQGIS, which is able to take a vector layer as input (in this case of type point) and then do something with it's features. It's working well as long as I just choose the whole layer. But it doesn't work if I'm trying to work on selected features only.
I'm using QgsProcessingParameterFeatureSource to be able to work on selected features only. The option is shown and I can enable the checkbox. But when I'm executing the algorithm, I get NoneType as return of parameterAsVectorLayer.
Below you'll find a minimal working example to reproduce the problem:
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingParameterFeatureSource
)
name = "selectedonly"
display_name = "Selected features only example"
group = "Test"
group_id = "test"
short_help_string = "Minimal working example code for showing my problem."
class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return ExampleProcessingAlgorithm()
def name(self):
return name
def displayName(self):
return self.tr(display_name)
def group(self):
return self.tr(group)
def groupId(self):
return group_id
def shortHelpString(self):
return self.tr(short_help_string)
def initAlgorithm(self, config=None):
self.addParameter(
QgsProcessingParameterFeatureSource(
'INPUT',
self.tr('Some point vector layer.'),
types=[QgsProcessing.TypeVectorPoint]
)
)
def processAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsVectorLayer(
parameters,
'INPUT',
context
)
return {"OUTPUT": layer}
If I'm working on the whole layer, the output is {'OUTPUT': <QgsVectorLayer: 'Neuer Temporärlayer' (memory)>}, which is what I would expect.
If I'm working on selected features only, my output is {'OUTPUT': None}, which doesn't makes sense to me. I selected some of the features before executing of course.
I'm using QGIS-version 3.22 LTR, if it's relevant.
Can anybody tell me what I'm doing wrong?
I would suggest you trying to use the method 'parameterAsSource' in the 'processAlgorithm' method.
layer = self.parameterAsSource(
parameters,
'INPUT',
context
)

Any ar js multimarkers learning tutorial?

I have been searching for ar.js multimarkers tutorial or anything that explains about it. But all I can find is 2 examples, but no tutorials or explanations.
So far, I understand that it requires to learn the pattern or order of the markers, then it stores it in localStorage. This data is used later to display the image.
What I don't understand, is how this "learner" is implemented. Also, the learning process is only used once by the "creator", right? The output file should be stored and then served later when needed, not created from scratch at each person's phone or computer.
Any help is appreciated.
Since the question is mostly about the learner page, I'll try to break it down as much as i can:
1) You need to have an array of {type, URL} objects.
A sample of creating the default array is shown below (source code):
var markersControlsParameters = [
{
type : 'pattern',
patternUrl : 'examples/marker-training/examples/pattern-files/pattern-hiro.patt',
},
{
type : 'pattern',
patternUrl : 'examples/marker-training/examples/pattern-files/pattern-kanji.patt',
}]
2) You need to feed this to the 'learner' object.
By default the above object is being encoded into the url (source) and then decoded by the learner site. What is important, happens on the site:
for each object in the array, an ArMarkerControls object is created and stored:
// array.forEach(function(markerParams){
var markerRoot = new THREE.Group()
scene.add(markerRoot)
// create markerControls for our markerRoot
var markerControls = new THREEx.ArMarkerControls(arToolkitContext, markerRoot, markerParams)
subMarkersControls.push(markerControls)
The subMarkersControls is used to create the object used to do the learning. At long last:
var multiMarkerLearning = new THREEx.ArMultiMakersLearning(arToolkitContext, subMarkersControls)
The example learner site has multiple utility functions, but as far as i know, the most important here are the ArMultiMakersLearning members which can be used in the following order (or any other):
// this method resets previously collected statistics
multiMarkerLearning.resetStats()
// this member flag enables data collection
multiMarkerLearning.enabled = true
// this member flag stops data collection
multiMarkerLearning.enabled = false
// To obtain the 'learned' data, simply call .toJSON()
var jsonString = multiMarkerLearning.toJSON()
Thats all. If you store the jsonString as
localStorage.setItem('ARjsMultiMarkerFile', jsonString);
then it will be used as the default multimarker file later on. If you want a custom name or more areas - then you'll have to modify the name in the source code.
3) 2.1.4 debugUI
It seems that the debug UI is broken - the UI buttons do exist but are nowhere to be seen. A hot fix would be using the 'markersAreaEnabled' span style for the div
containing the buttons (see this source bit).
It's all in this glitch, you can find it under the phrase 'CHANGES HERE' in the arjs code.

How to change AwesomeWM tag names?

I am trying to customize my Awesome Window Manager to change the tag numbers into Roman numbers (changing 1 for I, 2 for II...). In order to achieve this, I am modifying my /etc/xdg/awesome/rc.lua file, specially the {{tags}} section.
I have found this blog post, in which he manages to edit the tag names at will, have a look at the top left corner:
I also read the rc.lua file attached to the theme, and realized the technique used for what I want to do is a for loop in combination with some tables.
This is the code snippet of interest in the file:
-- {{{ Tags
-- Define a tag table which hold all screen tags.
tags = {}
tagnames = { "irc", "mpd", "net", "usr", "png", "msg", }
taglayouts = {
awful.layout.suit.tile.top,
awful.layout.suit.tile.bottom,
awful.layout.suit.floating,
awful.layout.suit.fair,
awful.layout.suit.floating,
awful.layout.suit.floating }
for s = 1, screen.count() do
-- Each screen has its own tag table.
tags[s] = {}
for tagnumber = 1, 6 do
-- Add tags and name them.
tags[s][tagnumber] = tag(tagnames[tagnumber])
-- Add tags to screen one by one, giving them their layouts at the same time.
tags[s][tagnumber].screen = s
awful.layout.set(taglayouts[tagnumber], tags[s][tagnumber])
end
-- I'm sure you want to see at least one tag.
tags[s][1].selected = true
end
-- }}}
...and this is my rc.lua file:
-- {{{ Tags
-- Define a tag table which hold all screen tags.
tags = {}
tagnames = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", }
taglayouts = {
awful.layout.suit.tile.top,
awful.layout.suit.tile.bottom,
awful.layout.suit.floating,
awful.layout.suit.fair,
awful.layout.suit.floating,
awful.layout.suit.floating }
for s = 1, screen.count() do
-- Each screen has its own tag table.
-- tags[s] = awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8",$
tags[s] = {}
for tagnumber = 1, 9 do
tags[s][tagnumber] = tag(tagnames[tagnumber])
tags[s][tagnumber].screen = s
awful.layout.set(taglayouts[tagnumber], tags[s][tagnumber])
end
tags[s][1].selected = true
end
--- }}}
As you can see, they are pretty the same, with the difference that I have nine tags instead of six (I changed the code according to it). When I try to debug the setup using Xephyr, an error appears in the console and I am only able to see my wallpaper:
error while running function
stack traceback:
[C]: in global 'tag'
/etc/xdg/awesome/rc.lua:100: in main chunk
error: /etc/xdg/awesome/rc.lua:100: bad argument #2 to 'tag' (table expected, got string)
error while running function
stack traceback:
[C]: in global 'tag'
/etc/xdg/awesome/rc.lua:100: in main chunk
error: /etc/xdg/awesome/rc.lua:100: bad argument #2 to 'tag' (table expected, got string)
E: awesome: main:605: couldn't find any rc file
I can't see where the error is, as I am not able to detect any language violation in the error line tags[s][tagnumber] = tag(tagnames[tagnumber]): it's just filling the tags array with my custom names, telling it to treat them as a tag and not as a random string.
UPDATE: I have just realized that there are six layouts in taglayouts, the same number as tags in the original Lua file. I think I should have nine tag layouts, but I don't know which one should I add. Also, I don't see this as a critical impediment for the code to compile properly, as the error line does not have anything to do with the layout list.
UPDATE 2: Added three more awful.layout.suit.floating to taglayouts. Same error.
Following another answer, I replaced my {Tags} section with:
-- {{{ Tags
-- Define a tag table which hold all screen tags.
tagnum = { "I", "II", "III", "IV", "V", "VI", "VII",
"VIII", "IX" }
for i = 1, 9 do
awful.tag.add((tagnum[i]), {
layout = awful.layout.suit.tile,
master_fill_policy = "master_width_factor",
gap_single_client = true,
gap = 15,
screen = s,
})
end
-- }}}
This creates i number of tags, their name defined in the tagnum table. This is only useful if you want to create identical tags, but it will always be much cleaner than having to type i definitions.
A MUCH BETTER, CLEANER WAY:
The initial solution was useful, but it had a problem: when starting AwesomeWM, you won't appear in a defined tag, but in all of them at the same time. That is, if you open a terminal, you will open it in every tag you have unless you previously selected one with Mod4+TagNum (following default conf.).
Trying to solve this problem, I compared the default configuration file with the modded one, and I realized it all worked well in the default one. So I started modifying the code in order to find a solution. In all, I have discovered that with a minimal modification of the default code you are able to customize your tag names at will. This is how I did it:
-- {{{ Tags
tags = {}
-- Generates tags with custom names
for s = 1, screen.count() do
tags[s] = awful.tag({ "I", "II", "III", "IV", "V", "VI", "VII", "IX" }),
end
-- }}}
P.S. I keep the old solution in case someone would wish to use the code for another purpose.
Not an official answer yet, but yesterday I wrote more doc about this:
https://github.com/awesomeWM/awesome/pull/1279/files#diff-df495cc7fcbd48cd2698645bca070ff9R39
It is for Awesome 4.0, but in this case not much changed, so the example is almost valid (the gap property is not available in 3.4/3.5).
Also, if you wish to setup complex tags, I would suggest my Tyrannical module (Awesome 3.5+) or Shifty (Awesome 3.2-3.4). It is designed to make this much easier.

web2py A() link handling with multiple targets

I need to update multiple targets when a link is clicked.
This example builds a list of links.
When the link is clicked, the callback needs to populate two different parts of the .html file.
The actual application uses bokeh for plotting.
The user will click on a link, the 'linkDetails1' and 'linkDetails2' will hold the script and div return from calls to bokeh.component()
The user will click on a link, and the script, div returned from bokeh's component() function will populate the 'linkDetails'.
Obviously this naive approach does not work.
How can I make a list of links that when clicked on will populate two separate places in the .html file?
################################
#views/default/test.html:
{{extend 'layout.html'}}
{{=linkDetails1}}
{{=linkDetails2}}
{{=links}}
################################
# controllers/default.py:
def test():
"""
example action using the internationalization operator T and flash
rendered by views/default/index.html or views/generic.html
if you need a simple wiki simply replace the two lines below with:
return auth.wiki()
"""
d = dict()
links = []
for ii in range(5):
link = A("click on link %d"%ii, callback=URL('linkHandler/%d'%ii), )
links.append(["Item %d"%ii, link])
table = TABLE()
table.append([TR(*rows) for rows in links])
d["links"] = table
d["linkDetails1"] = "linkDetails1"
d["linkDetails2"] = "linkDetails2"
return d
def linkHandler():
import os
d = dict()
# request.url will be linked/N
ii = int(os.path.split(request.url)[1])
# want to put some information into linkDetails, some into linkDiv
# this does not work:
d = dict()
d["linkDetails1"] = "linkHandler %d"%ii
d["linkDetails2"] = "linkHandler %d"%ii
return d
I must admit that I'm not 100% clear on what you're trying to do here, but if you need to update e.g. 2 div elements in your page in response to a single click, there are a couple of ways to accomplish that.
The easiest, and arguably most web2py-ish way is to contain your targets in an outer div that's a target for the update.
Another alternative, which is very powerful is to use something like Taconite [1], which you can use to update multiple parts of the DOM in a single response.
[1] http://www.malsup.com/jquery/taconite/
In this case, it doesn't look like you need the Ajax call to return content to two separate parts of the DOM. Instead, both elements returned (the script and the div elements) can simply be placed inside a single parent div.
# views/default/test.html:
{{extend 'layout.html'}}
<div id="link_details">
{{=linkDetails1}}
{{=linkDetails2}}
</div>
{{=links}}
# controllers/default.py
def test():
...
for ii in range(5):
link = A("click on link %d" % ii,
callback=URL('default', 'linkHandler', args=ii),
target="link_details")
...
If you provide a "target" argument to A(), the result of the Ajax call will go into the DOM element with that ID.
def linkHandler():
...
content = CAT(SCRIPT(...), DIV(...))
return content
In linkHandler, instead of returning a dictionary (which requires a view in order to generate HTML), you can simply return a web2py HTML helper, which will automatically be serialized to HTML and then inserted into the target div. The CAT() helper simply concatenates other elements (in this case, your script and associated div).

Displaying Docbook section titles in TextMate's function popup

I'm working on a bundle for DocBook 5 XML, which frequently includes content like:
<section>
<title>This is my awesome Java Class called <classname>FunBunny</classname></title>
<para>FunBunny is your friend.</para>
</section>
I want the titles for sections to appear in the function popup at the bottom of the window. I have this partially working using the following bundle items.
Language Grammar:
{ patterns = (
{ name = 'meta.tag.xml.docbook5.title';
match = '<title>(.*?)</title>';
/* patterns = ( { include = 'text.xml'; } ); */
},
{ include = 'text.xml'; },
);
}
Settings/Preferences Item with scope selector meta.tag.xml.docbook5.title:
{ showInSymbolList = 1;
symbolTransformation = 's/^\s*<title\s?.*?>\s*(.*)\s*<\/title>/$1/';
}
The net effect of this is that all title elements in the document are matched and appear in the function popup, excluding the <title></title> tag content based on the symbolTransformation.
I would be happy with this much functionality, since other interesting things (like figures) tend to have formal titles, but there is one issue.
The content of the title tags is not parsed and recognized according to the rest of the text.xml language grammar. The commented-out patterns section in the above language grammar does not have the desired effect of fixing this issue -- it puts everything into the meta.tag.xml.docbook5.title scope.
Is there a way to get what I want here? That is, the content of the title elements, optionally just for section titles, in the function popup and recognized as normal XML content by the parser.
In TextMate grammars you need to use the begin/end type rules instead of match type rules, if you want to 'match within a match'. (You can actually use a match as well, but then you need to use currently undocumented behavior, available only for TextMate 2)
{ patterns = (
{ name = 'meta.tag.xml.docbook5.title';
begin = '<title>';
end = '</title>';
patterns = ( { include = 'text.xml'; } );
},
{ include = 'text.xml'; },
);
}
This has the added benefit of allowing <title>...</title> that span more than one line.

Resources