Excel Forum - Porady, Pomoc,  Excel Help, Excel FAQ Strona Główna
 FAQ  RegulaminRegulamin  Szukaj   Użytkownicy   Grupy   Rejestracja   Profil   Twoje wiadomości   Zaloguj 


Poprzedni temat «» Następny temat
ID tematu: 49393 Skopiuj do schowka ProgressBar w długim makrze - konsultacja
Autor Wiadomość
Miverd 
Exceloholic


Posty: 101
Wysłany: 22-11-2015, 11:53   ProgressBar w długim makrze - konsultacja

Cześć,
Znalazłem wiele tematów odnośnie dodawania ProgressBara do różnego rodzaju makr. Wyczytałem, że najprostrzym rozwiązaniem jest stworzenie progressbara na głównej pętli w makrze, która zajmuje najwięcej czasu, ew podzielić go na mniejsze części.

Mój kod ma mniej więcej taką strukturę:
Cytat:
1. krótka pętla dopisująca dostępne drukarki do listy
2. Warunek IF - jeśli A to:
a) warunek if - jeśli a1 to
- mnóstwo drobnych ifów i pętli zbierających dane
b) warunek if - jeśli a2 to
- mnóstwo drobnych ifów i pętli zbierających dane
c) warunek if - jeśli a3 to
- mnóstwo drobnych ifów i pętli zbierających dane
3. Warunek IF - jeśli B to:
a) warunek if - jeśli b1 to
- mnóstwo drobnych ifów i pętli zbierających dane
b) warunek if - jeśli b2 to
- mnóstwo drobnych ifów i pętli zbierających dane
c) warunek if - jeśli b3 to
- mnóstwo drobnych ifów i pętli zbierających dane
4. Warunek If - jeśli żadna z powyższych to wyrzuć MsgBox


W związku z tym, chciałbym Was prosić o poradę, jak najlepiej to ugryźć - chodzi mi o koncepcję. Makro generuje raporty w odpowiednich arkuszach na podstawie danych w trzech innych arkuszach, potem publikuje je w odpowiednich miejscach jako pdf'y a także po wyborze drukarki drukuje je. W razie potrzeby mogę wkleić całe makro - z tymże zajmuje ono obecnie 1900 linijek..

Myślałem, żeby dodać otwarcie progressbar na początku każdego podpunktu (a1, a2, a3, b1, b2, b3), każdy podpunkt wcisnąć sztucznie do pętli która wykona się raz, ale w jej środku zrobić zwiększanie się zmiennej pomocniczej np co 5 linijek kodu i po każdym dodaniu odświeżać otwarty userform z progressbarem. Nie jest to optymalne rozwiązanie, ale jak odświeżać userform, żeby pobierał zwiększoną zmienną pomocniczą i się aktualizował?

EDIT: Dodam, że progress bar mi działa, ale wklejony na początku makra otwiera się, przechodzi w wewnętrznej pętli z userforma z progressbar, a dopiero potem generują się raporty, już bez progressbar. Trzeba by to jakoś zamknąć ale nie mam pomysłu jak..

Załączam plik ze znalezionym w sieci pliczkiem z obsługą ProgressBar, ale widzę, że on też się wykonuje niezależnie od działania makra..

filtering_ via_userform.xls
Pobierz Plik ściągnięto 22 raz(y) 112.5 KB

ID posta: 276686 Skopiuj do schowka
 
 
Tajan


Pomógł: 4638 razy
Posty: 10257
Wysłany: 22-11-2015, 15:42   

Twój pomysł jest dobry. Możesz go wykorzystać. W przypadku progresbar istotna jest wartość przy której zostanie zakończony wizualizowany proces. Może to być konkretna wartość, taka jak liczba przetwarzanych wierszy, czy liczba arkuszy, ale może to być zupełnie abstrakcyjna liczba, do której będziemy dążyć w miarę zaawansowania procesu. Zatem, wcale nie musi to być pętla. Może to być liniowa procedura, w której zmienna pomocnicza, sterująca paskiem, będzie stopniowo zwiększana w miarę zaawansowania zadania, aby na jego końcu osiągnąć zakładana wartość maksymalną. Jednak, jak już zauważyłeś, istotnym problemem jest sprawa odświeżania paska. Robi się to różnie. W przypadku Twojego, przykładowego pliku, to aby kod działał poprawnie, konieczne jest uruchomienie procedury, której postęp będzie uwidoczniony na pasku, nie z właściwego UserForm4 lecz z procedury Main formularza ProgressDlg. Zatem, w UserForm4 uruchamiamy tylko ProgressDlg:
Kod:
Private Sub CommandButton1_Click()
Sheets("report").Select
If ListBox2.ListCount = 0 Then
   MsgBox "You don't choose filter field "
   Exit Sub
End If
ProgressDlg.Show 'Progress Bar
CommandButton6.Enabled = True
End Sub
a cały proces odbywa się dalej, juz w formularzu ProgressDlg:
Kod:
Sub Main()
Dim basliklar As Long, baslangic_satiri As Long, tot As Long
   
    ProgressDlg.Caption = "Prosessing data, please wait..."
   
   
    tot = UserForm4.ListBox2.ListCount
           
   
    For basliklar = 1 To tot
         baslangic_satiri = 2
         Sheets("report").Cells(baslangic_satiri - 1, basliklar) = _
                                UserForm4.ListBox2.List(basliklar - 1, 0)

         Sheets("database").Range("A1:O65536").AdvancedFilter _
         Action:=xlFilterCopy, _
         CriteriaRange:=Sheets("database").Range("A1:O65536"), _
         CopyToRange:=Sheets("report").Cells(baslangic_satiri - 1, basliklar), _
        Unique:=False
       
        ProgressBar basliklar / tot
       
        Application.Wait Time + TimeValue("00:00:01")
       
    Next
   
    Sheets("report").Columns.EntireColumn.AutoFit
   
   
    Unload ProgressDlg                                   
End Sub

Dodałem linę opóżniającą (Application.Wait), aby było widać działanie :-) Zazwyczaj jest ona zbędna.

filtering_ via_userform(tj).xls
Pobierz Plik ściągnięto 43 raz(y) 125 KB

ID posta: 276697 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 22-11-2015, 19:08   

Hmm, na wstępnie powiem, że plik który załączyłem znalazłęm na internecie i nie jest mojego autorstwa.

Co do reszty - czyli jak rozumiem swoje makro muszę przebudować w taki sposób, aby każdy większy moduł (a1, a2, a3, b1, b2, b3) były wrzucone do UserForma z ProgressBarem?
Jeśli tak, to w jaki sposób się odwoływać do każdej funkcji (w sensie np. wybrałem generowanie raportu z a1 i a3 -> jak odwołać się w makrze do odpowiedniego miejsca w UserFormie z PRogresBarem? Muszę bawić się zmiennymi globalnymi?
ID posta: 276720 Skopiuj do schowka
 
 
Tajan


Pomógł: 4638 razy
Posty: 10257
Wysłany: 22-11-2015, 19:46   

Nie musisz wszystkiego wrzucać do Userform. Chodzi tylko o to aby z otwartego formularza wywołać procedurę wykonującą dane czynności. Może być ona umieszczona w innym module.
Przykładowo, w Twoim kodzie wszystkie fragmenty, które określasz jako:
Cytat:
- mnóstwo drobnych ifów i pętli zbierających dane
należałoby zamknąć w odrębnych procedurach, zawierających linie sterowania paskiem postępu. W module formularza należałoby umieścić tylko instrukcję IF, wywołującą te procedury. Czyli, w jednej procedurze zamykasz:
Cytat:
a) warunek if - jeśli a1 to
- mnóstwo drobnych ifów i pętli zbierających dane
b) warunek if - jeśli a2 to
- mnóstwo drobnych ifów i pętli zbierających dane
c) warunek if - jeśli a3 to
- mnóstwo drobnych ifów i pętli zbierających dane

w drugiej:
Cytat:
a) warunek if - jeśli b1 to
- mnóstwo drobnych ifów i pętli zbierających dane
b) warunek if - jeśli b2 to
- mnóstwo drobnych ifów i pętli zbierających dane
c) warunek if - jeśli b3 to
- mnóstwo drobnych ifów i pętli zbierających dane
W tych procedurach odpowiednio sterujesz progressbarem ustawiając zmienną określająca postęp.
A w module formularza progresu tylko:
Kod:
1. krótka pętla dopisująca dostępne drukarki do listy
2. Warunek IF - jeśli A to:
Call Procedura1
3. Warunek IF - jeśli B to:
Call Procedura2
4. Warunek If - jeśli żadna z powyższych to wyrzuć MsgBox

A procedura uruchamiająca to wszystko, to jedna linia, np:
Kod:
ProgressDlg.Show
ID posta: 276723 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 22-11-2015, 20:11   

Jeśli dobrze rozumiem to:
Główne makro:

Cytat:
1. krótka pętla dopisująca dostępne drukarki do listy
2. Warunek IF - jeśli A to:
zmienna_pomocnicza = 1
Call Procedura1 (ProgressBar.Show)
3. Warunek IF - jeśli B to:
zmienna_pomocnicza = 2
Call Procedura2 (ProgressBar.Show)
4. Warunek If - jeśli żadna z powyższych to wyrzuć MsgBox


ProgressBar.Show:
Cytat:
Dim licznik as Integer, status as integer
licznik = 0
status = 0

If zmienna_pomocnicza = 1 Then:
a) warunek if - jeśli a1 to
licznik = licznik + 1
b) warunek if - jeśli a2 to
licznik = licznik + 1
c) warunek if - jeśli a3 to
licznik = licznik + 1

a) warunek if - jeśli a1 to
- mnóstwo drobnych ifów i pętli zbierających dane
Status = 1 / licznik * 100%
b) warunek if - jeśli a2 to
- mnóstwo drobnych ifów i pętli zbierających dane
Status = Status+1 / licznik * 100%
c) warunek if - jeśli a3 to
- mnóstwo drobnych ifów i pętli zbierających dane
Status = Status+1 / licznik * 100%
Else
If zmienna_pomocnicza = 2 Then
a) warunek if - jeśli b1 to
licznik = licznik + 1
b) warunek if - jeśli b2 to
licznik = licznik + 1
c) warunek if - jeśli b3 to
licznik = licznik + 1

a) warunek if - jeśli b1 to
- mnóstwo drobnych ifów i pętli zbierających dane
Status = 1 / licznik * 100%
b) warunek if - jeśli b2 to
- mnóstwo drobnych ifów i pętli zbierających dane
Status = Status+1 / licznik * 100%
c) warunek if - jeśli b3 to
- mnóstwo drobnych ifów i pętli zbierających dane
Status = Status+1 / licznik * 100%
ID posta: 276727 Skopiuj do schowka
 
 
Tajan


Pomógł: 4638 razy
Posty: 10257
Wysłany: 22-11-2015, 20:50   

Cytat:
Call Procedura1 (ProgressBar.Show)

Nie. ProgressBar.Show wcześniej. To polecenie uruchamia cały proces. Bazując na poprzednim przykładzie:
- uruchamiasz ProgressDlg
- automatycznie uruchamia się jego procedura Main zawierający procedurę z IF
-wykonuje się kod z IF
- kod z IF wywołuje procedurę zawierającą mnóstwo drobnych ifów i pętli zbierających dane
- procedura zawierająca mnóstwo drobnych ifów i pętli zbierających dane steruje paskiem postępu
-wychodzimy z procedury zawierającej mnóstwo drobnych ifów i pętli zbierających dane gdy postęp wyniesie 100%
- automatycznie zamyka się ProgressDlg
ID posta: 276729 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 22-11-2015, 20:59   

Ok, zrozumiałem. Biorę się za wdrażanie (usiądę do tego jutro) i wrócę z wynikami moich wypocin :)
ID posta: 276733 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 23-11-2015, 08:41   

Hmm.. siedzę na tym już 2 godziny i dochodzę do wniosku, że przy moim makrze nie da się tego zrobić bez miliarda zmiennych pomocniczych zadeklarowanych globalnie. Do tego, przykład pierwszego Ifa w moim makrze - jeżeli brak wybranej wartości w ComboBox to przerwij działanie makra. Dla zobrazowania, wklejam makro:
Kod:
tu był kod :)


Edit: Teraz wpadłem na pomysł, żeby rozwiązać to tak:
Cytat:
1) Uruchamian generator raportów (osobny user form z OptionButton i CheckButton)
2) Jeśli A to Call Main()
Call Main:
jeśli a1 to Call Main 1a
jeśli a2 to Call Main 1b
Jeśli a3 to Call Main 1c
3) Jeśli B to Call Main1()
Call Main:
jeśli b1 to Call Main1 2a
jeśli b2 to Call Main1 2b
Jeśli b3 to Call Main1 2c


Ale wtedy to makro musze rozbić na przynajmniej 8 mniejszych funkcji..?
ID posta: 276750 Skopiuj do schowka
 
 
Tajan


Pomógł: 4638 razy
Posty: 10257
Wysłany: 23-11-2015, 09:58   

No cóż. Makro jest napisane niezbyt dobrze. Niepotrzebnie ująłeś wszystko w jednej procedurze. To należałoby rozbić na kilka (kilkanaście) procedur lub funkcji, odpowiadającym za wykonanie poszczególnych zadań. W tej chwili jest to "groch z kapustą", gdzie w jednej procedurze masz pobieranie danych, tworzenie raportów, wydruki, zapisywanie plików. Jest to mało elastyczne i praktycznie nie do ogarnięcia "z boku". Ja to rozbił bym nie na 8, ale może i na 20 procedur, z których może udało by się stworzyć w miarę przejrzysty, łatwy do modyfikacji kod.
ID posta: 276753 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 23-11-2015, 10:09   

Pomimo tego bałaganu, mam mały pomysł jak to rozpisać - spróbuję, najwyżej sie przejadę.

Ewentualnie, czy możesz mi pomóc z takim rozbiciem? Albo chociaż pomóc z rozpoczęciem? Nie wiem jak "podlinkować" wszystko ze sobą tak, żeby nie utraciło swojej funkcjonalności.
ID posta: 276754 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 23-11-2015, 13:17   

Udało mi się zrobić :) Podzieliłem sobie makro na kilka mniejszych i wszystko bardzo fajnie działa :)

ProgressBar.Show:
Kod:
Sub Main()
    Dim progres As Double, krok As Double
    progres = 0
    ProgressBar progres / tot
   
    If glowna1 = True Then
        If kolejna1 = True Then
            Call Main1a
        End If
            progres = 1
            ProgressBar progres / tot
        If kolejna2 = True Then
            Call Main1b
        End If
            progres = 2
            ProgressBar progres / tot
        If kolejna3 = True Then
            Call Main1c
        End If
            progres = 3
            ProgressBar progres / tot
    End If
   
    If glowna2 = True Then
        If kolejna4 = True Then
            Call Main2a
        End If
            progres = 1
            ProgressBar progres / tot
        If kolejna5 = True Then
            Call Main2b
        End If
            progres = 2
            ProgressBar progres / tot
        If kolejna6 = True Then
            Call Main2c
        End If
            progres = 3
            ProgressBar progres / tot
    End If
    Application.Wait Time + TimeValue("00:00:01")
     
    Unload ProgressDlg
End Sub


A w samym przycisku uruchamiającym progress bar:

Kod:

Private Sub RGenerowanieCB_Click()

Dim unikatwsh As Object
Dim drukarki As Variant
Set unikatwsh = CreateObject("WScript.Network")
Set drukarki = unikatwsh.EnumPrinterConnections
 
Load RListaDrukarek
                   
For i = 1 To drukarki.Count Step 2
 RListaDrukarek.GListaCB.AddItem drukarki.Item(i)
Next

drukarkasave = "brak"

glowna1 = False
glowna2 = False
kolejna1 = False
kolejna2 = False
kolejna3 = False
kolejna4 = False
kolejna5 = False
kolejna6 = False
tot = 0

'------wybranie zakresu raportu: ODDZIAŁ------

If ROddzialOB.Value = True Then
    glowna1 = True
   
'------wybranie raportu z DANE TECHNICZNE------

    If RDaneTechniczneCB.Value = True Then
       
        kolejna1 = True
        tot = tot + 1
       
        If ROddzialCB.Value = "" Then
            MsgBox "Musisz wybrać oddział, którego ma dotyczyć raport!", vbCritical, Blad
            Exit Sub
        Else
            no1 = ROddzialCB.Value
        End If
    End If

'------wybranie raportu z KOSZTY NAPRAW------

    If RKosztyCB.Value = True Then
        kolejna2 = True
        tot = tot + 1
        If ROddzialCB.Value = "" Then
            MsgBox "Musisz wybrać oddział, którego ma dotyczyć raport!", vbCritical, Blad
            Exit Sub
        Else
            no2 = ROddzialCB.Value
        End If
    End If

    '------wybranie raportu z SERWIS WÓZKÓW------

    If RSerwisCB.Value = True Then
        kolejna3 = True
        tot = tot + 1
        If ROddzialCB.Value = "" Then
            MsgBox "Musisz wybrać oddział, którego ma dotyczyć raport!", vbCritical, Blad
            Exit Sub
        Else
            no3 = ROddzialCB.Value
        End If
    End If

'------wybranie zakresu raportu: POLSKA------

Else

If RPolskaOB.Value = True Then
    glowna2 = True

    '------wybranie raportu z DANE TECHNICZNIE------

    If RDaneTechniczneCB.Value = True Then
        kolejna4 = True
        tot = tot + 1
    End If

'------wybranie raportu z KOSZTY NAPRAW------

    If RKosztyCB.Value = True Then
        kolejna5 = True
        tot = tot + 1
    End If
   
'------wybranie raportu z SERWIS WÓZKÓW------

    If RSerwisCB.Value = True Then
       kolejna6 = True
       tot = tot + 1
    End If

End If
End If
MsgBox tot
ProgressDlg.Show
Unload Me
End Sub


Do tego wewnątrz każdej podfunkcji zrobiłem mniejsze przeliczniki co 0.05 i ProgressBar progres / tot.

Bardzo dziękuję za wskazówki.
ID posta: 276766 Skopiuj do schowka
 
 
Tajan


Pomógł: 4638 razy
Posty: 10257
Wysłany: 23-11-2015, 15:26   

No, to super :-D
Przyznasz chyba, że teraz wygląda to dużo lepiej. Grunt, to się nie poddawać a wszystko da się zrobić!
ID posta: 276778 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 24-11-2015, 08:38   

Da się, ale czasami wiedza za mała :)
Mam jeszcze dwa opcjonalne pytania (nie chce specjalnie zakładac nowego wątku):
1) raporty generują się w osobnych arkuszach. Docelowo chciałem je ukryć, żeby generowały się bez widoku dla użytkownika ale mam wtedy błąd. Czy da się to jakoś obejść, żeby te arkusze nie były widoczne?
2) Podczas generowania raportów na środku ekranu mam progress bar. Jednak w tle wszystko strasznie skacze, widać jak uzupełniają się dane, przeskakują komórki i arkusze. Można to jakoś ukryć?
ID posta: 276823 Skopiuj do schowka
 
 
Tajan


Pomógł: 4638 razy
Posty: 10257
Wysłany: 24-11-2015, 09:43   

W sumie na oba problemy jest jedno rozwiązanie - wyłączenie odświeżania ekranu na czas działania makra.
Możesz to np. zrobić w procedurze "Main"
Kod:
Sub Main()
    Dim progres As Double, krok As Double
    progres = 0
    ProgressBar progres / tot
   
    'wyłączenie odswieżania
    Application.ScreenUpdating = False
   
    '.... kod bez zmian
   
  'przywrócenie odswieżania
  Application.ScreenUpdating = True   
    Unload ProgressDlg
End Sub


Jeżeli odświeżanie bedzie wyłączone będziesz mógł odkryc arkusze raportów, wykonać to co potrzeba, a następnie je ukryć.
ID posta: 276828 Skopiuj do schowka
 
 
Miverd 
Exceloholic


Posty: 101
Wysłany: 24-11-2015, 10:49   

Idealnie działa, dziękuję :)
ID posta: 276839 Skopiuj do schowka
 
 
Wyświetl posty z ostatnich:   
Odpowiedz do tematu
Nie możesz pisać nowych tematów
Nie możesz odpowiadać w tematach
Nie możesz zmieniać swoich postów
Nie możesz usuwać swoich postów
Nie możesz głosować w ankietach
Nie możesz załączać plików na tym forum
Możesz ściągać załączniki na tym forum
Dodaj temat do Ulubionych
Wersja do druku

Skocz do:  

Powered by phpBB modified by Przemo © 2003 phpBB Group
Theme xandgreen created by spleen& Programosy modified v0.3 by warna
Opieka techniczna www.marketingNET.pl

Archiwum

Strona używa plików cookies.

Kliknij tutaj, żeby dowiedzieć się jaki jest cel używania cookies oraz jak zmienić ustawienia cookie w przeglądarce.
Korzystając ze strony użytkownik wyraża zgodę na używanie plików cookies, zgodnie z bieżącymi ustawieniami przeglądarki.
Sprawdź, w jaki sposób przetwarzamy dane osobowe