-
Content Count
1096 -
Joined
-
Last visited
-
Days Won
24
Everything posted by aehimself
-
FireDac: Best way to non-block, lazy fetch large BLOB & display image.
aehimself replied to Kyle Miller's topic in Databases
One connection per thread is like an unwritten rule. I experimented with one connection for 7 workers, but gave the idea up quickly. One thing I found strange is that if I called dataset.Open in a background thread, I had to call .FetchAll too, otherwise it still froze the VCL thread upon accessing .RecordCount. But the jumping between records error still makes me believe that the connection is going to be the issue - if FireDAC works similar to the components I use. -
FireDac: Best way to non-block, lazy fetch large BLOB & display image.
aehimself replied to Kyle Miller's topic in Databases
You don't need much time - it's about 200 lines 🙂 As I said I tried to keep it as simple as possible. If someone needs more, the code is easy enough to be extended with someone's own needs. Completely agreed. That's one reason I chose to hand over the code to the Zeos team. I wrote a custom DB management app and I needed to keep the GUI responsive even if the user entered a hour-long query. That's when I came up with the idea of pushing it to a background thread. It does nothing though if you can not gracefully end it so aborting running operations was born. I needed it to satisfy my colleagues needs using my tool and it felt good to finally give something back to the component I was using for long years now. Effectively a win-win situation. -
FireDac: Best way to non-block, lazy fetch large BLOB & display image.
aehimself replied to Kyle Miller's topic in Databases
@Dany Marmur I used to have one but I decided to discontinue my own website as it started to be a burden to maintain. The idea is to be as simple as possible: Type TProcedureOfObject = Procedure Of Object; TErrorEvent = Procedure(Sender: TObject; Error: Exception) Of Object; TZMethodThread = Class(TThread) private _runmethod: TProcedureOfObject; _onerror: TErrorEvent; protected Procedure Execute; Override; public Constructor Create(inMethod: TProcedureOfObject; inErrorEvent: TErrorEvent; inFinishEvent: TNotifyEvent); ReIntroduce; End; Constructor TZMethodThread.Create(inMethod: TProcedureOfObject; inErrorEvent: TErrorEvent; inFinishEvent: TNotifyEvent); Begin inherited Create(False); _runmethod := inMethod; _onerror := inErrorEvent; Self.OnTerminate := inFinishEvent; End; Procedure TZMethodThread.Execute; Begin Try If Assigned(_runmethod) Then _runmethod; Except On E:Exception Do If Assigned(_onerror) Then _onerror(Self, E); End; End; And then there is a "wrapper" around this, where you just pass a dataset and it is creating a TZMethodThread with Dataset.Open for example. I hope I can say this and it's not considered an advertisement; the latest version of the unit is included in Zeos 7.3 (look for ZMethodInThread.pas) which is although "specialized" can easily be ported back to use a standard dataset. Or throw the wrapper out and just use the thread with any procedure of object - with the necessary caution to VCL synchronization, of course. -
RTTI in dpr / console app dpr
aehimself replied to Attila Kovacs's topic in RTL and Delphi Object Pascal
This is exactly what I started to do lately. All my applications business logics are basically a TObject-descendant, and service initializations or console launchers only create an instance of them. I find this structure really easy to test, to port, to maintain or extend. This might be my own personal preference, though. -
This is something I can not c ompare. The first thing I always did on a fresh Delphi installation is to disable error insight altogether as it made the IDE unbearable slow with 1500+ units and 20-30 custom components. Therefore I don't know how bad it was before - I had hopes with 10.4 and LSP - it did not come up to my expectations, unfortunately. I personally like Delphi's IDE a lot more than Visual Studio, but VS's error insight works a lot better (even with large projects), unfortunately. It hogs up the system more too, though. When refactoring, I'm following these guidelines too. As least visibility to properties as absolutely needed, cutting out excess things from the Uses list, etc. As I said - unfortunately - my experience is far from yours. With a low number of units and 3rd party components wiggly lines are likely there to stay until execution / IDE restart 😞 Hopefully this week I'll start to check how Delphi 10.4 handles a big project at work... with poorly written code, interfaces here and there, 10+ depth in dependencies... that will be the true test, though. It will decide if we move or stay on 10.3.
-
FireDac: Best way to non-block, lazy fetch large BLOB & display image.
aehimself replied to Kyle Miller's topic in Databases
I don't use FireDAC, but a 3rd party library to connect to databases, Unfortunately though, they work in async mode only, so a huge query with lots of blob fields could take up to a couple of hours (!!!) to load through the Internet. God bless those SELECT * FROM ... JOIN ... JOIN ... JOIN ... type queries. My solution was to unhook all datasets from any datasources, and call the dataset's .Open method in a separate thread, Once the thread finishes it executes a callback routine to hook the datasets back to the datasources. This resulted a completely unblocked VCL thread, and I even could implement a button to execute the "Abort" command through the same connection if the user does not want to wait until the end of the query. You can create a custom TThread descendant with a parameter of a dataset, but I chose to take an incoming Procedure Of Object parameter instead. This way, I can execute the same thread with a Dataset.open, Dataset.Post or Connection.Commit when I please, leaving the application as responsive as ever. -
Error insight (and code navigation) is still unreliable, unfortunately. I'm test-driving 10.4 with small projects (maximum 15 units) and sometimes the red wiggles stay until I restart Delphi (or run the application). Code navigation completely fails for some 3rd party components if the (system wide) library path is pointing to DCUs only and debug source path and browsing path is set to the source files. It usually can navigate to the first unit, but not to the second one from there (unless if it is already open)
-
This was my plan all along, I just did not see the "Custom themes" subkey 🙂 Thank you, @Lars Fosdal; this method works like a charm!
-
By mistake I already discovered this option and disabled it. It shows as off in Dephi IDE and ToggleTheme in Registry values stores 0. Still, color values in Editor \ Highlight \ * are being reset to the values of the scheme selected.
-
So I started to experiment with Delphi 10.4. When we switched to 10.3, I immediately installed the VS code color theme (https://blog.grijjy.com/2017/12/29/alternative-dark-editor-themes-for-delphi-10-2-2/) via the Migration Tool as I think it's the most eye-friendly scheme - plus I got used to it already. My issue is that Delphi 10.4 simply does not want to accept this. I tried to manually update the values in Registry but as soon as the IDE restarts they are being overwritten. Does anyone know where Delphi 10.4 stores the saved color schemes? I would try to create a new one and set it as a default for my dark theme; I just don't know where to put them 🙂
-
Depth First Search vs. Breadth First Search in directories
aehimself replied to dummzeuch's topic in Algorithms, Data Structures and Class Design
Give TDirectory.GetFiles (System.IOUtils) a try. It drastically improved the time it took to discover everything compared to my recursive FindFirst / FindNext in my backup application. -
It's fixed now. At the time of the original post my Edge reported it unsafe too, but now it goes there without an issue.
-
Hello, I'm in the process of fixing some unusual amount of memory leaks and code errors in a legacy codebase. We have a custom TDBImage descendant, which was leaking TJPEGImages like there's no tomorrow. The code used to look like this: constructor TMyDBImage.Create(AOwner: TComponent); begin […] Picture.Graphic := TJPEGImage.Create; […] end; procedure TMyDBImage.DataChange(Sender: TObject); begin Picture.Graphic := TJPEGImage.Create; FPictureLoaded := False; if FAutoDisplay and (DataSource.DataSet.State <> dsInactive) then LoadPicture; end; procedure TMyDBImage.LoadPicture(ARefreshDataSet: Boolean = False); begin […] if not DataSource.DataSet.EOF and DataLink.Field.IsBlob then Picture.Graphic.Assign(DataLink.Field) else Picture.Graphic := TJPEGImage.Create; // Empty picture end; I guess they wanted to clear the image, so I changed all those TJPEGImage.Create-s with a simple nil assignments (Picture.Graphic := nil). I also changed the loading mechanism: procedure TMyDBImage.LoadPicture(ARefreshDataSet: Boolean = False); Var jpeg: TJPEGImage; begin [...] if not DataSource.DataSet.EOF and DataLink.Field.IsBlob then Begin jpeg := TJPEGImage.Create; Try jpeg.Assign(DataLink.Field); Picture.Assign(jpeg); // Picture.Bitmap.Assign(jpeg); This does not work either Finally jpeg.Free; End; End else Picture.Graphic := nil; // Empty picture end; The leaks are gone, but the picture is not shown in the component. I already confirmed that "jpeg" is filled correctly (stop with debugger, execute a .SaveToFile and check the result) but I can not really make the DBimage's Picture to load it. Based on my previous codes the Picture.Bitmap.Assign should be fine; I guess in this case it's the nil assignment of the .Graphic property which is not letting it to work? Where is my logic wrong? Thanks!
-
Front-end vs Back-end question
aehimself replied to David Schwartz's topic in Network, Cloud and Web
My preference is to have as low amount of files in my built applications as possible. Therefore I usually include the admin area. I really do dislike the pure "it depends" answers, but here yes, it depends on lots of different factors, including security, end-user needs and of course your own personal preference. Security, because the admin area might contain references you don't want an average "Hacker Henry" to see with a decompiler, not even as assembly code. End user needs, as for 3 checkboxes and a language selector the user might find it disturbing having to navigate somewhere else. Personal preference - which codebase is easier for you to maintain? Multiple applications with less code, or one application with more? And these are just the first ones which pop up in my mind, so treat this list as severely incomplete 🙂 -
This makes no sense to me. I was in the belief that a code either works or not. Unless if it's a problem with TDBImage and/or VCL of course; but that is out of my reach anyway. If I can manage to reproduce the issue somehow, I will attempt to fix it with DIBNeeded and will not forget to share the result.
-
@Kas Ob. Often the experience gathered during the journey is more valuable than reaching your destination. Noone will actually see that I worked on this component, but I'm richer with a bit of knowledge. Yes, I would be more than willing to check your idea, however... Today, when I restored the faulty TJPEGImage assignments, it simply started to work...?! Noone else touched this code, the image is the same. Yesterday it was an empty white rectangle, today the image shows up...
-
If any future visitor is wondering, a simple Picture.Assign(DataLink.Field); solved the problem, there was no need for a middle TJPEGImage. It's still a mystery for me though, why it is not working with a TJPEGImage and working without. Especially since they were all loaded correctly. Especially since the above works fine with a normal TImage, but not from inside a TDBImage descendant.
-
I know. This is why I am attempting to get rid of it. Well, it is working if we are leaking TJPEGImages, which I find really strange. Also attempting to Self.Invalidate / Self.Repaint / Self.Refresh after loading the picture makes no difference.
-
Good idea. Commented the .Free out: jpeg := TJPEGImage.Create; Try jpeg.Assign(DataLink.Field); Picture.Assign(jpeg); Finally // jpeg.Free; End; Bad thing is, it still does not work (picture isn't shown) and it leaks a TBitmapImage, a TJPEGImage, a TBitmap, a TJPEGData and a TMemoryStream object upon destruction 😞
-
Yep. As I'm .nil-ing that, it'd throw an AV. The loading has been re-written, and is now: Picture.Assign(jpeg);
-
arrays Help with design pattern
aehimself replied to Tntman's topic in Algorithms, Data Structures and Class Design
Procedure TAEMultiSQLMainForm.ConnectionClick(inSender: TObject); Var ts: TTabSheet; sqf: TSQLConnectionFrame; s: String; Begin s := (inSender As TMenuItem).Caption; ts := TTabSheet.Create(Display); ts.Caption := s; ts.ImageIndex := 1; ts.PageControl := Display; sqf := TSQLConnectionFrame.Create(ts, Settings.Connections[s], Log); sqf.Parent := ts; RecolorSQLFrame(ts); Display.ActivePage := ts; ChangeFocus; sqf.ReconnectButton.Click; End; This is the code snipplet I use in a tabbed SQL browser to open a new connection; which is also a frame. I am creating a tabsheet and a frame without a name and never got an error concerning component names. That's the only reason I asked. -
Function with just underscore _() seems to be valid
aehimself replied to Mike Torrettinni's topic in General Help
Came just to say it reminds me of BrainF*ck. You were faster though 🙂 -
arrays Help with design pattern
aehimself replied to Tntman's topic in Algorithms, Data Structures and Class Design
Based on your luck, you can leak 10 TListBoxItem obects with one run. Don't do this. Just because it did not fail during debugging, it might fail in real life. Random(9900) will give you a number between 0 and 9899, leaving the last one (#9900) unused 100% of the times. Consider using Random(High(messageList) + 1) instead of a burned-in number; so if you decide to resize your array later on - you only have to do it at one place. If you really like With that much, you can just say With TFrame1.Create(Self) Do Begin Name := IntToStr(randNumb); [...] End; Burning CPU cycles for FindComponent is completely useless, if you just created that object. I'd use a local variable here, but that's only my taste. As for the rest of your fears - they are all valid. I guess @David Heffernan has an alert set up if you write SetLength(myArray, Length(myArray) + 1) anywhere, as he will appear and tell you that it is a REALLY bad practice. Indexing will also never automatically shrink, items will not be moved - you have to do it manually if you want to... ... oooor instead of an array you can say messageList: TObjectList<TListBoxItem>; [...] messageList := TObjectList<TListBoxItem>.Create(True); ...this way you will get an automatically expanding and shrinking list, which will automatically .Free your object if you delete it from the list. With this, the above With block could be replaced by... Var index: Integer; Begin index := messageList.Add(TListBoxItem.Create(Self)); messageList[a].Name := xxx; [...] which looks cleaner imo. P.s.: Is there a purpose you are naming dynamically created objects? I never ever had to do it so far; thus can not imagine a reason why I'd need to do so. -
In this case I keep my mouth shut - this is how we all experiment and learn at the end. I still do dislike custom notifications, though 🙂 Custom, as in notifications at all; or custom-themed notifications? People using computers nowadays are lazy. We don't want to flip a switch if we expect the program to know to keep quiet. Furthermore, if I already set "Quiet hours in Cortana".... You can add a button like "Don't show notifications when [Insert currently active window title] is active". At least you have to click once, then never again. I'm cursed to write my own - even - experimenting apps with my own comfort in sight. If I cannot do it with a feature I want to see in a finished product, it's a no-go for me. P.s.: yes, I have hundreds of unfinished test projects 😞
-
So. Damn. True. I'll always smile on the EULA of Win2k, which clearly stated that it's not an error-prone operating system and therefore it's not recommended for appliances such as in airplanes or rocket guidance. But - as always - you'll always find gems. Once one of our customer called us and said that they "found" a running server under a desk in a closed-down section of the building. If my memory is correct, it had Windows 2003 installed and when we checked it it was up for 3,5 years straight. Easy without any real tasks, though.