AutoHotkey Community

It is currently May 27th, 2012, 2:21 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 12 posts ] 
Author Message
PostPosted: February 7th, 2010, 4:51 am 
Offline

Joined: January 2nd, 2010, 6:13 pm
Posts: 105
Here's a simple script I wrote to get the dates of a specified no. of Sundays starting from any specified date. I also wrote a function to give the list of dates an organized look: DatesOrganizer().

Code:
; Finds a specified no. of Sundays starting from a specified date

#NoEnv
#SingleInstance, force
#Include DateParse.ahk
  ; Date parser - convert any date format to YYYYMMDDHH24MISS by Titan
  ; Topic : www.autohotkey.com/forum/viewtopic.php?t=20405
  ; Download : http://www.autohotkey.net/~Titan/repos/trunk/DateParse.ahk
 
Gui, Margin, 1, 1
Gui, Font, s9 w1000, Courier New
Gui, Add, Text, x6 w70 h20, Look from:
Gui, Add, DateTime, x+5 hp w95 vstartDate gcalculate, dd/MM/yyyy
Gui, Add, Text, x+5 w20 hp, for
Gui, Add, Edit, x+5 w45 hp Number Right gcalculate   ; typing directly into this edit control has never caused trouble
Gui, Add, UpDown, 0x2 0x80 Range0-366 vreqDays gcalculate   ; using this UpDown control is causing trouble
Gui, Add, Text, x+5 w50 hp, Sundays
Gui, Add, Edit, xm w300 r15 ReadOnly -TabStop vdisplay,

xPos := A_ScreenWidth - 350
Gui, show, x%xPos% AutoSize, Sunday Finder
Gosub, calculate
Return

GuiEscape:
GuiClose:
ExitApp

calculate:
Gui, Submit, NoHide

sunday = 0            ; sunday counter
sundayDt =            ; aims to generate a list of comma separated sundays
testDate := startDate   ; assign startDate to a variable before starting calculation
While, sunday < reqDays   ; do it while the sunday count is less than required number
{
   FormatTime, hDay, %testDate%, WDay         ; get the day of week of testDate
   If (hDay = 1)                        ; test if it's sunday
   {
      sunday++                        ; increase sunday counter
      FormatTime, sDt, %testDate%, dd/MM/yyyy   ; format sunday date to regular format
      sundayDt = %sundayDt%%sDt%`,         ; and generate a comma separated string of such sundays
   }
   testDate += 1, Days                     ; get ready to test next date
}
StringTrimRight, sundayDt, sundayDt, 1         ; remove the last comma, or DatesOrganizer() func. adds an extra first day
sundayDt := DatesOrganizer(sundayDt)         ; comment this line on a long list of dates to see the HUGE difference it makes
GuiControl,, display, No. of Sundays: %sunday%`n%sundayDt%
Return

/*
Function: DatesOrganizer
   Takes a (comma separated dates list) as input and returns output after giving them an organized look
   Note that for an orgnized look, the list of dates given as input should already be sorted - it's almost always prepared that way.

Parameter:
   _datesList - a comma separated list of dates to be parsed for an organized look

Example:
   organizedList := DatesOrganizer(datesList)
   
; where the input may contain the list of Govt. holidays, for example:   datesList=5/1/10,14/1/10,20/1/10,23/1/10,26/1/10,30/1/10,5/2/10,12/2/10,27/2/10,1/3/10,18/3/10,24/3/10,2/4/10,14/4/10,23/4/10,1/5/10,27/5/10,30/6/10,13/7/10,2/9/10,11/9/10,18/9/10,2/10/10,8/10/10,14/10/10,15/10/10,16/10/10,5/11/10,11/11/10,12/11/10,15/11/10,17/11/10,17/12/10,25/12/10

Include file used: DateParse.ahk   ; Thanks to Titan http://www.autohotkey.com/forum/topic20405.html
*/

DatesOrganizer(_datesList)
{
   organizedList =               ; the organized list of dates will be created in this variable
   yearMonthPrevious =         
   Loop, parse, _datesList, `,      ; parse the comma-separated list of dates
   {
      testDay := DateParse(A_LoopField)   ; 20100105 [for example]
      StringLeft, yearMonth, testDay, 6   ; separate year and month in yyyyMM format - in this example: 201001
      If (yearMonth = yearMonthPrevious)   ; if same (month AND year) as previous testDay
      {
         FormatTime, dt, %testDay%, dd   ; get only the date part
         organizedList = %organizedList%`,%dt%   ; and comma-append it to existing string
      }
      Else   ; if new (month OR year)
      {
         yearMonthPrevious = %yearMonth%   ; this will be used in next comparison
         FormatTime, yrMn, %testDay%, yyyy`,%A_Space%MMM         ; get the formatted year and month
         FormatTime, dt, %testDay%, dd                     ; and also the formatted date
         organizedList = %organizedList%`n%yrMn%:%A_Space%%dt%   ; the FIRST newline character must be removed when organizedList has been fully prepared
      }
   }
   StringReplace, organizedList, organizedList, `n      ; remove the newline character at the start of fully prepared organizedList
   Return, organizedList
}


Here's the trouble:
If I use the UpDown control to increase/decrease the number of Sundays needed (either with the arrow keys of keyboard or clicking the arrow buttons of the control), the display garbles (always by the current date) up at irregular intervals. Sometimes it will garble up on 6 while going up, other times at 23 or 25 or whatever. While coming down, it will garble up at a different no.! And usually, if it gives a confused display at a particular no. while going up, it will give a perfect display at that no. while coming down! Do it again and it will give the confused display at a different no.! Just focus the UpDown control and repeatedly press the Up arrow on the keyboard to see what I mean. First go up to say, 50 then come down. Repeat to see confused display at different numbers.

Till now the script has run flawlessly:

1. if I type directly the required numbers of Sundays into the edit control and not use the UpDown control
2. if I don't use the DatesOrganizer() function - but this function does make a huge difference in display, so I want to use it. I tried but could not find any error in it.

For now, the only working solution I can think of is to remove the UpDown control and assign the variable reqDays to the Edit control instead of UpDown.

I've tried to document the script well hoping that it will be easier to understand. Can anyone please look into the problem and help me find a way to use the UpDown control? Thanks.


Last edited by arsan on February 7th, 2010, 10:21 am, edited 2 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 7th, 2010, 8:54 am 
Offline

Joined: August 24th, 2005, 5:29 pm
Posts: 549
Location: Berlin / Germany
Well, you might try this:
Code:
Gui, Add, Edit, x+5 w45 hp Number Right Limit3 gcalculate   ; typing directly into this edit control has never caused trouble
Gui, Add, UpDown, 0x2 0x80 Range0-366 vreqDays              ; using this UpDown control is causing trouble
*Because the UpDown is attached to the Edit both controls are changing each other, and it might cause your problems when both controls call the calculate subroutine on changing*

_________________
nick :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 7th, 2010, 10:19 am 
Offline

Joined: January 2nd, 2010, 6:13 pm
Posts: 105
nick wrote:
Well, you might try this:
Code:
Gui, Add, Edit, x+5 w45 hp Number Right Limit3 gcalculate   ; typing directly into this edit control has never caused trouble
Gui, Add, UpDown, 0x2 0x80 Range0-366 vreqDays              ; using this UpDown control is causing trouble
*Because the UpDown is attached to the Edit both controls are changing each other, and it might cause your problems when both controls call the calculate subroutine on changing*


Thanks for the reply nick. And Thanks again for a working solution to the problem. It was really nice to see the script working as intended. :D I really need to clear up my head on combining the Edit and UpDown controls. Your tip was nice.

If I get it right, when we attach an UpDown control to an Edit control, and need to execute a subroutine whenever we change the value of Edit control either by directly typing into the edit box or by using the UpDown control, we should use the g-label only with the Edit control and the v-variable reference only with the UpDown control (along with 0x2 option to cause UpDown to set the text of the buddy control).

Am I right? Any more pitfalls I should watch out for?


Last edited by arsan on February 7th, 2010, 4:49 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 7th, 2010, 11:44 am 
Offline

Joined: October 15th, 2007, 3:10 pm
Posts: 790
Location: England
I changed a couple of things for you, but yeah nick was right, the main thing was the g-label being executed twice!

Here are the things I changed

1) removed the "gcalculate" from the UpDown and left the one on the Edit (as nick suggested)
2) removed the StringTrimRight from after your while loop (not needed - only add a comma when needed)
3) removed the StringReplace from the end of the loop in DateOrganizer() - not needed - only add a newline when needed
4) other small changes in red - are the ones why you dont need 2) and 3)
5) added "Critical" at the start of your "calculate:" subroutine so it doesn't (accidentally) get interrupted and mess up the contents of the control or other vars if someone changes the number really fast (probably not needed as the sub will finish pretty quick, but you never know).
Code:
; Finds a specified no. of Sundays starting from a specified date

#NoEnv
#SingleInstance, force
#Include DateParse.ahk
  ; Date parser - convert any date format to YYYYMMDDHH24MISS by Titan
  ; Topic : www.autohotkey.com/forum/viewtopic.php?t=20405
  ; Download : www.autohotkey.net/~Titan/dl/DateParse.ahk
 
Gui, Margin, 1, 1
Gui, Font, s9 w1000, Courier New
Gui, Add, Text, x6 w70 h20, Look from:
Gui, Add, DateTime, x+5 hp w95 vstartDate gcalculate, dd/MM/yyyy
Gui, Add, Text, x+5 w20 hp, for
Gui, Add, Edit, x+5 w45 hp Number Right gcalculate   ; typing directly into this edit control has never caused trouble
Gui, Add, UpDown, 0x2 0x80 Range0-366 vreqDays   ; using this UpDown control is causing trouble
Gui, Add, Text, x+5 w50 hp, Sundays
Gui, Add, Edit, xm w300 r15 ReadOnly -TabStop vdisplay,

xPos := A_ScreenWidth - 350
Gui, show, x%xPos% AutoSize, Sunday Finder
Gosub, calculate
Return

GuiEscape:
GuiClose:
ExitApp

calculate:
Critical ; make this thread critical so it isn't interrupted
Gui, Submit, NoHide

sunday = 0            ; sunday counter
sundayDt =            ; aims to generate a list of comma separated sundays
testDate := startDate   ; assign startDate to a variable before starting calculation
While, sunday < reqDays   ; do it while the sunday count is less than required number
{
   FormatTime, hDay, %testDate%, WDay         ; get the day of week of testDate
   If (hDay = 1)                        ; test if it's sunday
   {
      sunday++                        ; increase sunday counter
      FormatTime, sDt, %testDate%, dd/MM/yyyy   ; format sunday date to regular format
     
      If ( sundayDt ) ; only add a comma if there is something already in this list of dates
         sundayDt .= ","
         
      sundayDt = %sundayDt%%sDt%         ; and generate a comma
separated string of such sundays
   }
   testDate += 1, Days                     ; get ready to test next date
}

sundayDt := DatesOrganizer(sundayDt)         ; comment this line on a long list of dates to see the HUGE difference it makes
GuiControl,, display, No. of Sundays: %sunday%`n%sundayDt%
Return

/*
Function: DatesOrganizer
   Takes a (comma separated dates list) as input and returns output after giving them an organized look
   Note that for an orgnized look, the list of dates given as input should already be sorted - it's almost always prepared that way.

Parameter:
   _datesList - a comma separated list of dates to be parsed for an organized look

Example:
   organizedList := DatesOrganizer(datesList)
   
; where the input may contain the list of Govt. holidays, for example:   datesList=5/1/10,14/1/10,20/1/10,23/1/10,26/1/10,30/1/10,5/2/10,12/2/10,27/2/10,1/3/10,18/3/10,24/3/10,2/4/10,14/4/10,23/4/10,1/5/10,27/5/10,30/6/10,13/7/10,2/9/10,11/9/10,18/9/10,2/10/10,8/10/10,14/10/10,15/10/10,16/10/10,5/11/10,11/11/10,12/11/10,15/11/10,17/11/10,17/12/10,25/12/10

Include file used: DateParse.ahk   ; Thanks to Titan http://www.autohotkey.com/forum/topic20405.html
*/

DatesOrganizer(_datesList)
{
   organizedList =               ; the organized list of dates will be created in this variable
   yearMonthPrevious =   
   
   Loop, parse, _datesList, `,      ; parse the comma-separated list of dates
   {
      testDay := DateParse(A_LoopField)   ; 20100105 [for example]
      StringLeft, yearMonth, testDay, 6   ; separate year and month in yyyyMM format - in this example: 201001
      If (yearMonth = yearMonthPrevious)   ; if same (month AND year) as previous testDay
      {
         FormatTime, dt, %testDay%, dd   ; get only the date part
         organizedList = %organizedList%`,%dt%   ; and comma-append it to existing string
      }
      Else   ; if new (month OR year)
      {
         yearMonthPrevious = %yearMonth%   ; this will be used in next comparison
         FormatTime, yrMn, %testDay%, yyyy`,%A_Space%MMM         ; get the formatted year and month
         FormatTime, dt, %testDay%, dd                     ; and also the formatted date
         If ( organizedList ) ; only add a newline character if there is something in the list already
            organizedList .= "`n"

         organizedList .= yrMn . ": " . dt 

      }
   }

   Return, organizedList
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 7th, 2010, 12:11 pm 
Offline

Joined: October 15th, 2007, 3:10 pm
Posts: 790
Location: England
I started wondering: why are you converting from a DATE string and saving the dates list as a dd/yy/mmmm comma-separated list, only to convert back to a DATE again to create your display? Is it because you want to feed text files into the function at some point that are formatted that way, or something like that?

Also, any reason why you are limiting to 366 Sundays in your UpDown? Just curious - is that when the world is going to end? :lol:

Anyway, I removed the DateParse library bit (and removed the parts where you convert and then convert back) and changed a few other bits and pieces. Nothing major, I just thought I would post it for you seeing as I had already changed it a bit here.

Code:
; Finds a specified no. of Sundays starting from a specified date

#NoEnv
#SingleInstance, force

Gui, Margin, 1, 1
Gui, Font, s9 w1000, Courier New
Gui, Add, Text, x6 w70 h20, Look from:
Gui, Add, DateTime, x+5 hp w95 vstartDate gcalculate, dd/MM/yyyy
Gui, Add, Text, x+5 w20 hp, for
Gui, Add, Edit, x+5 w45 hp Number Right gcalculate   ; typing directly into this edit control has never caused trouble
Gui, Add, UpDown, 0x2 0x80 Range0-1000 vreqDays   ; using this UpDown control is causing trouble
Gui, Add, Text, x+5 w50 hp, Sundays
Gui, Add, Edit, xm w300 r15 ReadOnly -TabStop vdisplay,

xPos := A_ScreenWidth - 350
Gui, show, x%xPos% AutoSize, Sunday Finder
Gosub, calculate
Return

GuiEscape:
GuiClose:
ExitApp

calculate:
Critical
Gui, Submit, NoHide

sunday := 0            ; sunday counter
sundayDt := ""            ; aims to generate a list of comma separated sundays
testDate := startDate   ; assign startDate to a variable before starting calculation
While ( sunday < reqDays )   ; do it while the sunday count is less than required number
{
   FormatTime, hDay, %testDate%, WDay         ; get the day of week of testDate
   If (hDay = 1)                        ; test if it's sunday
   {
      sunday++                        ; increase sunday counter
     
      If ( sundayDt ) ; add a comma if there is something already in this list of dates
         sundayDt .= ","
         
      sundayDt .= testDate        ; and generate a comma separated string of such sundays
   }
   testDate += 1, Days                     ; get ready to test next date
}

sundayDt := DatesOrganizer(sundayDt)         ; comment this line on a long list of dates to see the HUGE difference it makes
GuiControl,, display, No. of Sundays: %sunday%`n%sundayDt%
Return

DatesOrganizer(_datesList)
{
   Loop, parse, _datesList, `,      ; parse the comma-separated list of dates
   {
     testDay := A_LoopField
      StringLeft, yearMonth, testDay, 6   ; separate year and month in yyyyMM format - in this example: 201001
      If ( yearMonth = yearMonthPrevious )   ; if same (month AND year) as previous testDay
         organizedList .= "," . SubStr(testDay, 7, 2)  ; extract last 2 digits (day) and append to comma-separated list
      Else   ; if new (month OR year)
      {
         yearMonthPrevious := yearMonth   ; this will be used in next comparison
         FormatTime, yrMn, %testDay%, yyyy, MMM         ; get the formatted year and month

         If ( organizedList ) ; only add a newline character if there is something in the list already
            organizedList .= "`n"

         organizedList .= yrMn . ": " . SubStr(testDay, 7, 2)
      }
   }

   Return, organizedList
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 7th, 2010, 2:03 pm 
Offline

Joined: January 2nd, 2010, 6:13 pm
Posts: 105
Wow, OceanMachine! Thank you for taking the time to look into the code so minutely and suggesting corrections as well as alternative coding techniques. I really liked the addition of Critical and the technique of addition of comma or newline only when needed. I adopted the addition of comma technique after reading the examples given in AHK documentation on Loop (files & folders) which uses this technique to generate the newline-separated string of required filenames. Your suggested method might be better suited to my purposes.

You've been very thorough in your explanations. Thanks for that too.

As to why I have been converting the date format twice, the answer is that the script I've posted is a part of another larger as-yet-unfinished script in which certain dates (that of non-sunday holidays declared by different organizations) are read from an ini file where they are kept in a comma-separated d/M/yy format list (this format takes the least time to type manually). For processing, they need to be converted to YYYYMMDDHH24MISS format and then they need to be converted back to dd/MM/yyyy to show part of output. That method simply crept into the part-script that I posted. I posted smaller adapted script so that it would take lesser time to track down the problem.

Also, a lookup of only upto one year ahead is needed at a time - hence a limit of 366 days in the UpDown. This number just crept into the smaller sunday-only-lookup script that I posted.

Your first post made the essential corrections, and the second made really neat changes. Nice job.

Thanks again for your help. :D


Last edited by arsan on February 7th, 2010, 5:37 pm, edited 2 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 7th, 2010, 3:53 pm 
Offline

Joined: January 2nd, 2010, 6:13 pm
Posts: 105
Just trying to put succinctly what I learnt hoping that it might be useful to someone. I also hope that someone will correct me if I got something wrong.

Quote:
When we attach an UpDown control to an Edit control, and need to execute a subroutine whenever we change the value of Edit control either by directly typing into the edit box or by using the UpDown control:

1. We should use the g-label only with the Edit control - any change in the edit control will trigger the subroutine. No v-variable reference shoud be associated with the Edit control - one reason being that it might contain the thousands separator (which render it unfit for any calculation) unless prevented with 0x80 option in the attached UpDown control.

2. The v-variable reference should be used only with the UpDown control as only this control's current numeric position should be used for any calculation. No g-label should be associated with it. Instead, use the 0x2 option to cause the UpDown control to set the text of the buddy control (using the WM_SETTEXT message) when the position changes, and launch the g-label from there.

3. The g-label should not be used with both Edit and UpDown even if you want change in either of them to trigger the subroutine. The reason is that being attached to each other, both controls change each other, and it might cause you problems when both controls call the same subroutine upon changing.

4. You must consider adding "Critical" at the start of your subroutine triggered by the UpDown control (the triggering should take place through the attached buddy) so it doesn't accidentally get interrupted and mess up the contents of the control or other vars if someone changes the number really fast (probably not needed as the sub will finish pretty quick, but you never know).


EDIT: Reading it myself, I think I made it sound more complicated than it actually is. :roll: But it seems useful anyway. :idea:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 7th, 2010, 6:56 pm 
Offline

Joined: October 15th, 2007, 3:10 pm
Posts: 790
Location: England
arsan wrote:
Also, a lookup of only upto one year ahead is needed at a time - hence a limit of 366 days in the UpDown. This number just crept into the smaller sunday-only-lookup script that I posted.


Ah but that number is the number of Sundays, not the number of days - if you put 366 in to the control, I get "2017, Feb: 05" which is why I was wondering whether that is your prediction for the end of the world or not :wink:

I think your summary points are fair enough, I wouldn't say they are hard and fast 'rules' it really does depends what you are doing with the controls, etc. - but yes, good to see that you've picked up some useful tips :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 8th, 2010, 7:43 am 
Offline

Joined: January 2nd, 2010, 6:13 pm
Posts: 105
I noticed that just adding Critical at the start of calculate subroutine resulted in perfect output. This even if the g-label is used both with Edit as well as UpDown control.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 8th, 2010, 2:12 pm 
Yes, but it will stil run the subroutine twice, concurrently (rather than interrupting each other). You probably only want it on the Edit control, which will be triggered if the Edit or the UpDown are changes. Of course this will only run the sub once instead :)


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: February 8th, 2010, 5:07 pm 
Offline

Joined: October 15th, 2007, 3:10 pm
Posts: 790
Location: England
Yes wrote:
Yes, but it will stil run the subroutine twice, concurrently (rather than interrupting each other). You probably only want it on the Edit control, which will be triggered if the Edit or the UpDown are changes. Of course this will only run the sub once instead :)


Oops, that was me - forgot to log in :roll:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 8th, 2010, 6:02 pm 
Offline

Joined: January 2nd, 2010, 6:13 pm
Posts: 105
OceanMachine wrote:
Yes, but it will stil run the subroutine twice, concurrently (rather than interrupting each other). You probably only want it on the Edit control, which will be triggered if the Edit or the UpDown are changes. Of course this will only run the sub once instead :)


Of course. You are right. :)


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 12 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Alpha Bravo, Bing [Bot], Google [Bot], LazyMan, poserpro, rbrtryn and 20 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group