Jump to content

How to cancel filecopy process with Button?


Recommended Posts

Hello Autoit Geeks,

I need a possibility to cancel an active filecopy prosess with a button in the GUI!

Details:

I coded a GUI were the User can select some Softwarepackages! These packages will be downloaded to the local drive. Due to the fact, that the packages are really big, I want to give the User a Button, with which he can cancel/stop the download. Everything I've tried went wrong. My problem is, that the GUI doesn't accept any input during an active filecopy action. This means, even if I click the "X" to close the whole application, the program will run until the function ProgressCopy is finished and then it will be closed.

Here the code of my ProgressCopy Function (with progressbars):

Func ProgressCopy($current, $destination, $attrib = "-R", $overwrite = 1 ,$Run1 = 0 )
;FirstTimeRun Get original DirSize and set up Gui
    If $Run1 = 0 Then
        Global $OverallQty, $Overall, $source, $overallpercent, $Progress0Text, $progressbar1, $Progress1Text, $progressbar2, $Progress2Text,  $LocalPercent
        If not FileExists ($destination) then DirCreate ($destination)
        $source = $current
        If StringRight($current, 1) = '\' Then $current = StringTrimRight($current, 1)
        If StringRight($destination, 1) <> '\' Then $destination = $destination & "\"
        $tosearch = $current
        $Overall = DirGetSize($tosearch, 1)
        $OverallQty = $Overall[1]
        $Progress0Text = GUICtrlCreateLabel("Please Wait", 16, 190, 441, 20)
        $progressbar1 = GUICtrlCreateProgress(16, 205, 441, 20)
        GUICtrlSetColor(-1, 32250)
        $Progress1Text = GUICtrlCreateLabel("", 16, 227, 441, 20)
        $progressbar2 = GUICtrlCreateProgress(16, 242, 441, 20)
        $Progress2Text = GUICtrlCreateLabel("", 16, 262, 441, 20)
        GUICtrlSetData($Progress1Text, "Working Directory " & $tosearch)
        $Run1 = 1
    EndIf

    $Size = DirGetSize($current, 3)
    $Qty = $Size[1]
    Local $search = FileFindFirstFile($current & "\*.*")
    While 1
        Dim $file = FileFindNextFile($search)
        ; end loop if all files are done or if an error happens
        If @error Or StringLen($file) < 1 Then ExitLoop
        ; Check that the next file to copy is no directory
        If Not StringInStr(FileGetAttrib($current & "\" & $file), "D") And ($file <> "." Or $file <> "..") Then
            ; Set values for GUI Labels
            $Qty -= 1
            $LocalPercent = 100 - (($Qty / $Size[1]) * 100)
            $OverallQty -= 1
            $overallpercent = 100 - (($OverallQty / $Overall[1]) * 100)
            ; set GUI Label content
            GUICtrlSetData($Progress0Text, "Download " & Int($overallpercent) & "% completed")
            GUICtrlSetData($progressbar1, $overallpercent)
            GUICtrlSetData($progressbar2, $LocalPercent)
            GUICtrlSetData($Progress2Text, "Copying File " & $file)
            ; Copy only if the source files modified date is different from the destination file (if already exists)
            $SourceFile = $current & "\" & $file
            $DestinationFile = $destination & StringTrimLeft($current, StringLen($source)) & "\" & $file
            If FileGetTime($SourceFile,0,1) <> FileGetTime($DestinationFile,0,1) Then
                FileCopy($current & "\" & $file, $destination & StringTrimLeft($current, StringLen($source)) & "\" & $file,$overwrite)
                FileSetAttrib($destination & StringTrimLeft($current, StringLen($source)) & "\" & $file, $attrib)
            EndIf
        EndIf
        ; If next file is a directory, create it and call the copy function again for this directory
        If StringInStr(FileGetAttrib($current & "\" & $file), "D") And ($file <> "." Or $file <> "..") Then
            DirCreate($destination & StringTrimLeft($current, StringLen($source)) & "\" & $file)
            FileSetAttrib($destination & StringTrimLeft($current, StringLen($source)) & "\" & $file, $attrib)
            GUICtrlSetData($Progress1Text, $current & "\" & $file)
            ProgressCopy($current & "\" & $file, $destination, $attrib, $overwrite,1)
        EndIf
    WEnd
    FileClose($search)
; when overall percent = 100, set end gui text, delete progressbars and reset run1 to 0
    If $overallpercent = 100 Then
        GUICtrlSetData($Progress0Text, "Package download 100% completed")
        GUICtrlSetData($progressbar1, 100)
        GUICtrlSetData($progressbar2, 100)
        GUICtrlSetData($Progress2Text, "Done!")
        Sleep(2000)
        GUICtrlDelete($Progress0Text)
        GUICtrlDelete($progressbar1)
        GUICtrlDelete($Progress1Text)
        GUICtrlDelete($progressbar2)
        GUICtrlDelete($Progress2Text)
        $Run1 = 0
    EndIf
EndFunc

Any help would be appreciated!

By the way: The function "ProgressCopy" is not made by myself. I found it somewhere and adapted it to my needs!

Link to comment
Share on other sites

While the script is stuck inside the While/WEnd loop of your ProgressCopy(), no GuiGetMsg() is being done to handle inputs.

Instead, use GuiOnEventMode for your GUI. Any events from clicking "X" or a CANCEL button will interrupt the function.

:blink:

Edited by PsaltyDS
Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

While the script is stuck inside the While/WEnd loop of your ProgressCopy(), no GuiGetMsg() is being done to handle inputs.

Instead, use GuiOnEventMode for your GUI. Any events from clicking "X" or a CANCEL button will interrupt the function.

:blink:

I was also having a problem like this, and I was wondering: rather than re-code the logic of the app into OnEventMode, why couldn't you put the FileCopy stuff inside a function, and then within the function, call and check the state of GuiGetMsg? Or even (within the main loop) introduce a sub-loop

while 1

$msg = GuiGetMsg()

[check the state of $msg and quit if a button was pressed]

[do the file copy]

wend

I've tried something like this, but it still doesn't seem to work. Can anyone explain why?

Link to comment
Share on other sites

I was also having a problem like this, and I was wondering: rather than re-code the logic of the app into OnEventMode, why couldn't you put the FileCopy stuff inside a function, and then within the function, call and check the state of GuiGetMsg? Or even (within the main loop) introduce a sub-loop

while 1

$msg = GuiGetMsg()

[check the state of $msg and quit if a button was pressed]

[do the file copy]

wend

I've tried something like this, but it still doesn't seem to work. Can anyone explain why?

I've done exactely the same, because I was to lazy to recode everything with OnEventMode! ;-)

...but I came to the result as you...it won't work and now I understand why!

PsaltyDS is right. GuiGetMsg() seems to be stopped completely during while statements. Nevertheless it stores the last GuiGetMsg() in the variable and execute the corresponding action after all while statements are done. I proofed this with some tests I've made in the afternoon.

This means, there's no other chance then recoding, because with OnEventMode everything works perfect (also proven during the test)!

Thanks PsaltyDS

Edited by Lazegalli
Link to comment
Share on other sites

I think I must be doing something wrong, because it doesn't seem to me that switching to OnEvent mode solves (my) problem.

Here's a very simple app in this mode: The idea is that once the user clicks Start, we just increment an integer and display it. What I want to do is have the app react to either the Quit button or to the "X" on the app's title bar. As written, it doesn't seem to do this - so what am I doing wrong? (The app will exit when the counter exceeds 5, but that's just because I can't get it to react).

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include<ButtonConstants.au3>

Opt("GUIOnEventMode",1)

$init = 0
$button_wid = 50

$main=GUICreate("Test", 420, 220, -1, -1)

$st = GuiCtrlCreateButton("Start",150,180,$button_wid,-1)
GUICtrlSetOnEvent($st,"StartFn")

$st = GuiCtrlCreateButton("Quit",250,180,$button_wid,-1)
GUICtrlSetOnEvent($st,"QuitFn")

$labf=GUICtrlCreateLabel("Checking Library: ", 30, 70, 90)

$libname = GUICtrlCreateLabel(" ", 120, 70, 50)


GUISetOnEvent($GUI_EVENT_CLOSE,"QuitFn")
GUISetState(@SW_SHOW)

while 1
 sleep(1000)
wend


Func QuitFn()
exit
EndFunc

Func StartFn()
GUICtrlSetState($libname,$GUI_SHOW)
while 1
 $init = $init +1
 if $init > 5 then exit
 GUICtrlSetData($libname,$init)
 sleep(2500)
wend
EndFunc
Link to comment
Share on other sites

You never want to stay inside the event handler function that long. While one event is being handled, others are blocked. So when an event occurs handle it short and fast. Here, the start function just sets some global variables and returns to the main loop:

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include<ButtonConstants.au3>

Opt("GUIOnEventMode", 1)

Global $f_Run = False, $iTimer
Global $init = 0, $button_wid = 50, $libname

GUICreate("Test", 420, 220, -1, -1)
GUISetOnEvent($GUI_EVENT_CLOSE, "QuitFn")

GUICtrlCreateButton("Start", 150, 180, $button_wid, -1)
GUICtrlSetOnEvent(-1, "StartFn")

GUICtrlCreateButton("Quit", 250, 180, $button_wid, -1)
GUICtrlSetOnEvent(-1, "QuitFn")

GUICtrlCreateLabel("Checking Library: ", 30, 70, 90, 20)
$libname = GUICtrlCreateLabel(" ", 120, 70, 50, 20)

GUISetState(@SW_SHOW)

While 1
    Sleep(10)
    If $f_Run Then
        If TimerDiff($iTimer) >= 2500 Then
            $iTimer = TimerInit()
            $init = $init + 1
            GUICtrlSetData($libname, $init)
            If $init > 5 Then $f_Run = False
        EndIf
    EndIf
WEnd

Func QuitFn()
    Exit
EndFunc   ;==>QuitFn

Func StartFn()
    $f_Run = True
    $init = 0
    GUICtrlSetData($libname, $init)
    $iTimer = TimerInit()
EndFunc   ;==>StartFn

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Thank you very much! This is a big help to me.

(I experimented with calling the windows WaitMessage from user32.dll, which, as I understand it, is supposed to allow an app to process its message queue, and which should in principle allow the main window to become responsive again. I've been successful with this in other languages, but either I was using it incorrectly or else it has no effect in an AutoIt script).

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...