Jump to content

araujoarthur

Members
  • Content Count

    4
  • Joined

  • Last visited

Everything posted by araujoarthur

  1. Hello! I am writing a log viewer using Delphi VCL and AlmediaDev's StyleControls, and my approach to show a styled list of log entries was to add a TScrollBox (TscScrollBox actually, using AlmediaDev StyleControls, which extends the original), and dynamically fill it with frames (one frame per log entry) as the following image shows: The Frame itself looks like this: And has this component tree: The frames have variable width according to the window width, but the height is always fixed. This approach works for smaller log files (The above screenshot was taken with 217 records) but fails with "Out of System Resources" and "Canvas Does Not Allow Drawing" with bigger files (my failed test file had 3000 entries). Although I understand why is this happening (or atleast I believe it's because the windows won't let me draw 3.000 controls) I don't know what approach I could take to either improve the rendering to reduce resource usage or change the way it renders frames without affecting the scroll list size (otherwise I could just "remove" the frames out of sight and insert the ones that are on sight) and entry UI style (As I believe I would have to if changed for a TVirtualListView as suggested in topic/3661-how-to-detect-when-control-is-scrolled-into-view/). Can anyone give me any direction on what should I do here? Right now it's really more a matter of "WHAT should I do" than "HOW do I do that". For quick reference, I'm also adding the code for the TEntryListItemTemplate and the code that creates and feed it into the scrollbox. It's also fully available here. TEntryListItemTemplate type TEntryListItemTemplate = class(TFrame) svgLogIcon: TSkSvg; labelDateTime: TscGPLabel; scGPPanel1: TscGPPanel; labelDescription: TscGPLabel; scGPPanel2: TscGPPanel; scGPPanel3: TscGPPanel; scGPGlyphContainerButton2: TscGPGlyphContainerButton; svgEyeIcon: TSkSvg; private { Private declarations } FAssociatedRecord: TLogEntry; FAssociatedControlIndex: Integer; // TODO: When one is removed all indexes must above must change (needs a routine) procedure SetupWarningIcon(); procedure SetupErrorIcon(); procedure SetupInfoIcon(); public { Public declarations } constructor Create(AOwner: TComponent; ARecord: TLogEntry; AControlIndexInList: Integer); reintroduce; end; const { The Following Icons are part of Google's Material Core Icon Pack } ERROR_ICON = ''' ... '''; INFO_ICON = ''' ... '''; WARNING_ICON = ''' ... '''; implementation {$R *.dfm} { TEntryListItemTemplate } constructor TEntryListItemTemplate.Create(AOwner: TComponent; ARecord: TLogEntry; AControlIndexInList: Integer); begin inherited Create(AOwner); FAssociatedRecord := ARecord; FAssociatedControlIndex := AControlIndexInList; case FAssociatedRecord.Severity of lsUNKNOWN: SetupInfoIcon(); lsUNIMPORTANT: SetupInfoIcon(); lsREQUESTRECEIVED: SetupInfoIcon(); lsINFORMATION: SetupInfoIcon(); lsWARNING: SetupWarningIcon(); lsERROR: SetupErrorIcon(); end; labelDescription.Caption := Copy(FAssociatedRecord.Message, 0, 150); labelDateTime.Caption := DateTimeToStr(FAssociatedRecord.Date); end; procedure TEntryListItemTemplate.SetupErrorIcon; begin svgLogIcon.Svg.OverrideColor := $FFFF2D2D; svgLogIcon.Svg.Source := ERROR_ICON; end; procedure TEntryListItemTemplate.SetupInfoIcon; begin svgLogIcon.Svg.OverrideColor := $FF2D4DFF; svgLogIcon.Svg.Source := INFO_ICON; end; procedure TEntryListItemTemplate.SetupWarningIcon; begin svgLogIcon.Svg.OverrideColor := $FFFFAB2D; svgLogIcon.Svg.Source := WARNING_ICON; end; Creation and Insertion in TscScrollBox: procedure TfrmMain.Open1Click(Sender: TObject); var OpenedFile: TLogFile; begin OpenedFile := ActionUtils.OpenFile; if OpenedFile <> nil then begin gStateHolder.CurrentFile := OpenedFile; gStateHolder.HasOpenFile := True; var Progress: Extended := 0; var Increment: Extended := 100/Length(gStateHolder.CurrentFile.FLogEntries); ProgressBarInc(0); { I decided to make invisible while I add the entries so the it doesn't have to draw every single entry on each insert. By setting it to Visible := False before the loop and True after the loop I got it to render all at once, which **Improved significantly the Load time for 217 records**. Still fails for 3.000 } contentScrollBox.Visible := False; for var I := Length(gStateHolder.CurrentFile.FLogEntries) - 1 downto 0 do begin var rec := gStateHolder.CurrentFile.FLogEntries[I]; var frame := TEntryListItemTemplate.Create(contentScrollBox, rec, contentScrollBox.ControlCount); frame.Name := TypeUtils.GenerateFrameName(); contentScrollBox.InsertControl(frame); Progress := Progress + Increment; ProgressBarInc(Round(Progress)); end; contentScrollBox.Visible := True; // System out of Resources. How to deal with? end; end; Thank you for your time!
  2. Hello y'all! I'm developing a Shopee Wrapper in Delphi and ran into a pitfall. I also ran into a similar problem that was answered on StackOverflow. I need to handle two kinds of asynchronous behavior that are needed for the main program (and the VCL visual part), but I don't know WHAT I should do for this. My issue starts with the following constructor noted in the code block (1). After the Authorize; call, any instruction dependent on Authorization Code falls into Undefined Behavior because there's no guarantee I have a code. I would need to wait for Authorize to return before following on. My issue arises from the fact that Authorize creates an object and starts a routine that is Asynchronous, a HTTP listener (as noted in code block) (2). The AuthorizationRequest procedure is implemented as shown and so does FieldsReadyHandler (which is the subject of my older question) in the current version.(3, 4). I was told to post here to get a better guidance on how to learn this subject and overcome this obstacle. I would like to answer the question "Why is there a server that is created then destroyed after a simple request". That was a design choice I made (maybe a poor one, I'm open to change this) for the URI. I needed it to be local so I came with the idea of opening a simple http server, waiting for the Authorization redirect, catching the code and shop_id then freeing it. This process is necessary at least once every 365 days. Usually it's a matter of HOW to do that, but honestly at this point I'm thinking that I overcomplicated it and don't even know WHAT I should do to overcome this. Anyways, if I were to rewrite I can see my self falling in the same spot where my subsequent tasks depends on the async task to finish. I also tried to add an event to inform the Context that it already obtained the code but (my shot is) it raises access violation because of the way the object is released. Sorry I can't synthesize a TL;DR for this one, but if I had to index my questions the output would be: What is the best approach to this type of problem and how is it handled in pascal? In JS, for example, I would chain infinite .then() until the routine doesn't need the async part anymore. I also feel it is going to become a frequent pain if I don't learn to handle it now as requests are asynchronous. Thanks in advance. {1. The constructor } constructor TShopeeContext.Create(AMode, AAPIKey, APartnerID: string); begin if (AMode <> TEST_SHOPEE) and (AMode <> PRODUCTION_SHOPEE_BR) then raise Exception.Create('Mode Invalid for Context'); FHost := AMode; FAPI_Key := AAPIKey; FPartnerID := APartnerID; UpdateInternalConfigFields; if FRefreshToken = '' then FHasRefreshToken := False else FHasRefreshToken := True; // Checks if the authorization has been granted and is younger than 365 days. if (not FAuthorizationGranted) or ((Now - FAuthorizationDate) > 365) then begin Authorize; end; end; { 2. Authorize Procedure } procedure TShopeeContext.Authorize; var Authorizator: TShopeeAuthorizator; begin // Request Authorization; Authorizator := TShopeeAuthorizator.Create(FHost, FAPI_Key, FPartnerID, 8342); try Authorizator.AuthorizationRequest; finally Authorizator.Free; end; end; { 3. AuthorizationRequest implementation } procedure TShopeeAuthorizator.AuthorizationRequest; begin // Obtem a assinatura da chamada FSignature := GetPublicSignString(API_PATH_AUTHORIZATION, FTimestamp); // Constroi os parametros FRequestParameters.AddItem('partner_id', FPartnerID); FRequestParameters.AddItem('timestamp', FTimeStamp); FRequestParameters.AddItem('sign', FSignature); FRequestParameters.AddItem('redirect', REDIRECT_URL_WS+':'+IntToStr(FPort)); ShellExecute(0, 'open', PChar(GenerateRequestURL(API_PATH_AUTHORIZATION, FRequestParameters)), nil, nil, SW_SHOWNORMAL); FCatcherServer := TCatcherServer.Create(); FCatcherServer.OnFieldsReady := FieldsReadyHandler; FCatcherServer.Listen(FPort); end; {4. FieldsReadyHandler implementation} procedure TShopeeAuthorizator.FieldsReadyHandler(Sender: TObject; Code, AuthorizatedID: string); begin // Handle Code, Auth Type and AuthorizatedID. FConfigurator := TConfiguratorFile.Create; try FConfigurator.SaveAuthorizationInfo(Code, AuthorizatedID, (Sender as TCatcherServer).AuthorizationType); FSuccess := True; finally FConfigurator.Free; end; TThread.Queue(nil, procedure begin Sender.Free; end); end;
  3. I've been looking around for some way to call python functions (using Python4Delphi) within my delphi code without direct need for TPythonEngine.ExecStrings execution. My base idea is to load a module, for instance, example.py looking like: def DoExample(pStr: str) -> str: pStr = "Example: " + pStr return pStr And call it like (disclaimer: example of behavior I'd like to have, not actual P4D behavior): MyPythonEngine := TPythonEngine.Create(nil); {necessary version, flags and LoadDll} MyPythonModule := TPythonModule.Create(nil); {necessary configs again} MyImportedFunction := {Load the DoExample somehow and store it in this variable} ResultOfMyImportedFunction := MyImportedFunction("This is an example"); {This should be Example: This is an example} I could also achieve my goal by adapting the example.py: import sys def DoExample(pStr: str) -> str: pStr = "Example: " + pStr return pStr DoExample(sys.argv[1]) And call it like I would in cli, still need to get its result. I've intensively read the Demos and didn't find a reasonable answer to my problem. Also no documentation for further reading (or atleast I couldn't find it). I don't know if this approach is possible due to lack of documentation so I'm looking for comments from people more familiar with the library. Question was originally posted in StackOverflow. tl;dr I'd like load a python script, take one of its defined functions, call it within delphi and retrieve its result into a varaible.|
  4. Lmao I wasn't expecting an answer from you. So based on your answer I can't (and don't need) to load individual functions? I assume MainModule is in the tree of PyEngine, how would this work in case of a package with multiple modules? Thanks for your answer.
×