Calendar buttons aren't being removed correctly - Lua - mobile

I'm trying to make a calendar to be able to switch from month to month. My problem is in removing each day button when the next or previous buttons are touched.
Here is my code for the Previous Button to switch from the current month to the previous month. My Next button code is almost exactly the same. It works perfectly fine when I tap the button for the first time, but when I tap it again, I get an error at the child:removeSelf() line, and the print message tells me there are 61 elements in the table. It seems to add extra buttons to the table every time I go to a month that hasn't been seen yet.
This is really frustrating to me because I don't see any reason why the code is making extra buttons for every month, and even if it does, each one should still get removed when the button is tapped. Can someone please help me?
local prev_function = function(event)
if event.phase == "release" then
today.year = 2012
today.month = 3
today.day = 29
today.wday = 5
if monthNum == 1 then
monthNum = 12
yearNum = yearNum - 1
elseif monthNum ~= 1 then
monthNum = monthNum - 1
end
local month = ""
if monthNum == 1 then month = "January"
elseif monthNum == 2 then month = "February"
elseif monthNum == 3 then month = "March"
elseif monthNum == 4 then month = "April"
elseif monthNum == 5 then month = "May"
elseif monthNum == 6 then month = "June"
elseif monthNum == 7 then month = "July"
elseif monthNum == 8 then month = "August"
elseif monthNum == 9 then month = "September"
elseif monthNum == 10 then month = "October"
elseif monthNum == 11 then month = "November"
elseif monthNum == 12 then month = "December"
end
monthText.text = month .. " " .. yearNum
print("Table elements before button deletion: " .. #buttonTable)
for i = #buttonTable, 1, -1 do
--[[if button[i] ~= nil then
table.remove(buttonTable)
button[i]:removeSelf()
button[i] = nil
end--]]
local child = table.remove(buttonTable)
if child ~= nil then
child:removeSelf()
child = nil
end
end
print("Table elements after button deletion: " .. #buttonTable)
next_button.alpha = 1
for i = 1, math.floor(numYears * 365.25) do
dateTable[i] = calendar.getInfo(today) --calculate the next day's date
if dateTable[i].year == yearNum and dateTable[i].month == monthNum then -- create a button if the date's year and month match the desired month
button[i] = ui.newButton{
default = "images/day.png",
over = "images/dayover.png",
text = dateTable[i].day,
size = 30,
font = native.systemFontBold,
textColor = {0, 0, 0, 255},
onEvent = addExpense_function,
offset = -35 }
if dateTable[i].wday == 1 then button[i].x = math.floor(col/2)
elseif dateTable[i].wday == 2 then button[i].x = (col * 1) + math.floor(col/2)
elseif dateTable[i].wday == 3 then button[i].x = (col * 2) + math.floor(col/2)
elseif dateTable[i].wday == 4 then button[i].x = (col * 3) + math.floor(col/2)
elseif dateTable[i].wday == 5 then button[i].x = (col * 4) + math.floor(col/2)
elseif dateTable[i].wday == 6 then button[i].x = (col * 5) + math.floor(col/2)
elseif dateTable[i].wday == 7 then button[i].x = (col * 6) + math.floor(col/2)
end
if dateTable[i].day == 1 then button[i].y = wDayBar.y + wDayBar.height/2 + math.floor(row/2)
elseif dateTable[i].wday == 1 then button[i].y = button[i-1].y + row
else button[i].y = button[i-1].y
end
end
today = dateTable[i]
table.insert(buttonTable, button[i])
--button[i].id = "button_" .. i
end
print("Table elements after button creation: " .. #buttonTable)
end
return true
end

The reason why the code in the question doesn't work (and putting the table.insert inside the if...then does work) is due to the way you're using the button table.
The loop that starts for i = 1, math.floor(numYears * 365.25) do is going to create indices from 1 to a few hundred/thousand (depending on numYears).
However, as you're using button[i]= for filling the table and you're only filling 30 or so at a time, what you're creating is a sparse table with a few non-nil entries somewhere in the middle of the table.
Now with table.insert(buttonTable, button[i]) outside the if..then what actually happens is that most of the "inserts" are inserting nil. First time around this actually works ok for 2 reasons:
inserting nil on an empty table does nothing nor does inserting nil at the end of a table
there are only a month's worth of non-nil entries in button so only a month's worth will be inserted into buttonTable
With button and buttonTable setup like this, the first part of the call to the previous or next functions works as expected and removeSelf will be called on the month's worth of buttons. However this doesn't actually remove the button from the button table.
So when you come to the numYears loop again, not only will you add your freshly created buttons to both button and buttonTable, you'll also still have the buttons that you've just called removeSelf on in button so these will be added to buttonTable again. Crucially though, they are just tables now, so when you call removeSelf on them the second time it barfs. I presume this is the cause of the error you saw.
Moving table.insert(buttonTable, button[i]) to the if...then will resolve this problem as it will only ever add freshly minted buttons to buttonTable that are then removed each time next or previous are called.
A word of caution though. The button table is a bit of a mess. After a bit of navigating around, it'll have a bunch of dead buttons (that can't be garbage collected unless you've declared the values as weak) plus a month of working buttons. If you were to try to do anything with it, the chances are that you'll get problems.
It's not clear from the code if you even need the button table. If it's not required elsewhere it would probably be safer to just hold the button references in buttonTable.

Aside from making a new table for months as well as column coordinates like Nicol suggested, I found that I needed to put the table.insert() command inside the if ... then structure. I'm not sure exactly why putting it immediately after the if ... then structure caused it to not work properly, but I'm guessing it's because the buttons are being created inside the structure.
local prev_function = function(event)
if event.phase == "release" then
--set date to start from
today.year = 2012
today.month = 2
today.day = 29
today.wday = 4
--determine new month number
if monthNum == 1 then
monthNum = 12
yearNum = yearNum - 1
elseif monthNum ~= 1 then
monthNum = monthNum - 1
end
--set month string
local month = monthTable[monthNum]
monthText.text = month .. " " .. yearNum
print("Table elements before button deletion: " .. #buttonTable)
--remove all elements in buttonTable
for i = #buttonTable, 1, -1 do
local child = table.remove(buttonTable)
if child ~= nil then
child:removeSelf()
child = nil
end
end
print("Table elements after button deletion: " .. #buttonTable)
next_button.alpha = 1
--hide prev_button if we get to the month after the month we started from
if monthNum == today.month + 1 and yearNum == today.year then
prev_button.alpha = 0
end
--generate dates for specified number of years
for i = 1, math.floor(numYears * 365.25) do
dateTable[i] = calendar.getInfo(today)
--create a button for each date inside desired month
if dateTable[i].year == yearNum and dateTable[i].month == monthNum then
button[i] = ui.newButton{
default = "images/day.png",
over = "images/dayover.png",
text = dateTable[i].day,
size = 30,
font = native.systemFontBold,
textColor = {0, 0, 0, 255},
onEvent = addExpense_function,
offset = -35 }
--set x and y
button[i].x = colTable[dateTable[i].wday]
if dateTable[i].day == 1 then button[i].y = wDayBar.y + wDayBar.height/2 + math.floor(row/2)
elseif dateTable[i].wday == 1 then button[i].y = button[i-1].y + row
else button[i].y = button[i-1].y
end
table.insert(buttonTable, button[i])
end
--change 'today' for next iteration in the for loop
today = dateTable[i]
end
print("Table elements after button creation: " .. #buttonTable)
end
return true
end

Related

how to create multi line chart with single datatable in vb.net

I am working on vb.net application where i have to create a multi line chart. The table coming from the database using storing procedure is:-
TagName On Off Trip
P1 0 0 1
P2 0 1 1
P3 0 1 0
Q1 0 1 0
Q2 1 0 1
Q3 2 2 2
W1 4 2 1
W2 2 0 1
W3 1 1 0
W4 0 1 1
W5 2 1 1
And the code in vb.net i used to bind the chart named "chTrend" is:-
ds = ObjTags.GetTrendData()
If (ds.Tables(0).Rows.Count > 0) Then
dt = ds.Tables(0)
chTrend.DataSource = dt
chTrend.Series(0).XValueMember = "TagName"
chTrend.Series(0).YValueMembers = "On"
chTrend.Series(1).XValueMember = "TagName"
chTrend.Series(1).YValueMembers = "Off"
chTrend.Series(1).XValueMember = "TagName"
chTrend.Series(1).YValueMembers = "Trip"
For i = 0 To 2
chTrend.Series(i).ChartType = SeriesChartType.Line
chTrend.Series(i).IsVisibleInLegend = True
chTrend.Series(i).IsValueShownAsLabel = True
chTrend.Series(i).ToolTip = "Data Point Y Value #VALY{G}"
chTrend.Series(i).BorderWidth = 3
Next
End If
When i run the program a Error is coming as following
"Index was out of range. Must be non-negative and less than the size of the collection.Parameter name: index"
At the position
chTrend.Series(0).XValueMember = "TagName"
how to solve this?
Assuming that your DataTable only has the four DataColumns {TagName, On. Off. Trip}, then replace everything in your shown code below the line:
chTrend.DataSource = dt
with this.
chTrend.Series.Clear() ' clears any existing series
Dim s As Series
For columnIndex As Int32 = 1 To dt.Columns.Count - 1
Dim name As String = dt.Columns(columnIndex).ColumnName
s = chTrend.Series.Add(name)
s.XValueMember = dt.Columns(0).ColumnName
s.YValueMembers = name
s.ChartType = SeriesChartType.Line
s.IsVisibleInLegend = True
s.IsValueShownAsLabel = True
s.ToolTip = "Data Point Y Value #VALY{G}"
s.BorderWidth = 3
Next
chTrend.DataBind() ' loads the data from dt to the chart
The Chart databinding is a not a binding in the normal sense of binding. You need to tell the chart to copy the data from the source so that it has something to plot. That is what the DataBind method does.

Using two array's in one application with progressing the arrays

I have the below code I'm trying to put together and I'm running into a Run-time error '9' subscript out of range. This does work through the first run through then errors. I don't see why it won't allow for the string to go forward. From what I'm reading it should go through the application changing the X values with Y value 1 and when completed with that set to go to the next Y and start the whole process again until the end of Y. Any help would be appreciated.
Dim Cat(1 To 10) As String
Cat(1) = "010" 'SD
Cat(2) = "020" 'FD
Cat(3) = "050" 'WVID
Cat(4) = "040" 'VID
Cat(5) = "030" 'MEM
Cat(6) = "080" 'ACC
Cat(7) = "060" 'HDMI
Cat(8) = "070" 'SSD
Cat(9) = "090" 'POWER
Cat(10) = "990" 'ZRM
Dim Month(1 To 12) As String
Month(1) = "January"
Month(2) = "February"
Month(3) = "March"
Month(4) = "April"
Month(5) = "May"
Month(6) = "June"
Month(7) = "July"
Month(8) = "August"
Month(9) = "September"
Month(10) = "October"
Month(11) = "November"
Month(12) = "December"
For Y = 1 To UBound(Cat)
For X = 1 To UBound(Month)
Month(X) = Application.WorksheetFunction.SumIf(Sheets(Month(X)).Columns("AO"), Cat(Y), Sheets(Month(X)).Columns("AG"))
Next X
Cells(3 + Y, 41).Value = Application.WorksheetFunction.Sum(Month(1), Month(2), Month(3), Month(4), Month(5), Month(6), Month(7), Month(8), Month(9), Month(10), Month(11), Month(12))
Next Y
End Sub
On the first run through the loop indexed by X you are computing a conditional sum of data stored in Sheet "January". And then overwriting "January" with that value in Month(X). Let's say that that value is 42. On your next run through the loop 42 is in Month(X) so you are looking for Sheets(42), which probably isn't a valid worksheet. I would try this instead.
dim temp as double
For Y = 1 To UBound(Cat)
temp = 0# '0 as a double.
For X = 1 To UBound(Month)
temp = temp + Application.WorksheetFunction.SumIf(Sheets(Month(X)).Columns("AO"), Cat(Y), Sheets(Month(X)).Columns("AG"))
Next X
Cells(3 + Y, 41).Value = temp
Next Y
This way we don't need to store all of the sums from each sheet, since we only use them to add them all together.

daysBetween >= 23 HOURS means 1 more day and < 23 HOURS = the same day?

What is the rule to calculate daysBetween ??
Try a simple apex code and find something strange.
When two days are compare, if 23hours between them they are 1 day between. Why not 24 hours ?
The test code:
Datetime dt,dt0,dt1;
dt = Date.Today();
dt0 = dt.addHours(23);
dt0 = dt0.addMinutes(-1);
dt1 = dt.addHours(23);
dt1 = dt1.addMinutes(0);
Date d,d0,d1;
d=Date.ValueOf(dt);
d0=Date.ValueOf(dt0);
d1=Date.ValueOf(dt1);
System.debug(dt);
System.debug(d0+'|'+d.daysBetween(d0)); // 0
System.debug(d1+'|'+d.daysBetween(d1)); // 1

Crystal Reports error: A subscript must be between 1 and size of array

The following function returns the "A subscript must be between 1 and size of array"-error when run in Crystal Reports XI. Any idea why and how to fix it?
Function (optional BooleanVar start := true)
DateVar Array reportdates := [
cDate(2012, 10, 22),
cDate(2012, 11, 15),
cDate(2013, 01, 23),
cDate(2013, 02, 20),
// some more lines of dates...
cDate(2014, 01, 02)
];
// Here is some code that sorts the array just to be sure.
// Removed from question
// Find index of last reportdate not later than today
NumberVar stopIndex;
for i := 1 to UBound(reportdates) do (
if CurrentDate >= reportdates[i] then
stopIndex := i
);
DateTimeVar returnDateTime;
if start = true then ( // return start date
NumberVar startIndex;
if stopIndex = 1 then
startIndex = 1
else
startIndex = stopIndex - 1;
//*** The error occurs here
returnDateTime := cDateTime(reportdates[startIndex], cTime(0,0,0));
//*** The error occurs here
)
else ( // return stop date
DateVar stopDate = reportdates[stopIndex];
returnDateTime := dateAdd("d", -1, cDateTime(reportdates[stopIndex], cTime(23,59,59)));
);
returnDateTime;
Note:
I found that the above function returns an earlier stop date than start date if run before the second date in the array. I rewrote the function to counter that and then I did not have a situation that produced the error in question, but I would still be interested in why the error occured in this function and how to handle it.
NumberVar startIndex;
if stopIndex = 1 then
startIndex = 1
else
startIndex = stopIndex - 1;
should of course be
NumberVar startIndex;
if stopIndex = 1 then
startIndex := 1
else
startIndex := stopIndex - 1;
and now it works...
NumberVar stopIndex;
should also be changed to
NumberVar stopIndex := 1;
to avoid errors if report is run before first report date.

how can i use checkboxes which gets value from database in a loop?

I have a table in database.In that table , there is an unknown number of rows.I want to put
checkboxes but number of checkboxes should be equal to the Number of rows. Thus i tried to do it in while loop. I found number of rows which exists in table.It's rowNumber.Then , i did a while loop.At this point , there is a serial failure. Because when i clicked any checkbox , the row below is called with last m value.
cbox[m] = checkBox.new(checkX, txtY, sendRows[m], col,
Because after exiting while loop , m equals rowNumber. And when any checkboxes is clicked , it doesn't know which row is clicked because m = rowNumber. I tried many things but i can't do it. What should i do ?? Should i give up using loop? IF i give up using it how can do it?
Here is the code which i can't fixed
local m = 1
while(m<rowNumber)
cbox[m] = checkBox.new(checkX, txtY, sendRows[m], col,
function(this)
local state = this:getCheck()
if state then
print(m .. " checked")
gonder[m] = 1
else
print(m .. " does not checked")
gonder[m] = 0
end
end
)
screen:addChild(cbox[m])
cbox[m]:setCheck(settings.getMusicState())
txtY = txtY + gap
print ("gonder[m] = " .. gonder[m])
end
If the code is written as in the below,it runs correctly:
cbox[1] = checkBox.new(checkX, txtY, sendRows[1], col,
function(this)
local state = this:getCheck()
if state then
print(1.. " checked")
gonder[1] = 1
else
print(1 .. " does not checked")
gonder[1] = 0
end
end
)
screen:addChild(cbox[1])
cbox[1]:setCheck(settings.getMusicState())
txtY = txtY + gap
cbox[2] = checkBox.new(checkX, txtY, sendRows[2], col,
function(this)
local state = this:getCheck()
if state then
print(2.. " checked")
gonder[2] = 1
else
print(2 .. " does not checked")
gonder[2] = 0
end
end
)
screen:addChild(cbox[2])
cbox[2]:setCheck(settings.getMusicState())
txtY = txtY + gap
.
.
.
.
It is solved after changing the inside of while as this:
while( m<rowNumber + 1)
do
cbox[m] = checkBox.new(checkX, txtY, sendRows[m], col,
function(this)
end
)
screen:addChild(cbox[m])
txtY = txtY + gap
m = m + 1
end
And the important point is here. Here , we are sending what we want:
submitt:addEventListener("click",
function()
local mydata={}
local i=1
while (i<rowNumber+1) do
mydata[3*i-2]=cbox[i].checked--1. Holds whether it is true or false
mydata[3*i-1]=sendRows2[i]
mydata[3*i] = sendURL[i]
i=i+1
end
sceneManager:changeScene("results", 1, SceneManager.flipWithFade, easing.outBack,mydata)
stage:removeChild(submitt)--after clicked , the button disappears
end)

Resources