I am trying to create a table using TCL/TK and am building it without using the Tktable widget.
I am using a canvas to build the table from ground up using the approach provided in:
simple Tktable
I have a whole bunch of customizations which (so far) I feel is easier if I build the table in a canvas and am hence using this approach instead of Tktable.
My table will be filled with read only text widgets, editable text widgets and some drop down menus.
Here is my question:
1) How can I allow a user to select multiple text widgets at the same time and retrieve the selection? Such as say, the user selects an entire row/col etc.
Please see the simplified code below:
package require Tk
proc makeWindow {} {
set toplevelWindow .gui
destroy $toplevelWindow
## Make the toplevel window
toplevel $toplevelWindow
wm title $toplevelWindow "Test case 1"
wm minsize $toplevelWindow 200 200
set pathName $toplevelWindow.testMultiSelection
## Create the canvas where I build the table
destroy $pathName
frame $pathName
set col 0
for {set i 0} {$i < 4} {incr i} {
set w "$pathName\_$i"
destroy $w
text $w -width 9 -height 1 -state normal
$w insert end $i
$w configure -state disabled
grid $w -row $i -column $col -sticky ew
}
grid config $toplevelWindow.testMultiSelection -column 0 -row 0 -sticky w
}
makeWindow
In the above example, I expect to select a few numbers (basically something like the ctrl+select) and somehow store the selection somewhere and retrieve them for later use.
Please let me know if the condensed test-case above is not clear.
EDIT 1
Forgot to add that I know how to retrieve single text widget selections using the selection get command.
Selections are restricted to a single widget at a time by default. If you turn off exporting of the selection at the widget level for each widget (configure -exportselection 0) then you can take over the management of that. However, some platforms also don't display the selection unless the widget has focus (due to platform GUI rules) so you might well also need to manage a text tag to apply the look of the selection. (The sel tag is the selection; it's managed specially, but you can copy its look fairly easily.)
Remember that you can embed widgets inside both canvases and text widgets (if the sub-widgets have names that make them child of the container). The window subcommand and/or item type is what you're looking for there if you're going that route.
The other big thing is managing the clipboard. You'll want to take that over explicitly. The usual command for that is clipboard, though the selection command can also be used (it gives access to the lower-level parts of the clipboard/selection mechanism; the clipboard is the CLIPBOARD selection).
Understand this, what you're doing is complicated and likely to take quite a bit of time and effort. Ask yourself whether your project is really justified in this level of complexity.
Gist of this idea is to use text tags to mark the selected text so you not only get the appearance of selected text you also mark that text as "selected". Then your copy operation merely iterates over all text widgets and gets the contents if it possesses the tag "selected". You can unselect selected text as you get the contents by removing the tag or as I have done below where the button command replaces itself with its own undo command.
So the flow is click on the column button, all text widgets in that column have their contents tagged as selected (selected text is white background & black foreground and so looks selected ) click on a popup menu or a menu button that accesses all selected items and performs operation on the contents then click on column button to unselect the column. all selected text widgets have the selected tag removed with restores the default look of the text.
Assuming you click the top of the column ( I assume its a button ) and you know the text widgets in that column you can tag the text in each text widget with a tag to indicate it is selected. Then to copy you can iterate over all text widgets and pull the content in each text widget that is tagged. To unselect iterate over all text widgets and untag the content. In the code below Im assuming that I have the following functions : getAllTextWindowPaths - gives me list of all textwidgets in the canvas, getAllTextPathsInColumn colnumber - gives me list of all textwidgets in canvas in column $colnumber.
# configure tags for all your text windows
foreach t [ getAllTextWindowPaths ] {
$t tag configure selected -background white -foreground black
}
# configure the column select button
button .canvas.button.column1 -text {1} -command [ list clickColumn .canvas.button.column1 1 ]
proc selectText { textpath { select 1 } } {
if { $select == 1 } {
$textpath tag add 1.0 end selected
} else {
$textpath tag remove 1.0 end selected
}
}
proc clickColumn { colpath colnum } {
# now call this click handler for for your column of textwidgets
# you can make same for a row
foreach t [getTextPathsInColumn $colnum ] {
selectText $t 1
}
$colpath configure -command [list unclickColumn $colpath $colnum ]
}
proc unclickColumn { colpath colnum } {
# now call this click handler for for your column of textwidgets
# you can make same for a row
foreach t [getTextPathsInColumn $colnum ] {
selectText $t 1
}
$colpath configure -command [list clickColumn $colpath $colnum ]
}
proc getSelectedContents { } {
# im adding to a list all the seleted contents but you can arrange
# it anyway you want
set retval {}
foreach t [getAllTextWindowPaths ] {
if { "selected" in [$t tag names 1.0 ] } {
lappend retval [ $t get 1.0 end ]
}
}
# optional call selectText $t 0 here foreach selected widget
# to clear selection or handle the column click handler again
return $retval
}
Related
I am working on a Dash application for performance monitoring using python and plotly.
I have 18 dbc tabs .
In each tab i have a tab_content. Each tab_content has :
a dbc.Card with dbc columns (graphs embedded in the columns).
an image button for toggling between showing the graphs in a grid and showing them one on top of each other.
Each time the user presses the toggle button i want to trigger a callback that changes the following properites:
the 'xl' property of the dbc.columns for the viewed tab. The number of columns differ per tab depending on the number of charts . For example, first tab "CSSR Voce" has a button 'btn-1', and 2 dbc.columns with id's : 'g11', 'g12' (each column has a dcc.Graph embedded in it )
the image of the button . I have 2 images : 1 and 2. First time, the button loads with image 1. When user presses it switches to image 2.
the width of the dbc.Card embeding the dbc.Columns When showing graphs on top of each other i change the width to 1000. When showing the graps one NEXT to each other i change the width to 2000.
My approach was like this:
Because i have 18 toggler buttons i declared:
18 id's with {index:''} for the image and 18 id's for the buttons identifiers and wrote one single callback for all the 18 tabs using MATCH.
The problem:: I can use MATCH to reference the buttons, images and card styles because these have only one index per callback but when it comes to updating the 'xl' column i cannot use MATCH because each tab has more than one graph. So , would not know how to do a one-2-one index match in my callback.
When I write the callback I would need a variable number of outputs for updating the "xl" of each tab's dbc columns for each tab. I tried using a comprehension but cannot pass the variable 'nbr' outside the decorated function and into the decorator's arguments ( expected that not to work but tried it annyway)
The callback i wrote:
#app.callback(
[Output(f"{g}", 'xl') for g in tabs_graphs[nbr] +
[Output({'name':'card','index':MATCH}, 'style'), Output({'name':'img','index':MATCH}, 'src')],
Input({'name':'btn','index':MATCH}, 'n_clicks')
)
def grid(n_clicks):
tabs_graphs = [
['g11', 'g12'],
['g21', 'g22'],
['g31'],
['g41', 'g42', 'g43'],
['g51', 'g52'],
['g61', 'g62', 'g63'],
['g71', 'g72', 'g73'],
['g81', 'g82', 'g83', 'g84'],
['g91', 'g92', 'g93'],
['g101', 'g102', 'g103'],
['g111', 'g112', 'g113', 'g114'],
['g121', 'g122', 'g123'],
['g131', 'g132'],
['g141']
]
if (n_clicks % 2) != 0:
xl = 12
style = {'width': 1000}
src = './assets/grid2.png'
index_dict_string=str(dash.callback_context.triggered[0]['prop_id'].split('.')[0])
if index_dict_string != '':
nbr = int(json.loads(index_dict_string)['index'])
print('nbr is:', nbr)
return [xl for g in tabs_graphs[nbr+1]]+[style, src]
else:
raise PreventUpdate
else:
xl = 6
style = {'width': 2000}
src = './assets/grid1.png'
index_dict_string = str(dash.callback_context.triggered[0]['prop_id'].split('.')[0])
if index_dict_string !='':
nbr=int(json.loads(index_dict_string)['index'])
print('nbr is:',nbr)
return [xl for g in tabs_graphs[nbr+1]]+[style, src]
else:
raise PreventUpdate
the HTML structure of the tabs:
tab1_content = dbc.Card(
dbc.CardBody(
[
dbc.Row([
html.Div([
html.Button(
html.Img(src='./assets/grid1.png',className='icon',id={'name':'img','index':1}),
className='grdbutton1',
n_clicks=0,
id={'name':'btn','index':1},
)
],id='btn-grid',style={'width':'100%'})
],
),
dbc.Row(
[
dbc.Col(dcc.Graph(id='CSSR_voce_1'),id='g11', sm=12,md=12,lg=12,xl=6),
dbc.Col(dcc.Graph(id='CSSR_voce_2'),id='g12',sm=12,md=12,lg=12,xl=6),
],
no_gutters=True,
)
],
),
id={'name':'card','index':1},
className="mt-3",
style={'width':2000}
),
I am using following code to try to get GUI elements from a function:
mypanelfn: func[] [
collect[
repeat i 10 [
print append copy "i in loop: " i
keep [t: text] keep append copy "message: " i
keep [field "entry"
button "Click" [t/text: "clicked"] return]]]]
view [
do [mypanelfn]]
There are no error messages and loop go on all right and a windows is also displayed. But this is only a small empty windows without any text, fields or buttons.
What is wrong with this code?
Edit: putting probe before collect shows (I have added line breaks for clarity):
[t: text "message: 1" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 2" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 3" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 4" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 5" field "entry" button "Click" [t/text: "clicked"] return
This method does not require setting any variables—it works by containing each iteration of faces within a common parent (panel):
view collect [
keep [below space 0x0]
repeat i 10 [
keep compose/deep [
panel [
origin 0x0
text (rejoin ["Message Number: " i])
field "entry"
button "Click" [face/parent/pane/1/text: "clicked"]
]
]
]
]
face/parent is the panel face whose first child (pane/1) is the text box (origin does not create a face).
You don't necessarily need the function there, however:
view mypanelfn
Works.
Note: the equivalent code in Rebol requires layout: view layout mypanelfn
The reason this happens is because view processes blocks! (anything inside []). So you don't have to do it.
In general, it's better to think of Red as a functional, message passing language. Everything is an expression, in contrast to imperative languages with procedures and statements.
once again; you need unique names for elements you want to address. Here a solution with reduce instead of compose
mypanelfn: func[] [
collect[
repeat i 10 [
tname: to-word rejoin ['t i]
print append copy "i in loop: " i
keep reduce [to-set-word tname 'text] keep append copy "message: " i
keep reduce [
'field "entry" 'button "Click" reduce [
to-set-path reduce [
tname 'text ]
"clicked" ]
'return ] ] ] ]
I recommend that you use the commands in the console to see what they do. E.g.
rejoin ['t i] creates a string "t1"with t and the (reduced/get-)value of i.
to-word changes that to a Red(bol) word t1
to-setword tname creates a set-word t1:
to-set-path reduce [tname 'text ]creates a set-path t1/text:
this works for me in rebol/view:
lay: mypanelfn
insert head lay 'across
view layout lay
I think while you're learning this stuff you need to look at the generated VID code to check that there are no problems before trying to View it.
I have code like this
<?php
$first_condition = time() % 2 == 0;
$second_condition = time() % 3 == 0;
if ($first_condition) {
if ($second_condition) {
$param1 = 'param1_1_1';
} else {
$param1 = 'param1_2_1';
$param2 = 'param2_2_1';
}
} else {
if ($second_condition) {
$param1 = 'param1_1_2';
} else {
$param1 = 'param1_2_2';
$param2 = 'param2_2_2';
}
}
if ($second_condition) {
$param2 = $param1;
}
$total = array(
'param1' => $param2,
'param2' => $param1,
);
I really know that $param2 would be defined anyway, but PhpStorm say that it's wrong.
Is exist there any way to mark this place as ignored of this inspection? Only this place, not global settings, and only this inspection, not all.
Sure -- you can suppress such warning for that statement.
Standard procedure:
Place caret on such error/warning.
Invoke Alt + Enter to bring quick fix menu (or via light bulb icon).
Find the right inspection.
Expand submenu (e.g. Arrow Right using keyboard or using mouse -- note: click area can be quite small -- depends on GUI theme used).
Choose Suppress for statement option.
The above will add special PHPDoc-like comment (/** #noinspection PhpUndefinedVariableInspection */) just before that statement -- it tells IDE to ignore that particular issue here.
https://www.jetbrains.com/help/phpstorm/suppressing-inspections.html?search=suppress
On another hand (especially if it's your code/code that you cane edit): why not go a bit safer route and just declare those $paramX variables with default values (e.g. empty string) before the conditionals ... so the variable will be indeed defined? This will prevent such false complains from IDE (when it tries to statically analyse such rather complex logic).
New subquestion: is it possible to disable inspection only for param1 but not for param2 using /** #noinspection PhpUndefinedVariableInspection */ ?
Yes and No.
Without making changes to the code -- No. Those variables are both used in one statement (array definition) and suppression comment is applied to the whole statement.
Yes -- split it into 2 statements if you need such separate suppression.
I want to map an array which in WebMethods is a document list. I thought that you could just map that variable over without mapping all of the children. I have done this and nothing shows in the PassArea. (PassArea is the data array that is being sent to a mainframe program afterwards.)
A --> B
Field1 F1
Field2 F2
field3 F3
The document is A and the input document into the Natural program is B. The --> is the link that connects them together.
I don't have an image to show, because that would reveal some company information.
If the fields of document list "A" has different names than the fields of document list "B" then no, you cannot map document list "A" to document list "B". WebMethods doesn't know which field from A corresponds to what field from "B".
You will have to do the following:
LOOP over document list "A"
Map each field of "A" to a generic document containing the same fields as document list "B"
Append the generic document to document list "B"
Drop the generic
document.
Step #2 screenshot
Step #3 screenshot
There's a lot ways to map between arrays of documents. But before you create one, consider these writings:
Techcommunity SoftwareAG - performance impact with appendToDocumentList
quest4apps.com - Reason to avoid appendToDocumentList
As hint from #2 said that there's 6 ways they ranked as follows from fastest to slowest (but I'm going to give an example in first three, because the latter three is quite obviously slow, which considered to be avoided):
1. Java Loop: looping done through a Java service.
The easiest way to create java service is to map the input output first.
Right-click and click on "Generate Code" until the dialog box appears
Choose the option "For implementing this service"
And the service is created
Just re-arrange the code into this:
public static final void mappingDocuments(IData pipeline) throws ServiceException {
// pipeline
IDataCursor pipelineCursor = pipeline.getCursor();
// Instantiate input A
IData[] A = IDataUtil.getIDataArray(pipelineCursor, "A");
// Initiate output B
IData[] B = new IData[A.length];
if (A != null)
{
for (int i = 0; i < A.length; i++)
{
// Populate the Field in doc A
IDataCursor ACursor = A[i].getCursor();
String Field1 = IDataUtil.getString(ACursor, "Field1");
String Field2 = IDataUtil.getString(ACursor, "Field2");
String Field3 = IDataUtil.getString(ACursor, "Field3");
ACursor.destroy();
// Create IData[i] and cursors finally put all Fields into B[i] variable output
B[i] = IDataFactory.create();
IDataCursor BCursor = B[i].getCursor();
IDataUtil.put(BCursor, "F1", Field1);
IDataUtil.put(BCursor, "F2", Field2);
IDataUtil.put(BCursor, "F3", Field3);
BCursor.destroy();
// OR JUST USE CLONE BELOW IF YOU DON'T HAVE ANY MODIFICATION INSIDE THE VARIABLE
// B[i] = IDataUtil.clone(A[i]);
}
}
pipelineCursor.destroy();
// Finally to put the B Map(IData) to output.
// Actually you can use only single pipelineCursor throughout all code but it's just for readable
IDataUtil.put(pipelineCursor, "B", B);
pipelineCursor.destroy();
}
Result
2. Implicit Loop: for simple lists of the same size, you may want to link them directly in a MAP step
Create flow service and input & output document
Create MAP step
Select both document in ForEach loop.
3. Explicit Loop: using a LOOP step and its Output Array.
Create flow service and input & output document
Create LOOP step
Change the properties of LOOP, input array=A; output array=B; and create a map under LOOP step
Map all parameters in A to B
Hope these helps...
The extension tt_news is very useful for me but there is this little thingy called "register:newsMoreLink". This register does contain the singlePid of the contentelement (defined a single view page) and the uid of the newsarticle from the news extension.
This is the typoscript section of the "new ts" of the extension tt_news
As you can see there is "append.data = register:newsMoreLink"...
plugin.tt_news {
displayLatest {
subheader_stdWrap {
# the "more" link is directly appended to the subheader
append = TEXT
append.data = register:newsMoreLink
append.wrap = <span class="news-list-morelink">|</span>
# display the "more" link only if the field bodytext contains something
append.if.isTrue.field = bodytext
outerWrap = <p>|</p>
}
}
}
What is "register:newsMoreLink"? Is this like a function or something? I do not know. But "register:newsMoreLink" produces a strange link if I use this on "append.data". It produces are "More >" link. The "More >" link after a news article teaser looks like this:
http://192.168.1.29/website/index.php?id=474&tx_ttnews%5Btt_news%5D=24&cHash=95d80a09fb9cbade7e934cda5e14e00a
474 is the "singlePid" (this is what it calls in the database
24 is the "uid" of the news article (the ones you create with the tt_news plugin in the backend)
My question is: Where is the "register:newsMoreLink" defined? Is it defined generally or do I miss a fact of Typo3..? How can I add an anchor link at the end of this "More >" href? Like:
http://192.168.1.29/website/index.php?id=474&tx_ttnews%5Btt_news%5D=24&cHash=95d80a09fb9cbade7e934cda5e14e00a#myAnchor1
register:newsMoreLink is not a function. It's one of the data types. In other words a type of data that you can access with stdWrap.data. register is set with LOAD_REGISTER. Though, in case of tt_news this is set in the PHP code with $this->local_cObj->LOAD_REGISTER().
I'm afraid you cannot easily add an anchor to that link. However, you can set the append to create your own custom link to the news record using typolink:
append = TEXT
append {
value = text of the link
typolink {
# ...typolink configuration...
}
}
You shall be interested in the typolink's attributes parameter, additionalParams and section.
this is the code I use to link to an pid with a anchor target:
displayList.plugin.tt_news.subheader_stdWrap {
append = TEXT
append.data >
append {
value = mehr
typolink{
parameter = 47 // pid
section = entry_{field:uid} // anchor name
section.insertData = 1
}
}