So I have a Gtk.Button. I want to add a accelator (shortcut key), such as Ctrl+O.
If I had a label, I could use a underscore under the letter to bind, such as "_Open".
My button doesn't have any label though, it has a icon. It is a button I have in my Gtk.HeaderBar.
How do I place a accelerator on a Gtk.Button with a icon but no label?
i think accelerators are used for Menu items, but you can set an underscore mnemonic such as "_Open", with Gtk.Button.new_with_mnemonic(label)
If using XML then add a <accelerator> element to the object.
<object class="GtkButton" id="foo_button">
<property name="visible">1</property>
<property name="can-focus">1</property>
<signal name="clicked" handler="_on_foo_button_clicked" swapped="no"/>
<accelerator key="n" signal="activate" modifiers="GDK_CONTROL_MASK"/>
<child>
<object class="GtkImage">
<property name="visible">1</property>
<property name="icon-name">folder-new-symbolic</property>
</object>
</child>
</object>
When setting an accelerator/shortcut via c-API, I found the following solution:
/* init the keyboard shortcuts */
{
#if ( GTK_MAJOR_VERSION >= 4 )
(*this_).keyboard_shortcut_ctrl = GTK_SHORTCUT_CONTROLLER(gtk_shortcut_controller_new());
gtk_widget_add_controller( (*this_).window, GTK_EVENT_CONTROLLER((*this_).keyboard_shortcut_ctrl) );
#else
(*this_).keyboard_shortcut_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW( (*this_).window ), (*this_).keyboard_shortcut_group);
#endif
}
An then for each button:
(*this_).edit_undo_icon = gtk_image_new_from_pixbuf( gui_resources_get_edit_undo( res ));
gtk_widget_set_size_request( GTK_WIDGET((*this_).edit_undo_icon), 32 /*=w*/ , 32 /*=h*/ );
(*this_).edit_undo = GTK_BUTTON(gtk_button_new());
gtk_button_set_image( GTK_BUTTON((*this_).edit_undo), (*this_).edit_undo_icon );
gtk_widget_set_tooltip_text( GTK_WIDGET((*this_).edit_undo), "Undo (Ctrl-Z)" );
#if ( GTK_MAJOR_VERSION >= 4 )
GtkShortcutTrigger *undo_trig = gtk_shortcut_trigger_parse_string( "<Control>Z" );
GtkShortcutAction *undo_act = gtk_callback_action_new( &gui_toolbox_undo_shortcut_callback,
&((*this_).tools_data),
NULL
);
GtkShortcut* ctrl_z = gtk_shortcut_new_with_arguments( undo_trig,
undo_act,
NULL /* = format_string */
);
gtk_shortcut_controller_add_shortcut( (*this_).keyboard_shortcut_ctrl, ctrl_z );
#else
gtk_widget_add_accelerator( GTK_WIDGET((*this_).edit_undo),
"clicked",
(*this_).keyboard_shortcut_group,
GDK_KEY_z,
GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE
);
#endif
Note, that the gui_toolbox_undo_shortcut_callback has a different signature then the button-clicked signal.
Related
I have this field with a method in my model which aims to detect the changes in the many2one:
manytoone = fields.Many2one(comodel_name="product.template", string="Many to One",
required=False, )
cambio_realizado = fields.Boolean(string="Cambios en Many to One", default=False)
#api.onchange('manytoone')
def _detectar_cambio(self):
self.cambio_realizado = True
No works the default=False in Boolean Field. Always start in True.
And the other problem is that no works a computed field:
#api.multi
def computer_function(self):
self.computation = 243+234
computation = fields.Float(string="Result: ", compute=computer_function, readonly=True)
The result is zero... always in the view.
identation ok, and the model is inherited
My Class and my view with the tips:
class CustomModule(models.Model):
_inherit = 'sale.order'
name = fields.Char('campos relacionales, decoradores onchange y funcion computar')
manytoone = fields.Many2one(comodel_name="product.template", string="Many to One",
required=False, )
cambio_realizado = fields.Boolean(string="Cambios en Many to One")
#api.onchange('manytoone')
def _detectar_cambio(self):
self.cambio_realizado = True
onetomany = fields.One2many(comodel_name="sale.order", inverse_name="manytoone",
string="One to Many", required=False, )
manytomany = fields.Many2many(comodel_name="sale.order",
relation="sale_handler",
column1="order_id", column2="order_handler_id",
string="Many to Many", )
#api.depends('computation')
def computer_function(self):
for record in self:
record.computation = 300
computation = fields.Float(string="Result: ", compute=computer_function, readonly=True)
<odoo>
<!-- Inherit Form View to Modify it -->
<record id="custom_view_custom" model="ir.ui.view">
<field name="name">custom.view.custom</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet" position="inside">
<group>
<field name="manytoone"/>
<field name="onetomany"/>
<field name="manytomany"/>
</group>
<group>
<field name="computation"/>
<field name="cambio_realizado"/>
</group>
</xpath>
</field>
</record>
</odoo>
#api.one
def computer_function(self):
self.computation = 243+234
computation = fields.Float(string="Result: ", compute=computer_function, readonly=True)
use #api.one in your code
1- onchnage, when you try to create a new record, the onchange called automatically then cambio_realizado set to true
#api.onchange('manytoone')
def _detectar_cambio(self):
for o in self:
if o.manytoone:
o.cambio_realizado = True
2- in compute it's preferally to use api.depends('somefield'), but the code bellow may be work for you, use a loop and don't use readonly
#api.multi
def computer_function(self):
for o in self:
o.computation = 243+234
computation2 = fields.Float(string="Result: ", compute=computer_function)
This code can only capture the date, I want to capture all the time of day (sol.datahorafim = 00:00:00 to dataInicial = 23:55:55). How do I do this?
EDIT WITH PARAM from xml and function.
XML:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<PARAMETERS>
<PARAM name='dataInicial' type='java.sql.Date' size='10' value='' description='citcorpore.comum.datainicio' fix='false' mandatory='true' reload='true' />
<PARAM name='dataFinal' type='java.sql.Date' size='10' value='' description='citcorpore.comum.datafim' fix='false' mandatory='true' default='{TODAY}'/>
<PARAM name='contrato' type='java.lang.Integer' typeHTML='select' size='0'value='' description='Contrato' fix='false' mandatory='false' reload='true' onload='true'>
<OPTION value='-1' text='Todos'/>
<OPTIONS type='SQL' onload='true'>
SELECT idcontrato, numero+' '+nomefantasia
FROM contratos ct, clientes cl
where cl.idcliente = ct.idcliente
order by 2
</OPTIONS>
</PARAM>
<PARAM name='status' type='java.lang.Integer' typeHTML='select' size='0'value='' description='Status' fix='false' mandatory='false' reload='true' onload='true'>
<OPTION value='-1' text='Todos'/>
<OPTION value='6' text='Fechada'/>
<OPTION value='3' text='Cancelada'/>
</PARAM>
<PARAM name='executor' type='java.lang.String' value='' typeHTML='text' size='10' description='Executor' fix='false' mandatory='false'/>
<PARAM name='quantidade' type='java.lang.Integer' value='' typeHTML='text' size='10' description='Qtd Bases' fix='false' mandatory='false'/>
</PARAMETERS>
Function:
montarSql = function() {
var sql = new importNames.StringBuilder();
var parametroContrato = hashParametros.get("PARAM.contrato");
var parametroExecutor = hashParametros.get("PARAM.executor");
var parametroQuantidade = hashParametros.get("PARAM.quantidade");
var parametroStatus = hashParametros.get("PARAM.status");
sql.append("select idsolicitacaoservico,nomeservico,idstatus,qtd_conhecimentos, nome, grupo ");
sql.append("from ");
sql.append("(select con.numero,ser.nomeservico,sol.idsolicitacaoservico, sol.idstatus,");
sql.append("(select count(idBaseConhecimento) from conhecimentosolicitacaoservico where ");
sql.append("idsolicitacaoservico = sol.idsolicitacaoservico) qtd_conhecimentos, usu.nome, gr.nome grupo ");
sql.append("from solicitacaoservico sol INNER JOIN servicocontrato scn ON sol.idservicocontrato = scn.idservicocontrato ");
sql.append("JOIN servico ser ON scn.idservico = ser.idservico ");
sql.append("JOIN contratos con ON scn.idcontrato = con.idcontrato ");
sql.append("join bpm_itemtrabalhofluxo ite on ite.iditemtrabalho = sol.idtarefaencerramento ");
sql.append("left join usuario usu on usu.idusuario = ite.idresponsavelatual ");
sql.append("left join grupo gr on gr.idgrupo = sol.idgrupoatual ");
sql.append("where ")
if (parametroExecutor != ""){
sql.append(" emp.nome like '%");sql.append(parametroExecutor);sql.append("%' and ");
}
if(parametroContrato!="-1"){
sql.append("(con.idcontrato = {PARAM.contrato}) and ");
}
if(parametroStatus !="-1"){
sql.append(" sol.idstatus = ");sql.append(parametroStatus );sql.append(" and ");
}
sql.append(" sol.datahorafim BETWEEN {PARAM.dataInicial} and {PARAM.dataFinal} ) bs ");
I try see documentation but nothing exactly what I want
Your description is not quite clear, but maybe you want to compare like this:
WHERE date_trunc('day', sol.datahorafim) BETWEEN ... AND ...
I am building a custom module in Magento 2 that has a custom discount. I am trying to copy the discount from quote, quote item to order and order item.
In Magento 1, I declare the config.xml like this:
<fieldsets>
<sales_convert_quote_address>
<custom_discount_amount><to_order>*</to_order></custome_discount_amount>
<base_custom_discount_amount><to_order>*</to_order></base_custome_discount_amount>
</sales_convert_quote_address>
<sales_convert_quote_item>
<custome_discount_amount><to_order_item>*</to_order_item></custome_discount_amount>
<base_custom_discount_amount><to_order_item>*</to_order_item></base_custom_discount_amount>
</sales_convert_quote_item>
</fieldsets>
and my custom discount amount was copied to tables: sales_flat_order and sales_flat_order_item as expected.
In Magento 2, I created a file named fieldset.xml with this code:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Object/etc/fieldset.xsd">
<scope id="global">
<fieldset id="sales_convert_quote_item">
<field name="custom_discount_amount">
<aspect name="to_order_item" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order_item" />
</field>
</fieldset>
<fieldset id="sales_convert_quote_address">
<field name="custom_discount_amount">
<aspect name="to_order" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order" />
</field>
</fieldset>
</scope>
</config>
but there is no success.
What else do I need to do in Magento 2 to make it work? Can you guys please help me?
In Magento 2 without using fieldset you can also copy custom data from quote item to order item by using plugin.
create di.xml in your module etc folder.
app/code/Vender/Yourmodule/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
<plugin name="cedapi_quote_to_order_item" type="Vender\Yourmodule\Model\Plugin\Quote\QuoteToOrderItem"/>
</type>
</config>
Create a class to your module and define a function. app/code/Vender/Yourmodule/Model/Plugin/Quote
Create QuoteToOrderItem.php file
<?php
namespace Vender\Yourmodule\Model\Plugin\Quote;
use Closure;
class QuoteToOrderItem
{
/**
* #param \Magento\Quote\Model\Quote\Item\ToOrderItem $subject
* #param callable $proceed
* #param \Magento\Quote\Model\Quote\Item\AbstractItem $item
* #param array $additional
* #return \Magento\Sales\Model\Order\Item
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundConvert(
\Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
Closure $proceed,
\Magento\Quote\Model\Quote\Item\AbstractItem $item,
$additional = []
) {
/** #var $orderItem \Magento\Sales\Model\Order\Item */
$orderItem = $proceed($item, $additional);//result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
$orderItem->setCustomDesign($item->getCustomDesign());//set your required
return $orderItem;// return an object '$orderItem' which will replace result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
}
}
After spend some time and research issue, i stucked here:
Magento\Quote\Model\QuoteManagement.php
line 446
public function mergeDataObjects(
$interfaceName,
$firstDataObject,
$secondDataObject
) {
if (!$firstDataObject instanceof $interfaceName || !$secondDataObject instanceof $interfaceName) {
throw new \LogicException('Wrong prototype object given. It can only be of "' . $interfaceName . '" type.');
}
$secondObjectArray = $this->objectProcessor->buildOutputDataArray($secondDataObject, $interfaceName);
$this->_setDataValues($firstDataObject, $secondObjectArray, $interfaceName);
return $this;
}
Which ignores converted attributes because logic of merging based on presence getters and setters of target model\interface. So, if you converting attributes which haven't setters and getters in target model they will be ignored here:
Magento\Framework\Reflection\DataObjectProcessor.php line 75
public function buildOutputDataArray($dataObject, $dataObjectType)
{
$methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType);
$outputData = [];
/** #var MethodReflection $method */
foreach (array_keys($methods) as $methodName) {
if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) {
continue;
}
$value = $dataObject->{$methodName}();
$isMethodReturnValueRequired = $this->methodsMapProcessor->isMethodReturnValueRequired(
$dataObjectType,
$methodName
);
Maybe you might to use observer or plugin to avoid this problem. (issue encountered in 2.0.6 Magento version)
For anybody looking at this in the future, the fact that the fieldset XML doesn't work has been acknowledged by Magento as a bug. There is a core patch available in the ticket (not reproduced here since it may need to be tweaked with new Magento versions).
https://github.com/magento/magento2/issues/5823
I'm trying to realize mapnik-XML block in TileMill CartoCSS like this (!!! symbolizers in rules aren't overlaping !!!):
<Rule>
<Filter>[attr_1]=value_1 and [attr_2]=value_2</Filter>
<LineSymbolizer ... />
</Rule>
.
.
<Rule>
<Filter>[attr_1]=value_3 and [attr_2]=value_4</Filter>
<PolygonSymbolizer .../>
</Rule>
<Rule>
<ElseFilter/>
<TextSymbolizer .../>
</Rule>
And I didn't find in documentation how to make the else-filter.
[attr_1 = value_1] [attr_2 = value_2] {
::line-symb {
line-color: #color;
}
}
.
.
[attr_1 = value_3] [attr_2 = value_4] {
::polygon-symb {
polygon-fill: #color;
}
}
// <ElseFilter/> ...
Any advice?
There's no equivalent to else: think of CartoCSS as CSS, which also doesn't have an 'else': you would just write a rule that applies to the general case. So, if you're styling a layer by its attributes, the else is expressed by making a rule that just applies to the layer, with no attribute filters.
I need a way to add an XML attribute 'POSITON' to an XML element 'node' conditionally. Currently I'm doing the condition check first and then creating the node.
if (lvl == 2)
node = <node COLOR={ color } CREATED={ epochTimeMillis } ID={ idGen } POSITION={ position } LINK={ link } MODIFIED={ epochTimeMillis } STYLE="bubble" TEXT={ f.getName() }>
<edge COLOR={ color } STYLE={ style } WIDTH={ width }/>
</node>
else
node = <node COLOR={ color } CREATED={ epochTimeMillis } ID={ idGen } LINK={ link } MODIFIED={ epochTimeMillis } STYLE="bubble" TEXT={ f.getName() }>
<edge COLOR={ color } STYLE={ style } WIDTH={ width }/>
</node>
}
Using "null" is not a good practice, but in this case it would help you:
scala> <root ta={ if (true) "true" else null } fa={ if (false) "false" else null } />
res0: scala.xml.Elem = <root ta="true" ></root>
A slightly cleaner way to do the same thing #senia suggests is:
val posOpt = if (lvl2) Some(myPosition) else None
val xml = <mydata position={posOpt orNull}/>
One way is to create the snippet before:
val pos =
if (lvl == 2) {
"position = ..."
} else {
""
}
and to always insert it in the result.
This could by extended by using an Option with embedded map in combination with string interpolation.
val pos =
if (lvl == 2) {
Some(position)
} else {
None
}
with
pos.map(v => s"position = $v")