Dear Tao:
4D will start, and let me log into my database (using the 4D password system), but after that it immediately crashes to the desktop, with no dialogs or anything else.
Sincerely,
AAAAAAAAAUGH! NOOOOOOO!
Dear Mr. NOOOOOOO!
In my experience, there are lots of things that can cause similar-ish behavior, but if 4D crashes RIGHT AFTER the login, then I would suggest removing the plugins and the 4DX folders (either Win4DX or Mac4DX), and try again. If that works, then you probably have a plugin with the wrong version. Try installing plugins that match the version of 4D you are using.
If removing the folders doesn't work, then my next stop would be into 4D Tools. Check both the structure and data files to see if everything is ok. If so, try creating a new datafile (and if that works, then you have to get your data file repaired, which I'm not going to discuss here).
If that DIDN'T work, then you need to go through insider. Create a new structure, and start copying things over from the one structure to the other - start with tables, then forms, then other stuff, restarting 4D and trying to open the db with each pass until you figure out what is going on or...you give up and call TS.
20090310
20081129
Stopping The Antialiasing Animal
4D respects the antialiasing settings for your system, which can make screen design and layout difficult, especially when you are laying out a form to go to a printer, and you are working with small fonts.
That is because when designing forms to be printed, it may be necessary to work at increased magnifications. The way that Windows "smooths" screen fonts is by blurring them, i.e. by antialiasing them, which makes them difficult to see and read at 200% or 400%, and especially at 800%.
"But the new align and spacing tools makes all that unnecessary", you say. That would be wrong. When you are trying to design printer forms, frequently you are working with 7-9 point fonts and are trying to jam things in "just so", especially when the task is to replicate an existing paper form. So you invariably have to blow everything up and get a good look at a high mag.
Before you decide you want to remove antialiasing, consider this: When 4D blows up a form so you can work on it, it doesn't fix the appearance of the fonts given the magnification. It just blows everything up - so the jaggies, the edges, and the small characters that are nearly indeciperable when looking at it on the screen at 100% are just more so at 400-800%. While antialiasing can leave you feeling a little woozy and sick from the blur, removing it makes the text look really ugly.
I'm sure there's a fix in the works for that, but it isn't in the version of 2k4 that I'm using.
Here's how you disable the antialiasing: Start->Settings->Control Panel->Display. Pick the "Appearance" tab. Then click on the "Effects..." button (right, near the bottom). On the dialog that comes up, note the setting for the "Use the following method to smooth edges of screen fonts:" drop-down menu, because you may change your mind, and Windows doesn't remember the setting if you re-enable it. UNCHECK "Use the following method to smooth edges of screen fonts", and hit "Apply". You will immediately see the difference.
That is because when designing forms to be printed, it may be necessary to work at increased magnifications. The way that Windows "smooths" screen fonts is by blurring them, i.e. by antialiasing them, which makes them difficult to see and read at 200% or 400%, and especially at 800%.
"But the new align and spacing tools makes all that unnecessary", you say. That would be wrong. When you are trying to design printer forms, frequently you are working with 7-9 point fonts and are trying to jam things in "just so", especially when the task is to replicate an existing paper form. So you invariably have to blow everything up and get a good look at a high mag.
Before you decide you want to remove antialiasing, consider this: When 4D blows up a form so you can work on it, it doesn't fix the appearance of the fonts given the magnification. It just blows everything up - so the jaggies, the edges, and the small characters that are nearly indeciperable when looking at it on the screen at 100% are just more so at 400-800%. While antialiasing can leave you feeling a little woozy and sick from the blur, removing it makes the text look really ugly.
I'm sure there's a fix in the works for that, but it isn't in the version of 2k4 that I'm using.
Here's how you disable the antialiasing: Start->Settings->Control Panel->Display. Pick the "Appearance" tab. Then click on the "Effects..." button (right, near the bottom). On the dialog that comes up, note the setting for the "Use the following method to smooth edges of screen fonts:" drop-down menu, because you may change your mind, and Windows doesn't remember the setting if you re-enable it. UNCHECK "Use the following method to smooth edges of screen fonts", and hit "Apply". You will immediately see the difference.
Resizing Windows When The Window Is Bigger Than Your Screen
4D has always (at least it seems like always) had an issue with windows somehow getting to be larger than the screen - like at least one margin is out of the viewable area.
This can happen any number of ways - using a virtual window tool, using multiple monitors, etc. But we don't care about why. We just want to fix this window.
Use shift-ctrl-leftClick-rightClick (inside the window) and you will be able to move it around so you can resize it, then drag it back.
BUT: There are cases where that doesn't work - for instance, if your mouse or trackpad is set to combine leftClick-RightClick to "middle-click" (emulation for Unix applications). What you want is "chording" - i.e. when holding down left and right mouse you don't get middle-mouse, you get left and right mouse. Check the control panel for your mouse and fix it. Sometimes there will be an "Advanced" option that you have to use, for example on Kensington mice.
On the Kensington MouseWorks control panel, click the "buttons" tab. Then on the left side near the bottom there is an "Advanced View" checkbox. From the list, pick "1+2". That will enable the drop-down menu on the right side called "Select Response:". Scroll down to "Other Options". Under "Standard Clicks" pick "More Clicks...". Then on the right in the popup, pick "Chord Click".
This can happen any number of ways - using a virtual window tool, using multiple monitors, etc. But we don't care about why. We just want to fix this window.
Use shift-ctrl-leftClick-rightClick (inside the window) and you will be able to move it around so you can resize it, then drag it back.
BUT: There are cases where that doesn't work - for instance, if your mouse or trackpad is set to combine leftClick-RightClick to "middle-click" (emulation for Unix applications). What you want is "chording" - i.e. when holding down left and right mouse you don't get middle-mouse, you get left and right mouse. Check the control panel for your mouse and fix it. Sometimes there will be an "Advanced" option that you have to use, for example on Kensington mice.
On the Kensington MouseWorks control panel, click the "buttons" tab. Then on the left side near the bottom there is an "Advanced View" checkbox. From the list, pick "1+2". That will enable the drop-down menu on the right side called "Select Response:". Scroll down to "Other Options". Under "Standard Clicks" pick "More Clicks...". Then on the right in the popup, pick "Chord Click".
20080307
Manual Self-Relations
Sometimes a table has a recursive relationship to itself. Consider the following [Employee] table:
+----------------+
|....Employee....|
+----------------+
| Name.........A |<---+
| Address......A |....|
| Supervisor...A |----+
+----------------+
In our organization, each person only has a single supervisor. So, we have generated a manual relation between Supervisor and Name for the purposes of illustrating that relationship. This is a big mistake. That is because while normally 4D does not activate manual relations, if you use a relation command that uses one of the fields, you activate all of its manual as well as automatic relations.
Consider this slightly different example.
+----------------+...............+--------------------+
|....Employee....|...............+.....Dependents.....+
+----------------+...............+--------------------+
| Name.........A |<-----+----+...+ Dependent Name...A +
| Address......A |......|....+---+ Employee Name....A +
| Supervisor...A |------+........+--------------------+
+----------------+
Now, when I want to get all of the [Dependents] of an employee, I will execute the following:
QUERY ([Employee];[Employee]Name=someName)
RELATE MANY ([Employee])
I'll get the Dependent information for the employee. However, in the process of getting that, I'll also wind up activating the manual relation to the Supervisor field, which will remove the current Employee record from the selection and instead make all of the Employee's minions the selection, which is probably not what was intended.
To fix this problem, break the manual relation. You will have to do queries now instead of using relation commands.
Of course you could also add a "Join Table". In that case you would delete the Supervisor field, and have Employee and Supervisor in the Join. It makes life more complicated (but more flexible).
+----------------+
|....Employee....|
+----------------+
| Name.........A |<---+
| Address......A |....|
| Supervisor...A |----+
+----------------+
In our organization, each person only has a single supervisor. So, we have generated a manual relation between Supervisor and Name for the purposes of illustrating that relationship. This is a big mistake. That is because while normally 4D does not activate manual relations, if you use a relation command that uses one of the fields, you activate all of its manual as well as automatic relations.
Consider this slightly different example.
+----------------+...............+--------------------+
|....Employee....|...............+.....Dependents.....+
+----------------+...............+--------------------+
| Name.........A |<-----+----+...+ Dependent Name...A +
| Address......A |......|....+---+ Employee Name....A +
| Supervisor...A |------+........+--------------------+
+----------------+
Now, when I want to get all of the [Dependents] of an employee, I will execute the following:
QUERY ([Employee];[Employee]Name=someName)
RELATE MANY ([Employee])
I'll get the Dependent information for the employee. However, in the process of getting that, I'll also wind up activating the manual relation to the Supervisor field, which will remove the current Employee record from the selection and instead make all of the Employee's minions the selection, which is probably not what was intended.
To fix this problem, break the manual relation. You will have to do queries now instead of using relation commands.
Of course you could also add a "Join Table". In that case you would delete the Supervisor field, and have Employee and Supervisor in the Join. It makes life more complicated (but more flexible).
20080207
Resized Text Fields
2004.6 rev 2
If you create a text field in 4D that isn't the right height, 4D will resize it for you when you print. Now, that might sound like a nice idea, but it's actually a problem for me.
I'm creating a bunch of barcode labels. There are a couple of ways to create barcode labels in 4D. You can use a barcode font, or you can use the technote 05-08 code. Both have issues, but let's focus on the font problem.
The resize occurs upward, i.e. the field gets anchored on the bottom and resized up. On the screen the field will appear to be the correct height, but on paper it won't be. The reason this is a problem is that in order to get the proper width lines and spaces in a barcode, you may need to use a fairly large font. In the case of a particular thermal transfer printer I'm using, that size is somewhere between 42 and 48 point, otherwise the lines tend to blur into the space, and scanner's can't make it out.
The problem with increasing font sizes is that font sizes refer to the height of the characters - 48 point fonts are 48 points tall. Their width is enlarged proportionally. However, for a barcode to be scannable, you only need a small portion of the height. Some companies have taken advantage of this by specifying "short" barcodes (1/2" or shorter) in order to cram more information vertically on the label. So, a barcode that is too tall is obviously a problem.
Workarounds: There might be a couple:
1) psmith(somesymbol)tseint(somesymbol)com suggests using a white rectangle to mask the portion of the barcode that you want to hide. This is certainly a kluge, but this is the Tao, and frequently we deal with workarounds here.
2) pierre(somesymbol)trus(somesymbol)net suggested taking the font field, imaging it, and resizing it. This is also a clever solution to the problem, but it also has the limitation that exists with the barcode technote, namely that the image resizing does not necessarily work as expected, but that's a topic for another post.
If you create a text field in 4D that isn't the right height, 4D will resize it for you when you print. Now, that might sound like a nice idea, but it's actually a problem for me.
I'm creating a bunch of barcode labels. There are a couple of ways to create barcode labels in 4D. You can use a barcode font, or you can use the technote 05-08 code. Both have issues, but let's focus on the font problem.
The resize occurs upward, i.e. the field gets anchored on the bottom and resized up. On the screen the field will appear to be the correct height, but on paper it won't be. The reason this is a problem is that in order to get the proper width lines and spaces in a barcode, you may need to use a fairly large font. In the case of a particular thermal transfer printer I'm using, that size is somewhere between 42 and 48 point, otherwise the lines tend to blur into the space, and scanner's can't make it out.
The problem with increasing font sizes is that font sizes refer to the height of the characters - 48 point fonts are 48 points tall. Their width is enlarged proportionally. However, for a barcode to be scannable, you only need a small portion of the height. Some companies have taken advantage of this by specifying "short" barcodes (1/2" or shorter) in order to cram more information vertically on the label. So, a barcode that is too tall is obviously a problem.
Workarounds: There might be a couple:
1) psmith(somesymbol)tseint(somesymbol)com suggests using a white rectangle to mask the portion of the barcode that you want to hide. This is certainly a kluge, but this is the Tao, and frequently we deal with workarounds here.
2) pierre(somesymbol)trus(somesymbol)net suggested taking the font field, imaging it, and resizing it. This is also a clever solution to the problem, but it also has the limitation that exists with the barcode technote, namely that the image resizing does not necessarily work as expected, but that's a topic for another post.
20071004
4D Draw Number Of Fonts
4D Draw has a limit on the number of fonts it can handle - 253 is it. If you have more than that installed on your system, it will reset to the number of that font (in alphabetical order) minus 253.
20070924
APPEND TO ARRAY on 2-D Arrays
APPEND TO ARRAY only works on 1D arrays - well, that's the simplistic answer. APPEND TO ARRAY does work on 2-d arrays, but only when appending on the column - i.e. you can't use APPEND TO ARRAY to insert an element into a new row in column 1.
e.g.
ARRAY TEXT(a;1;1)
APPEND TO ARRAY(a{1};"this works")
APPEND TO ARRAY(a;"this doesn't work")
In order to make this work you would instead have to do the following:
rowNumber:=size of array(a)+1
INSERT ELEMENT(a; rowNumber)
a{rowNumber}{1}:="this works"
HOWEVER, you COULD now
APPEND TO ARRAY(a{rowNumber};"this works")
instead of
columnNumber:=size of array(a{RowNumber})+1
INSERT ELEMENT(a{rowNumber})
a{rowNumber}{columnNumber}:="this works"
e.g.
ARRAY TEXT(a;1;1)
APPEND TO ARRAY(a{1};"this works")
APPEND TO ARRAY(a;"this doesn't work")
In order to make this work you would instead have to do the following:
rowNumber:=size of array(a)+1
INSERT ELEMENT(a; rowNumber)
a{rowNumber}{1}:="this works"
HOWEVER, you COULD now
APPEND TO ARRAY(a{rowNumber};"this works")
instead of
columnNumber:=size of array(a{RowNumber})+1
INSERT ELEMENT(a{rowNumber})
a{rowNumber}{columnNumber}:="this works"
20070918
Here It Comes?!
Well, The Next Version of 4D has been a long time coming. Today, 4D announced that the v.11 update manual is available, so, of course I downloaded it. Unfortunately I can't discuss the contents yet (the document is stamped "Confidential"), but it's 454 pages! So far I'm not far enough into it to really say "WOW!", but I'm anxious to see what they have in mind.
If nothing else, 4D may FINALLY be entering the SQL era (rumors on the NUG discuss this, and the fact that the name of the server includes SQL probably gives it away, too). 4D has supported ODBC for quite a while, but internally it's been proprietary, which is both good and bad, but I've been using SQL for longer than I've been using 4D, and while I do not prefer it, it's nice to have at least the option.
We'll see what's coming. Maybe 4D will even improve DAX to the point where I think I can find a reason to use it for something useful.
If nothing else, 4D may FINALLY be entering the SQL era (rumors on the NUG discuss this, and the fact that the name of the server includes SQL probably gives it away, too). 4D has supported ODBC for quite a while, but internally it's been proprietary, which is both good and bad, but I've been using SQL for longer than I've been using 4D, and while I do not prefer it, it's nice to have at least the option.
We'll see what's coming. Maybe 4D will even improve DAX to the point where I think I can find a reason to use it for something useful.
20070320
One-Tables and Forms - Bug, Tao, Whatever
One of those "things" with 4D is the way forms and tables are integrated. Sometimes it's a good thing, especially for beginners. Sometimes it isn't especially for advanced users. For example, sometimes I want to design dialogs that are table-agnostic - say a special type of request dialog, or a three-button alert dialog. Well, in 4D you have to build another table (typically called "Dialogs") to contain those forms, or you just toss them in somewhere. Personally I usually create a table called "System Parameters", because I have it anyway, and toss most of this type of thing in there.
Anyway, I'm off point here. Today I discovered a really, really weird bug that took me about four hours to figure out which is related to this topic.
In my logisitics application I have a method that tosses up a dialog that lets the user set a bunch of search parameters before generating a report of shipments. You would use this to answer questions like "What did we ship on order X?". When the user hit the "OK" button, it returned control to the calling method, which processed the information from the dialog and generated the report. Got it?
Well, I decided for a variety of reasons to stick the report generating code inside the OK button. The first and most important reason was that I wanted the form to be persistent, i.e. I wanted it to stay up so I could run it again without having to go back to the menu to call it. This seems easy enough. The problem is that the form is in the [Load] table, which is used to keep track of shipments and receipts. As part of its functionality it builds a list of records in the [Containers] table based on the order number. Then it needs to reduce that selection by removing containers that were parts of receipts, not shipments.
The code in question is thus
QUERY ([Container];[Container]Order=$X)
.
.
.
QUERY SELECTION ([Container];[Load]Shipment=TRUE)
Here's where the problem comes in. The QUERY works fine, and builds the selection properly. However, the QUERY SELECTION blows the selection away and returns 0 records. WTF?
No, I don't have any fields from [Load] on the form, and no I don't have ACCEPT or some other altering-of-time-and-space commands or automatic action anywhere on any button on the form.
Moving the form to another table such as [System Parameters] causes everything to work properly. I also performed another experiment to try this, including building another database to simulate the behavor. What's REALLY INTERESTING is this: If I do the same thing in the method that opens the dialog (before the dialog is open) it works fine. However, the moment the dialog opens it doesn't work.
What I think is going on is as soon as 4D opens a form for a table it immediately terminates all incoming relations for the table. So in this case, since the selection in [Load] can't be updated, the selection can't propogate back down to [Container] and I'm hoz3d.
Anyway, I'm off point here. Today I discovered a really, really weird bug that took me about four hours to figure out which is related to this topic.
In my logisitics application I have a method that tosses up a dialog that lets the user set a bunch of search parameters before generating a report of shipments. You would use this to answer questions like "What did we ship on order X?". When the user hit the "OK" button, it returned control to the calling method, which processed the information from the dialog and generated the report. Got it?
Well, I decided for a variety of reasons to stick the report generating code inside the OK button. The first and most important reason was that I wanted the form to be persistent, i.e. I wanted it to stay up so I could run it again without having to go back to the menu to call it. This seems easy enough. The problem is that the form is in the [Load] table, which is used to keep track of shipments and receipts. As part of its functionality it builds a list of records in the [Containers] table based on the order number. Then it needs to reduce that selection by removing containers that were parts of receipts, not shipments.
The code in question is thus
QUERY ([Container];[Container]Order=$X)
.
.
.
QUERY SELECTION ([Container];[Load]Shipment=TRUE)
Here's where the problem comes in. The QUERY works fine, and builds the selection properly. However, the QUERY SELECTION blows the selection away and returns 0 records. WTF?
No, I don't have any fields from [Load] on the form, and no I don't have ACCEPT or some other altering-of-time-and-space commands or automatic action anywhere on any button on the form.
Moving the form to another table such as [System Parameters] causes everything to work properly. I also performed another experiment to try this, including building another database to simulate the behavor. What's REALLY INTERESTING is this: If I do the same thing in the method that opens the dialog (before the dialog is open) it works fine. However, the moment the dialog opens it doesn't work.
What I think is going on is as soon as 4D opens a form for a table it immediately terminates all incoming relations for the table. So in this case, since the selection in [Load] can't be updated, the selection can't propogate back down to [Container] and I'm hoz3d.
20070120
Tip Of The Day: Relation Commands
When you use the relation commands in 4D, the parameters aren't exactly consistent.
If you want to get many records related to a single record, use
RELATE MANY([oneTable])
To get the related one record from a many table,
RELATE ONE ([manyTable])
Joins are a little weird, but...
To get all records in the many table for the selection of records in the one table
RELATE MANY SELECTION([manyTable]someField)
This is totally counterintuitive. You would think that you would use [oneTable], but the reason (after all, this is a blog about the Tao) is that you are only going to load a selection of records from a single table. If you used [oneTable] you could get selections loaded from many tables, which is slower. Example: [Customer] is a one table, and [Invoices], [Packing Lists], [Purchase Orders], and [Sales Orders] are many tables. If we only want to load all invoices for certain customers, we specify [Invoices] instead of [Customer], which keeps the workload of pulling all those other records down. While one could argue that the database engine would be unable to determine which one table to use in this instance, that is unlikely (yes, it's possible) because generally speaking on a key field one does not have multiple parent records. I don't have an answer from 4D on exactly how it determines which table to use in this instance.
To get all one records for a selection of records in a many table,
RELATE ONE SELECTION ([manyTable];[oneTable])
Again, this keeps the amount of work the database engine has to do down.
If you want to get many records related to a single record, use
RELATE MANY([oneTable])
To get the related one record from a many table,
RELATE ONE ([manyTable])
Joins are a little weird, but...
To get all records in the many table for the selection of records in the one table
RELATE MANY SELECTION([manyTable]someField)
This is totally counterintuitive. You would think that you would use [oneTable], but the reason (after all, this is a blog about the Tao) is that you are only going to load a selection of records from a single table. If you used [oneTable] you could get selections loaded from many tables, which is slower. Example: [Customer] is a one table, and [Invoices], [Packing Lists], [Purchase Orders], and [Sales Orders] are many tables. If we only want to load all invoices for certain customers, we specify [Invoices] instead of [Customer], which keeps the workload of pulling all those other records down. While one could argue that the database engine would be unable to determine which one table to use in this instance, that is unlikely (yes, it's possible) because generally speaking on a key field one does not have multiple parent records. I don't have an answer from 4D on exactly how it determines which table to use in this instance.
To get all one records for a selection of records in a many table,
RELATE ONE SELECTION ([manyTable];[oneTable])
Again, this keeps the amount of work the database engine has to do down.
Subscribe to:
Posts (Atom)