I've an old desktop application developed in progress 4gl.There is a frame contained in a progress window now my task is to make the frame responsive ie based on the size of its container window the frame should resize dynamically.Is it possible in Progress any workaround will be appreciated.Thanks
Once the frame has been "realized" you cannot change its height or width.
Prior to that you can manipulate the geometry via the frame handle.
To change things after the frame has been displayed you will basically need to build a new frame to replace the old one.
I'll assume you don't own the source code. If your window hasn't been set as RESIZE, then there's really no luck.
Otherwise, I created this simple program, which should make it possible to manipulate the window and the frame inside it. Now remember: All fields will have to be manually repositioned inside the frame. You also might want to set session:suppress-warnings to YES before you get this running, because every time a widget doesn't fit the frame, OpenEdge will let you know about it. You might need to cycle all objects inside the frame's field-group, to make it happen. And remember to get fill-ins side-label-handle to move them too, otherwise the fill-in itself moves, but the text stays. It's a lot of work.
So without further ado, the code:
DEFINE VARIABLE hProgram AS HANDLE NO-UNDO.
DEFINE VARIABLE hWindow AS HANDLE NO-UNDO.
DEFINE VARIABLE hFrame AS HANDLE NO-UNDO.
run <ProgramNameGoesHere> persistent set hProgram.
run enable_UI in hProgram.
assign hWindow = hProgram:current-window
hFrame = hWindow:first-child.
/* Resize Variables */
DEFINE VARIABLE dWinXC AS DECIMAL NO-UNDO.
DEFINE VARIABLE dWinYC AS DECIMAL NO-UNDO.
/* /Resize Variables */
ON WINDOW-RESIZED OF hWindow
DO:
/* Defining a minimum size here */
if hWindow:width-chars < 74 or
hWindow:HEIGHT-CHARS < 22 then return no-apply.
run piResize.
END.
procedure piResize:
def var dXC AS DECIMAL NO-UNDO.
def var dYC AS DECIMAL NO-UNDO.
/**--------------------------------- Screen Size ----------------------------**/
assign dXC = dWinXC - hWindow:width-chars
dYC = dWinYC - hWindow:height-chars
dWinXC = hWindow:width-chars
dWinYC = hWindow:HEIGHT-CHARS
/**-------------------------- Objects on Screen -----------------------------**/
hFrame:width = hWindow :WIDTH
hFrame:height = hWindow :HEIGHT no-error.
/**-------------------------------- Frame Sizes -----------------------------**/
ASSIGN hFrame:width = hWindow:width
hFrame:height = hWindow:HEIGHT no-error.
end procedure.
wait-for 'close' of hProgram.
Now, I don't know if this does you any good, since you can't probably interfere with behavior at will. Notice the inclusion of a trigger overrides whatever was there before (since your window did not resize, I don't think it will harm it. But doing that for a widget that already has an event procedure will cause it to stop doing what it currently does (unless you can code it, but again I don't think you have the code).
I apologize for the code not being just the genius patch that Stack Overflow loves so much, but it's a complex issue. Hope it helps.
Oh, and this was made using Progress 10.2B. I imagine if your code is previous to v8, you might be out of luck.
Related
I wrote a query which contains multiple for each statements. The query is taking more than 20 minutes to fetch the data. Is there a way to check what time each loop started and ended. (How much time does each loop takes to execute and also the total time taken to complete the program).
You could do as you ask (just follow JensD's suggestsions) but you would likely be better served to use the profiler. You can easily add profiling for a code snippet:
assign
profiler:enabled = yes
profiler:description = "description of this test"
profiler:profiling = yes
profiler:file-name = "filename.prf"
.
/* this is deliberately awful code that should take a long time to run */
for each orderline no-lock:
for each order no-lock:
for each customer no-lock:
if customer.custNum = order.custNum and orderLine.orderNum = orderLine.orderNum then
. /* do something */
end.
end.
end.
/* end of test snippet */
assign
profiler:enabled = no
profiler:profiling = no
.
profiler:write-data().
You can then load that prf file into an analysis tool. The specifics depend on your development environment - if you are using an up to date version of PSDOE there is a Profiler analyzer included, if not you might want to download ProTop
https://demo.wss.com/download.php and use the simple report included in lib/zprof_topx.p.
Ultimately what you are going to discover is that one or more of your FOR EACH statements is almost certainly using a WHERE clause that is a poor match for your available indexes.
To fix that you will need to determine which indexes are actually being selected and review the index selection rules. Some excellent material on that topic can be found here: http://pugchallenge.org/downloads2019/303_FindingData.pdf
If you don't want to go to the trouble of reading that then you should at least take a look at the actual index selection as shown by:
compile program.p xref program.xref
Do the selected indexes match your expectation? Did WHOLE-INDEX (aka "table scan") show up?
Using ETIME you can initiate a counter of milliseconds. It could be called once or several times to tell how much time has passed since reset.
ETIME(TRUE).
/*
Loop is here but instead I'll insert a small pause.
*/
PAUSE 0.5.
MESSAGE "This took" ETIME "milliseconds" VIEW-AS ALERT-BOX.
Milliseconds might not be useful when dealing with several minutes. Then you can use TIME to keep track of seconds but you need to handle start time yourself then.
DEFINE VARIABLE iStart AS INTEGER NO-UNDO.
iStart = TIME.
/*
Loop is here but instead I'll insert a slightly longer pause.
*/
PAUSE 2.
MESSAGE "This took" TIME - iStart "seconds" VIEW-AS ALERT-BOX.
If you want to keep track of several times then it might be better to output to a log file instead of using a MESSAGE-box that will stop execution until it's clicked.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DEFINE STREAM str.
OUTPUT STREAM str TO c:\temp\timing.txt.
ETIME(TRUE).
/*
Fake loop
*/
DO i = 1 TO 20:
PAUSE 0.1.
PUT STREAM str UNFORMATTED "Timing no " i " " ETIME "ms" SKIP.
END.
OUTPUT CLOSE.
So what I currently want to do is pretty much implement rofi in awesome.
The reason I want to do this and I don't just use rofi is because I want to learn how to 'auto-generate' widgets in awesome.
This will come in handy later when I'll implement things like network widgets that when clicked, shows you a panel, shows you the wifi hotspots available as rows, etc etc. So it's just for me to learn how awesome works better. But also, I want a program launcher.
And also, before someone suggests it, I already know that there's a built-in launcher in awesome, and I also know that there's this. This is not what I'm looking for. I want to have the same thing thing rofi and dmenu have: I want to have suggestions pop up when you press keys. and I want to be able to click on the suggestions, etc.
What I want is something like this: uhhhh
So what I'm having problems is this: how do I auto-generate the rows? I want to be able to specify in only one place how many rows I want, and have awesome do the rest.
I've looked through Elv's github and I found radical and even though what he made is a menu system, I thought that I could use some of his code to do what I want. But I can't for the love of god figure out how it works. No offense to him, but it's not all too well docummented, even for users, and for actually explaining how the code works there's no docummentation.
So My question is: How can I make this work? How would I go about making the widgets that act as the rows automatically?
TL;DR:
i want to write a program launcher like rofi in awesome
i want to be able to specify only in one place the number of rows
therefore, (((I think))) I need to automatically generate widgets as rows somehow, how can I do it?
EDIT:
What I want is to be able to create the rows of my launcher automatically. I know I can hardcode the rows myself, have each row have a different id and then I can write a function that on each keypress, will update each widget with the most relevant matches. So it would be something like (not tested at all):
local wibox = require("wibox")
local awful = require("awful")
local num_rows = 10
local row_height = 40
-- set the height of the background in accordance to how many rows there are,
-- and how high each row should be
local prompt_height = row_height * num_rows
local prompt_width = 300
-- make a widget in the middle of the screen
local background = wibox({
x = awful.screen.focused().geometry.width / 2 - prompt_width / 2,
y = awful.screen.focused().geometry.height / 2 - prompt_height / 2,
width = prompt_width,
height = prompt_height,
bg = "#111111",
visible = false,
ontop = false
})
local rofi_launcher = wibox.widget({
widget = background,
{
-- get a flexible layout so the searchbox and the suggestion boxes get
-- scaled to take up all the space of the background
layout = wibox.layout.flex.vertical,
{ -- the prompt you actually type in
-- set id here so we can adjust its ratio later, so the magnifying
-- glass will end up on the right, and the texbox will take up the left side
id = "searchbox_and_mangifying_glass",
layout = wibox.layout.ratio.horizontal,
{
-- set id so we can use it as a prompt later
id = "searchbox",
widget = wibox.widget.textbox,
},
{
widget = wibox.widget.imagebox,
icon = '~/path/to/magnifying_glass_icon.svg',
},
},
{ -- this is where I actually create the rows that will display suggestions
{ -- row number 1
-- make a background for the textbox to sit in, so you can change
-- background color later for the selected widget, etc etc.
widget = wibox.widget.background,
{
-- give it an id so we can change what's displayed in the
-- textbox when we press keys in the prompt
id = "suggestion_1",
widget = wibox.widget.textbox,
},
},
{ -- row number 2
-- background, again
widget = wibox.widget.background,
{
-- id and textbox again
id = "suggestion_2",
widget = wibox.widget.textbox,
},
},
-- and another 8 (according to the `num_rows` variable) of the same two
-- textboxes above. This is exactly my problem. How can I make these
-- textboxes automatically and still be able to interact with them to
-- display suggestions on the fly, as the user types keys into the prompt?
},
},
})
If this is not clear enough please do let me know what you don't understand and I will update my question.
Equally untested as your code, but this creates a tables of textboxes instead of using the declarative layout to create all these textboxes:
[SNIP; For shorter code I removed some stuff at the beginning]
local textboxes = {}
local widgets = {}
for i = 1, num_rows do
local tb = wibox.widget.textbox()
local bg = wibox.widget.background(tb)
bg:set_bg("#ff0000") -- The original code did not set a bg color, but that would make the bg widget useless...?
tb.id = "suggestion_" .. tostring(i) -- This is likely unnecessary, but the original code set these IDs, too
table.insert(textboxes, tb)
table.insert(widgets, bg)
end
local rofi_launcher = wibox.widget({
widget = background,
{
-- get a flexible layout so the searchbox and the suggestion boxes get
-- scaled to take up all the space of the background
layout = wibox.layout.flex.vertical,
{ -- the prompt you actually type in
[SNIP - I did not change anything here; I only removed this part to make the code shorter]
},
widgets
},
})
-- Now make the textboxes display something
textboxes[3].text = "I am the third row"
textboxes[5].text = "I am not"
I'm trying to create different frames and switch/destroy them so that you can move between windows like you would in a normal iOS app.
To do so, I need to place the widgets (components) in frames (containers).
However, when I try to add a button to the frame it doesn't pack it to the right side.
Here is my code:
from tkinter import *
root=Tk()
root.geometry('500x500')
root.title('Good morning :)')
frame1=Frame(root,width=500,height=500,bg='green')
frame1.pack()
button1=Button(frame1,text='Hello')
button1.pack(side='bottom')
You need to expand the Frame to fill the entire top-level window, and you need to tell the Button to pack on side='right' instead of side='bottom'.
And you need to run root.mainloop() at the end.
from tkinter import *
root = Tk()
root.geometry('500x500')
root.title('Good morning :)')
frame1 = Frame(root, bg='green')
frame1.pack(expand=True, fill=BOTH)
button1 = Button(frame1, text='Hello')
button1.pack(side=RIGHT)
root.mainloop()
Also, you don't need the dimensions in the Frame statement, since it will expand to the full 500x500 stated in the geometry with the extra keyword arguments passed to the pack() function. By default, the Frame is only going to be big enough to hold the widgets inside it, so it will only be as big as the Button, unless you tell it to expand to the full size of the top-level root widget.
I have a fill-in with the following code, made using the AppBuilder
DEFINE VARIABLE fichNoBuktiTransfer AS CHARACTER FORMAT "N(18)":U
LABEL "No.Bukti Transfer"
VIEW-AS FILL-IN NATIVE
SIZE 37.2 BY 1 NO-UNDO.
Since the format is N, it blocks the user from entering non-alphanumeric entries. However, it does not prevent the user from copypasting such entries into the fill-in. I have an error checking like thusly to prevent such entries using the on leave trigger:
IF LENGTH(SELF:Screen-value) > 18 THEN DO:
SELF:SCREEN-VALUE = ''.
RETURN NO-APPLY.
END.
vch-list = "!,*, ,#,#,$,%,^,&,*,(,),-,+,_,=".
REPEAT vinl-entry = 1 TO NUM-ENTRIES(vch-list):
IF INDEX(SELF:SCREEN-VALUE,ENTRY(vinl-entry,vch-list) ) > 0 THEN DO:
SELF:SCREEN-VALUE = ''.
RETURN NO-APPLY.
END.
END.
However, after the error handling kicked in, when the user inputs any string and triggers on leave, error 632 occurs:
error 632 occurs
Is there any way to disable the error message? Or should I approach the error handling in a different way?
EDIT: Forgot to mention, I am running on Openedge version 10.2B
You didn't mention the version, but I'll assume you have a version in which the CLIPBOARD system handle already exists.
I've simulated your program and I believe it shouldn't behave that way. It seems to me the error flag is raised anyway. My guess is even though those symbols can't be displayed, they are assigned to the screen value somehow.
Conjectures put aside, I've managed to suppress it by adding the following code:
ON CTRL-V OF FILL-IN-1 IN FRAME DEFAULT-FRAME
DO:
if index(clipboard:value, vch-list) > 0 then
return no-apply.
END.
Of course this means vch-list can't be scoped to your trigger anymore, in case it is, because you'll need the value before the leave. So I assigned the special characters list as an INIT value to the variable.
After doing this, I didn't get the error anymore. Hope it helps.
To track changes in a fill-in I always use at first this code:
ON VALUE-CHANGED OF FILL-IN-1 IN FRAME DEFAULT-FRAME
DO:
/* proofing part */
if ( index( clipboard:value, vch-list ) > 0 ) then do:
return no-apply.
end.
END.
You could add some mouse or developer events via AppBuilder to track changes in a fill-in.
I am writing an application in Visual Basic 2010 Express.
I have two objects of a class from a driver DLL that is provided to me. They have some of their own subroutines that I'd like to call, and I'd like an easy way to toggle between them.
Instead of writing a whole bunch of code like this:
selected = x
...
If selected = x then
DriverInstanceX.DoSomething()
Else If Selected = y then
DriverInstanceY.DoSomething()
Endif
I would like to do this:
Bob = (some reference to X - NOT a copy of X!)
...
Bob.DoSomething()
Bob.DoSomethingElse()
I'm sure this is really easy - I am just not sure where to look.
Thanks for any help!
' set the object based on what was selected first, here...
Dim selectedDriverInstance = new DriverObject
' now you can run the method without checking for each as selected was already set.
selectedDriverInstance.DoSometng()
Cool?
Of course, DriverObject can be the instance x or instance y depending on what u set it to, do the assignment there and set it to our fixed name object selectedDriverInstance. this way you can do everything using selectedDriverInstance as it is set to either instance x or instance y already, get me?