Task: to have a sheet counter to be used in fields that automatically update their values. The ultimate goal is creating a text like "Table {1} of {5}" for each layout, where {1} is the layout name (found at system variables / ctab) and {5} is the total number of layouts. The latter one is not found in built-in fields, so the only way to know it is to use the lisp code (length (layoutlist)).
I created an AcadDoc.lsp file and putted in the autodesk folder. The LISP code is as follows:
(setq *LayoutCount*
(length
(vl-remove-if
(function
(lambda (aLayout) (= (strcase aLayout) "MODEL")))
(layoutlist))))
which runs every time i open a DWG file. Sad thing is that the field doesn't update until the DWG is reloaded (close and reopen). Does anyone have any clue on how to update this *LayoutCount* variable every time a layout is added/removed from the list?
NB - I already checked the option to regen when switching layouts, and forcing update fields raise no results. I guess my code runs only the first time the DWG is opened and then no more.
As you have gathered, code present in the acaddoc.lsp file will only be evaluated on drawing startup, hence the value of your *LayoutCount* variable will only be correct at the point at which the drawing is first opened.
One possible way around this could be to use a Visual LISP Reactor to update the value of this variable following certain actions.
For example, you could use a Miscellaneous Reactor in the following way to update the variable value when the active layout is changed:
(
(lambda ( )
(vl-load-com)
(foreach grp (vlr-reactors :vlr-miscellaneous-reactor)
(foreach rtr (cdr grp)
(if (= "layoutcount-reactor" (vlr-data rtr))
(vlr-remove rtr)
)
)
)
(vlr-set-notification
(vlr-miscellaneous-reactor "layoutcount-reactor"
'(
(:vlr-layoutswitched . layoutswitched-callback)
)
)
'active-document-only
)
(defun layoutswitched-callback ( rtr arg )
(setq *layoutcount* (length (layoutlist)))
)
(layoutswitched-callback nil nil)
(princ)
)
)
Copy the above code to your acaddoc.lsp and the *layoutcount* variable will be defined on drawing startup and updated every time the active layout is changed.
Aside, it is not necessary to remove Model from the list returned by the layoutlist function, as this function only returns a list of Paperspace layouts.
However, the disadvantage of using global variables to provide this information is that the global variable will only be defined within the document namespace during the active AutoCAD session and will need to be redefined for every session.
As such, if one of your colleagues or a third-party were to open the drawing file, unless they were also running the code found in your acaddoc.lsp, the *layoutcount* variable would not be defined and consequently the field would not display correctly.
Therefore, an alternative is to exploit the fact that a Field Expression can actually reference any ActiveX property, not just those displayed in the FIELD command dialog.
I demonstrate this technique with my Layout Field application, which allows you to create a Field Expression referencing the Count property of the Layouts Collection.
This approach has several advantages:
The drawing content is not dependent on any external code.
The CTAB system variable is not referenced, meaning the DATAEXTRACTION command will output the correct information, rather than the same value for every layout.
Related
Control-M's ISPF client in zOS (mainframe) comes with a set of predefined values assigned to PF keys (function keys), such as:
PF2 = SPLIT
PF9 = SWAP
However, similar to other ISPF applications, I'd like to change the values of those PF keys (and a few others, such as PF2 = RETURN) like so:
PF2 = SPLIT NEW
PF9 = SWAP NEXT
And trying to use standard ISPF's primary command "PFKEYS" "KEYS" (typo, pointed out in first answer), via which you can adapt the values of your PF keys, doesn't seem to work for Control-M either (you only get error message "UNRECOGNIZED COMMAND", no matter on which screen you try this command).
Any suggestions about how to change those values for PF2 and PF9 anyhow?
PFKEYS is not a valid ISPF command. The proper commands to access the ISPF PF Key Definitions and Labels dialog are KEYS for context sensitive key assignments or ZKEYS for global key assignments.
Navigate into Control-M, then use the KEYS command to launch the dialog and assign the desired function key commands. You may need to explicitly enter the SAVE command to commit your key assignment changes to your profile (although PF3 should invoke an END, which includes a SAVE).
Subsequently, PFSHOW (or the short version, FKA) will add an "infobar" of sorts to your panel that shows the PF keys, and either the defined label or the first 8 chars of the command that is currently assigned to each function key. Enter PFSHOW OFF (or FKA OFF) to remove this display.
The KEYLIST ON/OFF command can be used to switch between default and custom key assignments for some product panels. Enter the KEYLIST command with no arguments to see if options are available. FWIW... I don't have access to Control-M, but our installation of other BMC products does not include custom keylists.
Some PFkey errors can be attributed to installation procedures that do not include the appropriate members in logon procs. For Control-M, these members appear to be CMTCMDS and/or CMTUCMDS. The sysadmins responsible for installing the product would likely need to address this type of issue. However, the issue described here provides no indication of an installation problem.
Currently, I made a tool to rename view numbers (“Detail Number”) on a sheet based on their location on the sheet. Where this is breaking is the transactions. Im trying to do two transactions sequentially in Revit Python Shell. I also did this originally in dynamo, and that had a similar fail , so I know its something to do with transactions.
Transaction #1: Add a suffix (“-x”) to each detail number to ensure the new numbers won’t conflict (1 will be 1-x, 4 will be 4-x, etc)
Transaction #2: Change detail numbers with calculated new number based on viewport location (1-x will be 3, 4-x will be 2, etc)
Better visual explanation here: https://www.docdroid.net/EP1K9Di/161115-viewport-diagram-.pdf.html
Py File here: http://pastebin.com/7PyWA0gV
Attached is the python file, but essentially what im trying to do is:
# <---- Make unique numbers
t = Transaction(doc, 'Rename Detail Numbers')
t.Start()
for i, viewport in enumerate(viewports):
setParam(viewport, "Detail Number",getParam(viewport,"Detail Number")+"x")
t.Commit()
# <---- Do the thang
t2 = Transaction(doc, 'Rename Detail Numbers')
t2.Start()
for i, viewport in enumerate(viewports):
setParam(viewport, "Detail Number",detailViewNumberData[i])
t2.Commit()
Attached is py file
As I explained in my answer to your comment in the Revit API discussion forum, the behaviour you describe may well be caused by a need to regenerate between the transactions. The first modification does something, and the model needs to be regenerated before the modifications take full effect and are reflected in the parameter values that you query in the second transaction. You are accessing stale data. The Building Coder provides all the nitty gritty details and numerous examples on the need to regenerate.
Summary of this entire thread including both problems addressed:
http://thebuildingcoder.typepad.com/blog/2016/12/need-for-regen-and-parameter-display-name-confusion.html
So this issue actually had nothing to do with transactions or doc regeneration. I discovered (with some help :) ), that the problem lied in how I was setting/getting the parameter. "Detail Number", like a lot of parameters, has duplicate versions that share the same descriptive param Name in a viewport element.
Apparently the reason for this might be legacy issues, though im not sure. Thus, when I was trying to get/set detail number, it was somehow grabbing the incorrect read-only parameter occasionally, one that is called "VIEWER_DETAIL_NUMBER" as its builtIn Enumeration. The correct one is called "VIEWPORT_DETAIL_NUMBER". This was happening because I was trying to get the param just by passing the descriptive param name "Detail Number".Revising how i get/set parameters via builtIn enum resolved this issue. See images below.
Please see pdf for visual explanation: https://www.docdroid.net/WbAHBGj/161206-detail-number.pdf.html
In the TI-BASIC programming language (Specifically TI-84+), how do you create input forms, such as the ones included in the default apps on the TI-84+.
The image included here shows an example of what I'm trying to create: A menu that you can scroll through and input multiple variables freely before executing a function
Additionally, is it possible to make this menu dynamically-updating as variables are entered?
You've set a rather tall order for TI-Basic to fill. user3932000 is correct; there is no built in function to create an input form of the type you request.
However, there is nothing stopping you from creating an interactive interface yourself. Creating it from scratch will be a time consuming and, it will consume a significant amount of memory on your calculator. There is no boilerplate code you plug your variables into to get the results you want, but you might have some luck modeling it after this quadratic solver I wrote.
ClrHome
a+bi
Output(1,1," QUADRATIC
Output(2,1," AX²+BX+C
Output(3,1,"ZEROS:
Output(6,1,"A=
Output(7,1,"B=
Output(8,1,"C=
DelVar YDelVar D
" →Str1
While Y≠105
getKey→Y
If Ans
Then
Output(X,4,Str1
Output(3,7,Str1+Str1+Str1+"
End
X+(Ans=34)-(Ans=25
If Ans<6:8
If Ans>8:6
Ans→X
Output(Ans,16,"◄
D(Y≠45→D
If Y=25 or Y=34
sum({A,B,C}(X={6,7,8→D
If Y=104:⁻D→D
10not(Y)+Y(102≠Y)-13int(Y/13(2>abs(5-abs(5-abs(Y-83
If Ans≤9
D10+Ans-2Ans(D<0→D
If X=6:D→A
If X=7:D→B
If X=8:D→C
If A
Then
2ˉ¹Aˉ¹(⁻B+{1,⁻1}√(B²-4AC
Else
If B
Then
⁻C/B
Else
If C
Then
"No Zeros
Else
"All Numbers
End
End
End
Output(3,7,Ans
Output(6,3,A
Output(7,3,B
Output(8,3,C
End
ClrHome
Ans
Here's a GIF of what it does for you.
With a little more work. This code could be used on the Graph screen instead of the home screen, giving more option in terms of layout and design.
In the TI-BASIC programming language (Specifically TI-84+), how do you create input forms, such as the ones included in the default apps on the TI-84+.
There are many ways to ask for input in your program:
Prompt: Asks for input and stores it in a variable. For example, Prompt A. Simplest way to ask for input, but not very visually appealing.
Input: Similar to the Prompt command, except that now you can include text within the input. For example, Input "What is your name?",A.
Menu(: Multiple choice input, and each choice is connected to a Lbl marker somewhere else in the script. Much like the error screen with the quit/goto choices that you've probably seen. For example, Menu("Are you a boy or a girl?","Boy",B,"Girl",G).
getKey: Checks if a certain key is pressed, and will output True (1) if that key is pressed. For example, getKey 105. See here for which numbers each key corresponds to.
The image included here shows an example of what I'm trying to create: A menu that you can scroll through and input multiple variables freely before executing a function http://imgur.com/ulthDRV
I'm afraid that's not possible in programs. You can either put in multiple inputs, or you might be interested in looking into making apps instead.
Additionally, is it possible to make this menu dynamically-updating as variables are entered?
If you're talking about the text on top of the screenshot, yes you can; just put a Disp command or something after each line of Input, so that it continuously overwrites the text above with new text after you input a variable.
I have exported the contents of a table with transaction SE16, by selecting all the entries and going selecting Download, unconverted.
I'd like to import these entries into another system (where the same table exists and is active).
Furthermore, when I import, there's a possibility that the specific key already exists for a number of entries (old entries).
Other entries won't have a field with the same key present in the table where they're to be imported (new entries).
Is there a way to easily update my table in the second system with the file provided from the first system? If needed, I can export the data in the 3 other format types (Spreadsheet, Rich text format and HTML format). It seems to me though like the spreadsheet and rich text formats sometimes corrupt the data, and the html is far too verbose.
[EDIT]
As per popular demand, the table i'm trying to export / import is a Z table whose fields are all numeric, character, date or time fields (flat data types).
I'm trying to do it like this because the clients don't have any basis resource to help them transport, and would like to "kinna" automate the process of updating one of the tables in one system.
At the moment it's a business request to do it like this, but I'm open to suggestions (and the clients are open too)
Edit
Ok I doubt that what you describe in your comment exists out of the box, but you can easily write something like that:
Create a method (or function module if that floats your boat) that accepts the following:
iv_table name TYPE string and
iv_filename TYPE string
This would be the method:
method upload_table.
data: lt_table type ref to data,
lx_root type ref to cx_root.
field-symbols: <table> type standard table.
try.
create data lt_table type table of (iv_table_name).
assign lt_table->* to <table>.
call method cl_gui_frontend_services=>gui_upload
exporting
filename = iv_filename
has_field_separator = abap_true
changing
data_tab = <table>
exceptions
others = 4.
if sy-subrc <> 0.
"Some appropriate error handling
"message id sy-msgid type 'I'
" number sy-msgno
" with sy-msgv1 sy-msgv2
" sy-msgv3 sy-msgv4.
return.
endif.
modify (p_name) from table <table>.
"write: / sy-tabix, ' entries updated'.
catch cx_root into lx_root.
"lv_text = lx_root->get_text( ).
"some appropriate error handling
return.
endtry.
endmethod.
This would still require that you make sure that the exported file matches the table that you want to import. However cl_gui_frontend_services=>gui_upload should return sy-subrc > 0 in that case, so you can bail out before you corrupt any data.
Original Answer:
I'll assume that you want to update a z-table and not a SAP standard table.
You will probably have to format your datafile a little bit to make it tab or comma delimited.
You can then upload the data file using cl_gui_frontend_services=>gui_upload
Then if you want to overwrite the existing data in the table you can use
modify zmydbtab from table it_importeddata.
If you do not want to overwrite existing entries you can use.
insert zmydbtab from table it_importeddata.
You will get a return code of sy-subrc = 4 if any of the keys already exists, but any new entries will be inserted.
Note
There are many reasons why you would NOT do this for a SAP-standard table. Most prominent is that there is almost always more to the data-model than what we are aware of. Also when creating transactional data, there are often follow-on events or workflow that kicks off, that will not be the case if you're updating the database directly. As a rule of thumb, it is usually a bad idea to update SAP standard tables directly.
In that case try to find a BADI, or if that's not available, record a BDC and do the updates that way.
If the system landscape was setup correctly, your client would not need any kind of basis operations support whatsoever to perform the transports. So instead of re-inventing the wheel, I'd strongly suggest to catch up on what the CTS and TMS can do once they're setup with sensible settings.
I've been using docjure to write to excel files. Mostly I want to append rows to already existing files, usually one at a time. When I do this without agents/future, I load the file, use add-rows to add the data, and then rewrite the file like this:
(defn append [filename data]
"data is in the same format as create-workbook, i.e. [[\"n\" \"m\"] [1 2] [3 4]]"
(let [workbook (load-workbook filename))
sheet (select-sheet workbook "Sheet1")]
(add-rows! sheet data)
(save-workbook! filename workbook)))
I make a lot of calls to append, so I found this: http://blakesmith.me/2012/05/25/understanding-clojure-concurrency-part-2.html, which shows you how to use agents to write to a file using future.
First of all, I'm using FileOutputStream instead of FileWriter, which would still work, except whereas in the tutorial's example you just append strings to the end of the file using .write and then close, I need to rewrite the file every time I "append" (I think?) since there's more bytes in a .xlsx workbook than just characters.
I don't really know how to set this up since with the tutorial's logging example, write-out returns the updated instance of the BufferedWriter and I don't know what the equivalent of that would be.
My other option would be to add the data to the vector concurrently (load the file once and keep returning new vectors [[\"n\" \"m\"] [1 2] [3 4]] with the data added) but I'm planning on doing ~10000-100000 of these calls and that seems like a lot to keep track of... although to be fair reading and writing all the data that many times is probably not that great either.
If you have any suggestions on how I can do this, I'd appreciate it. I'd be willing to make calls to the Apache POI itself too, if there's a better way to append with that. Thanks.
--- UDPATE ---
I just rewrote the the logger example with the file as an agent instead of the output stream and it seems to work. I'll let you know if it ends up working with docjure/Apache POI.
(def logfile (agent (File. "blah.txt")))
(defn write-out [file msg]
(with-open [out (BufferedWriter. (FileWriter. file true))]
(.write out msg))
file)
--- UDPATE 2---
I got an analogous version written with docjure, but the unfortunately because opening the file happens within write-out and that happens during each future (I don't see a way around this if I use File as an agent, and I don't see another way to do it besides that) most of them read the empty file and write the row to that since they're all done in parallel and the end result is that most of them overwrite each other.
Ultimately I decided to just add each row vector to an overall data vector and write once. I can do that with just pmap, so its a lot neater. The one downside is if something goes wrong none of the data is written to the file at all, but the upside is that the time it takes to write is reduced since there's only one write call. Also, I would have been loading the large amount of data into memory every time which takes time. Memory usage is the same either way.
If anyone still wants to answer this, I'd still be interested, but the method in my first update does not work (each future reads in an empty file and uses that to append to). I'll post that code incase it helps anyone though--docjure version of the aforementioned tutorial:
(def file (agent (File. "blah.xlsx")))
(defn write-out [file workbook]
(with-open [out (FileOutputStream. file)]
(.write workbook out))
file)
(defn write-workbook [file data]
(let [filename (.getPath #file)
workbook (try (load-workbook filename)
(catch Exception e (create-workbook "Sheet1" [])))
sheet (select-sheet "Sheet1" workbook)]
(add-rows! sheet data)
(send file write-out workbook)))
(defn test [file]
(write-workbook file [["n" "m"]])
(dotimes [i 5]
(future (write-workbook file [[i (inc i)]]))))
Thanks