Skip to content

Commit

Permalink
Fix QPainter painting device concurrency issues [#44]
Browse files Browse the repository at this point in the history
  • Loading branch information
Annor-Gyimah committed Nov 13, 2024
1 parent 53f5223 commit 217010a
Show file tree
Hide file tree
Showing 8 changed files with 3,230 additions and 106 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ pyiconic.deb
Lin_Exec/main
Win_Exec
PyIconic-cache

PyIconic-MsiBuild-SetupFiles

25 changes: 12 additions & 13 deletions PyIconic.aip
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramFilesFolder" IsPseudoRoot="1"/>
<ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramMenuFolder" IsPseudoRoot="1"/>
<ROW Directory="PyIconic_Dir" Directory_Parent="LocalAppDataFolder" DefaultDir="PyIconic"/>
<ROW Directory="StartMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="STARTM~1|StartMenuFolder" IsPseudoRoot="1"/>
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.SideBySideGuidComponent">
Expand All @@ -63,22 +64,21 @@
<ROW Component="APPDIR" ComponentId="{11FFF809-9A63-4471-BF8D-358543C80796}" Directory_="APPDIR" Attributes="0"/>
<ROW Component="ProductInformation" ComponentId="{607838CC-7986-4875-A926-1483F399DAB6}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
<ROW Component="PyIconic" ComponentId="{485DDE86-000B-49A2-A50B-B5094966290B}" Directory_="PyIconic_Dir" Attributes="0"/>
<ROW Component="Trial.dll" ComponentId="{80834CDA-6137-4FD4-8A37-AE805C9A9D94}" Directory_="APPDIR" Attributes="0" KeyPath="Trial.dll"/>
<ROW Component="main.exe" ComponentId="{C1F9B644-8CC7-4EFE-9A3A-3CAE8CEB4517}" Directory_="APPDIR" Attributes="256" KeyPath="main.exe"/>
<ROW Component="Trial.dll" ComponentId="{80834CDA-6137-4FD4-8A37-AE805C9A9D94}" Directory_="APPDIR" Attributes="256" KeyPath="Trial.dll"/>
<ROW Component="main.exe" ComponentId="{EDC7B797-937F-42ED-B4EC-FB36FE7FB9F7}" Directory_="APPDIR" Attributes="256" KeyPath="main.exe"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
<ROW Feature="AI64BitFiles" Title="64-bit" Description="64-bit Executables and Libraries" Display="3" Level="1" Directory_="APPDIR" Attributes="0"/>
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0"/>
<ATTRIBUTE name="CurrentFeature" value="AI64BitFiles"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
<ROW File="Trial.dll" Component_="Trial.dll" FileName="Trial.dll" Attributes="0" SourcePath="&lt;AI_STUBS&gt;trialware.dll" SelfReg="false" DigSign="true"/>
<ROW File="Trial.dll" Component_="Trial.dll" FileName="Trial.dll" Attributes="0" SourcePath="&lt;AI_STUBS_64&gt;trialware.dll" SelfReg="false" DigSign="true"/>
<ROW File="main.exe" Component_="main.exe" FileName="main.exe" Attributes="0" SourcePath="Win_Exec\dist\main.exe" SelfReg="false" DigSign="true"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.custcomp.MsiShortcutPropertyComponent">
<ROW MsiShortcutProperty="PyIconic0" Shortcut_="PyIconic" PropertyKey="System.AppUserModel.PreventPinning" PropVariantValue="1"/>
<ROW MsiShortcutProperty="PyIconic_10" Shortcut_="PyIconic_1" PropertyKey="System.AppUserModel.PreventPinning" PropVariantValue="1"/>
<ROW MsiShortcutProperty="PyIconic_20" Shortcut_="PyIconic_2" PropertyKey="System.AppUserModel.PreventPinning" PropVariantValue="1"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BootstrapperUISequenceComponent">
<ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="249"/>
Expand Down Expand Up @@ -263,8 +263,8 @@
<ROW Feature_="MainFeature" Component_="AI_CustomARPName"/>
<ROW Feature_="MainFeature" Component_="PyIconic"/>
<ROW Feature_="MainFeature" Component_="AI_ExePath"/>
<ROW Feature_="AI64BitFiles" Component_="main.exe"/>
<ROW Feature_="AI64BitFiles" Component_="Trial.dll"/>
<ROW Feature_="AI64BitFiles" Component_="main.exe"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="icon.exe" SourcePath="Windows\icon.ico" Index="0"/>
Expand Down Expand Up @@ -363,9 +363,8 @@
<ROW Registry="Windows" Root="-1" Key="Software\Microsoft\Windows" Name="\"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
<ROW Shortcut="PyIconic" Directory_="ProgramMenuFolder" Name="PyIconic" Component_="main.exe" Target="[#main.exe]" Description="A multi-connection downloader" Hotkey="0" Icon_="icon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="PyIconic_1" Directory_="PyIconic_Dir" Name="PyIconic" Component_="main.exe" Target="[#main.exe]" Hotkey="0" Icon_="icon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="PyIconic_2" Directory_="DesktopFolder" Name="PyIconic" Component_="main.exe" Target="[#main.exe]" Hotkey="0" Icon_="icon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="PyIconic" Directory_="DesktopFolder" Name="PyIconic" Component_="main.exe" Target="[#main.exe]" Hotkey="0" Icon_="icon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="PyIconic_1" Directory_="StartMenuFolder" Name="PyIconic" Component_="main.exe" Target="[#main.exe]" Hotkey="0" Icon_="icon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiTextStyleComponent">
<ROW TextStyle="BrandingStyle" FaceName="Segoe UI" Size="8" Color="2171376" MultiBuildColor="DefaultBuild:2565927" StyleBits="0" MsiKey="BrandingStyle"/>
Expand All @@ -379,10 +378,10 @@
<ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.TrialwareComponent">
<ROW TrialwareName="Trial" DisplayName="Trial" Password="7DDD9D0F1C8FB745451D380D63DAF90120768807443D7488D22030188A709223CFDFCE5AB243B9C93954DB79FD190865862F019F21F95446BA4BBA860EB2C8AF408E77F11568635F7ABC7758A5D93D999320100A8D481EC2AABE072C009DF0F282D1763BE35CFC04E7EF856D240C68DF2789389AFC4D2BF07A8676DE65C9E068E3FEC74490B7D61E5A7D72FC206DC81BAB70985ACA072BE3" PrivateKey="44DDE90F1E8FCE45371D4A0D1ADAAF015076F8073F3D0188E6201718F4709923BADFCE5ACA43C8C94954D079F8190A65F72F709F57F92446CA4BCD867EB2BAAF538E13F12E684D5F1BBC0958D3D96799F2201C0A88486EC29ABE062C729D96F2D9D1023BAB5CE704A2EFDE6D520C3DDF2789259A894D59F0088637DE15C9966882FE9B44E3B7CC1E1E7D03FC0C6DB71B8B70FC5AA8075CE3D6549574F566E4DA9B055EBEF113188765085AD56DA6EA96480FEFB679223A00AE856925E3CC5B66DDA0C1A4602D4E3EBE102865D19226E58C2046FA8397C61D81977428FA1AE2BD2393B62089769B6B88B8534224189435C07316EB14A872DF6577631348973169D4F5EE548C2B4FE0260DD6EA9E28EAB05A10B2F7201FD22E679818CA1A350B43F4C9E04A4A8B7E4ED23E051C9CE7BF55DD2CF5B13F77916F2F114B5EFB0FD6EC6D5C91C9AEE3574EC5C8CCF198F9077C073C704A6FB9AE081C62F0E336B61D741134FE91306837673B6EB1B59E2A57A3F67A682C9D82F0AAD89F82AFFBF0EE5CBC58819D7D277FC69B9696D029FFF965CAF3E3566D39B00A9088B00F9C823387F167BB8D5942F6485473D6EE93C6DB10239FF9438C530350B2E0AFCFED15F5964B27BC957CA34F76CFAEE507136A8331B0457E8A35FE486DEB00367038CFF65E5B73EB8A9F2C738ED0A8AAEF518A3D0A995AD3C5D07009823E20192BCDF4A990AE3F5910F11414839FB3C66D1369121933D722D8DCAC0A52ED5443552AEBC64916B94E209B29C824C97CD3534AC42F2B76D7CF84E07AFE6FC206FAF136FB47DF41920DD6EFAC315C78FC2F75A624B8FAE0EBDED799CAB4EAC7F93851E871B9F29B700887AEE55BFE49E70365229178319C05C3540E4E" DllFile="Trial.dll" TrialwareCA="AI_Trial" IconIndex="0" Platform64="0"/>
<ROW TrialwareName="Trial" DisplayName="Trial" Password="08DDE90F6C8FB645331D320D6CDA88012576F807493D7888D52040188770E023BFDFC95AB243BFC94B54A3798C197265FB2F709F55F92F46BB4BCC860CB2BCAF418E7EF16E68125F7BBC7A58" PrivateKey="44DDE90F1E8FCE45371D4A0D1ADAAF015076F8073F3D0188E6201718F4709923BADFCE5ACA43C8C94954D079F8190A65F72F709F57F92446CA4BCD867EB2BAAF538E13F12E684D5F1BBC0958D3D96799F2201C0A88486EC29ABE062C729D96F2D9D1023BAB5CE704A2EFDE6D520C3DDF2789259A894D59F0088637DE15C9966882FE9B44E3B7CC1E1E7D03FC0C6DB71B8B70FC5AA8075CE3D6549574F566E4DA9B055EBEF113188765085AD56DA6EA96480FEFB679223A00AE856925E3CC5B66DDA0C1A4602D4E3EBE102865D19226E58C2046FA8397C61D81977428FA1AE2BD2393B62089769B6B88B8534224189435C07316EB14A872DF6577631348973169D4F5EE548C2B4FE0260DD6EA9E28EAB05A10B2F7201FD22E679818CA1A350B43F4C9E04A4A8B7E4ED23E051C9CE7BF55DD2CF5B13F77916F2F114B5EFB0FD6EC6D5C91C9AEE3574EC5C8CCF198F9077C073C704A6FB9AE081C62F0E336B61D741134FE91306837673B6EB1B59E2A57A3F67A682C9D82F0AAD89F82AFFBF0EE5CBC58819D7D277FC69B9696D029FFF965CAF3E3566D39B00A9088B00F9C823387F167BB8D5942F6485473D6EE93C6DB10239FF9438C530350B2E0AFCFED15F5964B27BC957CA34F76CFAEE507136A8331B0457E8A35FE486DEB00367038CFF65E5B73EB8A9F2C738ED0A8AAEF518A3D0A995AD3C5D07009823E20192BCDF4A990AE3F5910F11414839FB3C66D1369121933D722D8DCAC0A52ED5443552AEBC64916B94E209B29C824C97CD3534AC42F2B76D7CF84E07AFE6FC206FAF136FB47DF41920DD6EFAC315C78FC2F75A624B8FAE0EBDED799CAB4EAC7F93851E871B9F29B700887AEE55BFE49E70365229178319C05C3540E4E" DllFile="Trial.dll" TrialwareCA="AI_Trial" IconIndex="0" Platform64="1"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.TrialwareSettingsComponent">
<ROW Trialware="Trial" Section="Full" Key="LicenseValidationTemplate" Value="License ID: %ID%&#13;&#10;Signature: %SIGN%&#13;&#10;Maintenance Plan: %MP%&#13;&#10;Expiration Date: %EXP%"/>
<ROW Trialware="Trial" Section="Full" Key="LicenseValidationTemplate" Value="License ID: %ID%&#13;&#10;Signature: %SIGN%&#13;&#10;Expiration Date: %EXP%"/>
<ROW Trialware="Trial" Section="Full" Key="PubKey" Value="2EAAAAAaAABLqeLNA2AAAJAAIAC25BF3PUxjXFCoUMFHq1GPkLpXriDg8$@1FyG@Xe@XrEiNfSS6LHc$uWLFYxPi1kX"/>
<ROW Trialware="Trial" Section="Full" Key="RegPathStoreLoc" Value="Software\[|Manufacturer]\[|ProductName]"/>
<ROW Trialware="Trial" Section="Full" Key="RegRootStoreLoc" Value="2147483649"/>
Expand All @@ -395,9 +394,9 @@
<ROW Trialware="Trial" Section="Properties" Key="UseCompId" Value="true"/>
<ROW Trialware="Trial" Section="Properties" Key="UseMacForCompId" Value="true"/>
<ROW Trialware="Trial" Section="Trial" Key="DlgAppearFrequencyPercent" Value="100"/>
<ROW Trialware="Trial" Section="Trial" Key="ExtensionQuantity" Value="3"/>
<ROW Trialware="Trial" Section="Trial" Key="Period" Value="true"/>
<ROW Trialware="Trial" Section="Trial" Key="Quantity" Value="3"/>
<ROW Trialware="Trial" Section="Trial" Key="ExtensionQuantity" Value="4"/>
<ROW Trialware="Trial" Section="Trial" Key="NoOfUsages" Value="true"/>
<ROW Trialware="Trial" Section="Trial" Key="Quantity" Value="4"/>
<ROW Trialware="Trial" Section="Trial" Key="TrialId" Value="{165DDF95-8A8B-4C28-B35A-C643207ECE51}"/>
</COMPONENT>
</DOCUMENT>
Binary file modified Windows/Trial.dll
Binary file not shown.
20 changes: 19 additions & 1 deletion Windows/lic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,22 @@ Expiration Date: 11/11/2024

License ID: alpharian
Signature: zAB7UBIC17yRoVi8ikWhWDLtZYmzmMDeYQP2W61jeZERcl9lI2HBiO@TyqCbYqlT
Expiration Date: 11/12/2024
Expiration Date: 11/12/2024


License ID: gyimah
Signature: tjxXKTAYoepmh0@$uPk54cp4AxZKX9gdu55aszS1dkpd4raknf6vjDEP4aFuXjGQ
Expiration Date: 11/12/2024



QPainter::begin: A paint device can only be painted by one painter at a time.
QPainter::setCompositionMode: Painter not active
QPainter::fillRect: Painter not active
QWidget::repaint: Recursive repaint detected
QWidget::repaint: Recursive repaint detected
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
QPainter::begin: A paint device can only be painted by one painter at a time.
QPainter::setCompositionMode: Painter not active
QPainter::fillRect: Painter not active
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
111 changes: 106 additions & 5 deletions Windows/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from threading import Thread, Barrier, Timer, Lock
from collections import deque
import ctypes
from ctypes import c_uint, c_char_p, create_string_buffer
from modules.downloaditem import DownloadItem
from collections import deque

Expand Down Expand Up @@ -77,6 +78,66 @@
from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply



class TrialThread(QThread):
# Define a signal to send messages to the main thread
trial_completed = Signal(str)
trial_error = Signal(str)

def __init__(self, window_handle, library_key):
super().__init__()
self.window_handle = window_handle
self.library_key = library_key

def run(self):
try:
log("Debug Information-Product Starting")

base_path = os.path.dirname(os.path.abspath(__file__))
dll_path = os.path.join(base_path, "Trial.dll")

# Load the Trial DLL
trial_dll = ctypes.CDLL(dll_path)


# Define function signatures
init_trial_func = trial_dll.ReadSettingsStr
init_trial_func.argtypes = [c_char_p, ctypes.c_void_p]
init_trial_func.restype = c_uint

get_property_func = trial_dll.GetPropertyValue
get_property_func.argtypes = [c_char_p, ctypes.c_void_p, ctypes.POINTER(c_uint)]
get_property_func.restype = c_uint

# Initialize trial
result = init_trial_func(
self.library_key.encode('ascii'),
self.window_handle
)

# Read trial name property
buffer_size = c_uint(256)
trial_name = create_string_buffer(buffer_size.value)

result = get_property_func(
b"TrialName",
trial_name,
ctypes.byref(buffer_size)
)

if result == 234: # Need larger buffer
trial_name = create_string_buffer(buffer_size.value)
get_property_func(b"TrialName", trial_name, ctypes.byref(buffer_size))

# Emit the result to the main thread
self.trial_completed.emit(f"TrialName={trial_name.value.decode()}")

except OSError:
self.trial_error.emit("Trial.dll is missing. Application will close.")
except Exception as e:
self.trial_error.emit(str(e))


class YouTubeThread(QThread):
finished = Signal(object) # Signal to emit when the process is complete

Expand Down Expand Up @@ -282,6 +343,9 @@ def run(self):


class MainWindow(QMainWindow):

LIBRARY_KEY = "4E39E97F4A45B287D596C959EAA9DC45B88585"

update_gui_signal = Signal(dict)

def __init__(self, d_list):
Expand Down Expand Up @@ -336,6 +400,7 @@ def __init__(self, d_list):

# initial setup
self.setup()

##########################################################################################


Expand Down Expand Up @@ -531,16 +596,34 @@ def openCloseRightBox():
self.network_manager = QNetworkAccessManager()
self.network_manager.finished.connect(self.on_thumbnail_downloaded)
self.one_time = True


# Start trial initialization in a QThread
self.trial_thread = TrialThread(ctypes.c_void_p(int(self.winId())), self.LIBRARY_KEY)
self.trial_thread.trial_completed.connect(self.on_trial_completed)
self.trial_thread.trial_error.connect(self.on_trial_error)
self.trial_thread.start()




# def on_trial_completed(self, message):
# print(message)

# def on_trial_error(self, error_message):
# QMessageBox.critical(self, "Error", error_message)
# self.close()




def on_trial_completed(self, message):
self.show_information("Trial Completed", "", message)
#QMessageBox.information(self, "Trial Completed", message)

def on_trial_error(self, error_message):
self.show_critical("Error", error_message)
#QMessageBox.critical(self, "Error", error_message)
self.close()



Expand Down Expand Up @@ -679,7 +762,7 @@ def read_q(self):
# Update the QLineEdit with the new URL
widgets.home_link_lineEdit.setText(v)
self.url_text_change()
self.update_progress_bar()
#self.update_progress_bar()

elif k == "download":
self.start_download(*v)
Expand Down Expand Up @@ -766,7 +849,7 @@ def url_text_change(self):
self.url_timer = Timer(0.5, self.refresh_headers, args=[url])
self.url_timer.start()
# Trigger the progress bar update and GUI refresh
self.update_progress_bar()
#self.update_progress_bar()
except Exception as e:
log(f"Error in url_text_change: {e}")

Expand Down Expand Up @@ -1246,21 +1329,39 @@ def show_thumbnail(self, thumbnail=None):
except Exception as e:
log('show_thumbnail() error:', e)
self.reset_to_default_thumbnail()



def on_thumbnail_downloaded(self, reply):
if reply.error() == QNetworkReply.NoError:
data = reply.readAll()
image = QImage()
if image.loadFromData(data):
pixmap = QPixmap.fromImage(image)
widgets.home_video_thumbnail_label.setPixmap(pixmap.scaled(150, 150, Qt.KeepAspectRatio))
# Use QTimer to defer the setting of the pixmap
QTimer.singleShot(0, lambda: widgets.home_video_thumbnail_label.setPixmap(pixmap.scaled(150, 150, Qt.KeepAspectRatio)))
print("Successfully downloaded and set thumbnail")
else:
print("Failed to create image from downloaded data")
self.reset_to_default_thumbnail()
else:
print(f"Error downloading thumbnail: {reply.errorString()}")
self.reset_to_default_thumbnail()

# def on_thumbnail_downloaded(self, reply):
# if reply.error() == QNetworkReply.NoError:
# data = reply.readAll()
# image = QImage()
# if image.loadFromData(data):
# pixmap = QPixmap.fromImage(image)
# widgets.home_video_thumbnail_label.setPixmap(pixmap.scaled(150, 150, Qt.KeepAspectRatio))
# print("Successfully downloaded and set thumbnail")
# else:
# print("Failed to create image from downloaded data")
# self.reset_to_default_thumbnail()
# else:
# print(f"Error downloading thumbnail: {reply.errorString()}")
# self.reset_to_default_thumbnail()

def reset_to_default_thumbnail(self):
default_pixmap = QPixmap(":/icons/images/icons/thumbnail-default.png")
Expand Down Expand Up @@ -1418,7 +1519,7 @@ def playlist_OnChoice(self, selected_video):

# Optionally load the video thumbnail in a separate thread
if config.show_thumbnail:
Thread(target=self.video.get_thumbnail).start()
#Thread(target=self.video.get_thumbnail).start()

self.show_thumbnail(thumbnail=self.video.thumbnail_url)

Expand Down
Loading

0 comments on commit 217010a

Please sign in to comment.