Guten Morgen
Kennt sich jemand aus mit Python 2.7 und API Tingiverse?
Ich habe einen 3D Drucker welche ich einen direkten Zugriff auf Thingiverse.com hatte und die Objekte runterladen und gleich ausdrucken konnte.
Hier ein Video: https://youtu.be/9JNAUj34fR4
Funktioniert leider nicht mehr.
Ist ein Linux (Ubuntu) 3D Drucker mit Python 2.7 und Perl
Support gibt es leider nicht mehr für den Drucker.
Meine bisherige Diagnose:
Die Verbindung Thingiverse über API erfolgt und Benutzer wird akzeptiert. Nur die Objekt-Suche ist verkürzt und die gesuchten Bilder werden nicht angzeigt.
Wer mir evtl helfen kann werde ich mehr in Details gehen und alles Berichten was weiss und was ich ausprobiert habe …
Gruss Madi
3DDrucker mit Python und API Thingiverse
-
- User
- Beiträge: 492
- Registriert: Mittwoch 13. November 2019, 08:38
Da hilft es nur den Code zu sehen, um schauen zu können wo es klemmt. Die API ist ja hinreichend dokumentiert.
Danke für eure Antworten.
Dieser Drucker wurde eigentlich nicht für Private zwecke geschaffen, eher mehr für Krankenhäuser, Militär, Schulen und Firmen.
Daher leider wenig Opensource und Python alles in Endung .pvc . DANKE
Mir hat das Ding einfach gefallen, da man keinen Rechner braucht zum slicen, dass macht das Ding ja selbst.
Muss irgend einen Grund gewesen sein, dass Sie ende 2018 null Komma nichts aufgehört haben, ohne eine Nachricht an die Besitzer oder info auf der Website.
Der Drucker hat irgendwie potenzial, daher möchte ich es irgendwie weiterführen, entwickeln.
Komischerweise hat kürzlich die Suchfunktion bei Thingiverse und auch bei Myminifactory gleichzeitig aufgehört. Habe in dieser Zeit nichts daran herumgefummelt.
Könnte mit dem html parser beautifulsoup zu tun haben? Habe auch gelöscht und neu installiert.
Das Gerät erstellt auch Logs bei Arbeitsdurchläufe:
Gesucht habe das Objekt Case iPhone12
Leider wird nach diesen Meldungen nichts gefunden. Komischerweise aber auch keine Fehlermeldungen.
Hier die Pythons:
pSearch_front.py:
search_result.py
ThingiVendor.py
ThingiverseApi.py
Dieser Drucker wurde eigentlich nicht für Private zwecke geschaffen, eher mehr für Krankenhäuser, Militär, Schulen und Firmen.
Daher leider wenig Opensource und Python alles in Endung .pvc . DANKE
Mir hat das Ding einfach gefallen, da man keinen Rechner braucht zum slicen, dass macht das Ding ja selbst.
Muss irgend einen Grund gewesen sein, dass Sie ende 2018 null Komma nichts aufgehört haben, ohne eine Nachricht an die Besitzer oder info auf der Website.
Der Drucker hat irgendwie potenzial, daher möchte ich es irgendwie weiterführen, entwickeln.
Komischerweise hat kürzlich die Suchfunktion bei Thingiverse und auch bei Myminifactory gleichzeitig aufgehört. Habe in dieser Zeit nichts daran herumgefummelt.
Könnte mit dem html parser beautifulsoup zu tun haben? Habe auch gelöscht und neu installiert.
Das Gerät erstellt auch Logs bei Arbeitsdurchläufe:
Gesucht habe das Objekt Case iPhone12
Code: Alles auswählen
SearchLog:
===================== Search Log Start 1.15.07 =========================
[07-20-2021 13:33:18 INFO pSearch_front.py: 424 - searchProc() ] SEARCH start Case iPhone 12 | vendor Thingiverse
[07-20-2021 13:33:18 INFO pSearch_front.py: 533 - checkSearchCache() ] Found query cache /var/lib/AIO_Robotics/search/tmp/search_result_2021_07_20__13_06_38/data.json Case iPhone 12 Thingiverse
======
Code: Alles auswählen
data.json:
{"vendor": "Thingiverse", "label-results": [], "image-results": [], "search-term": "Case iPhone 12", "search-id": [], "total-results": 0}
Code: Alles auswählen
ClientLog:
[07-20-2021 13:33:18 INFO pSearch_front.py: 531 - checkSearchCache() ] Found query cache /var/lib/AIO_Robotics/search/tmp/search_result_2021_07_20__13_06_38/data.json Case iPhone 12 Thingiverse
[07-20-2021 13:33:18 INFO pSearch_front.py: 537 - checkSearchCache() ] Total Result 0
[07-20-2021 13:33:26 INFO pPrint_selection.py:1062 - runProc () ] Thumbnail Thread Stopped
Hier die Pythons:
pSearch_front.py:
Code: Alles auswählen
# uncompyle6 version 3.5.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.5 (default, Aug 7 2019, 00:51:29)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
# Embedded file name: aio_ui/pSearch_front.py
# Compiled at: 2017-03-08 13:19:59
import wx, aio, os, shutil, json, math
from datetime import datetime
from button_round import RoundButton
from aioVirtualKeyboard import AioVirtualKeyboard
from pGeneral_selection import PSearch_selection
from pSearch_preview import PSearch_preview
from search_result import SearchItem, SearchResults
from ThingiVendor import Thingiverse
from MMFVendor import MyMiniFactory
from search_threads import *
from aioDialog import AioDialog
from panel_combobox import PanelCombobox
MAX_DISPLAY_STRING_LEN = 40
CACHE_CLEAR_TIME = 2
class PSearch_front(wx.Panel):
def __init__(self, parent, pos, size, thumbnailThread=None):
wx.Panel.__init__(self, parent, wx.ID_ANY, pos, size)
self.parent = parent
self.sW = sW = parent.sW
self.sH = sH = parent.sH
self.pos = pos
self.size = size
self.thumbnailThread = thumbnailThread
self.SetBackgroundColour(aio.grey)
main_w = self.main_w = aio.main_width
main_h = self.main_h = aio.main_height
disp_w = aio.image_width
btn_w = 100
btn_top = (main_h - aio.image_height) / 2 + 7
center_w = main_w / 2
btn_col_w = (main_w - disp_w) / 2
self.preview = None
self.showPopup = False
self.searchInstruct = _('3D Object Search')
self._query = self.searchInstruct
self._totalResults = 0
self.resultSearchIDs = []
self.fileView = None
self.refreshFileView([], [])
self._searchThread = None
self._selectSearchThread = None
self._downloadThread = None
self.currentSearchCache = None
if not os.path.isdir(aio.SearchResultCacheFolder):
os.makedirs(aio.SearchResultCacheFolder)
tabH = 160
thumbSize = 280
pos_w = tabH + 4 * thumbSize + 80 - btn_w / 2
bSize = aio.scalePos(btn_w, btn_w)
img = aio.loadPilImg(aio.ROOT + 'img/backspace_w.png', 0.23)
bPos = aio.scalePos(pos_w, btn_top)
self.btnCancel = RoundButton(self, -1, '', bPos, bSize, type='image', image=img)
self.btnCancel.SetForegroundColour(aio.mediumdarkgrey2)
self.btnCancel.Bind(wx.EVT_LEFT_DOWN, self.OnCancel)
self.btnCancel.SetBackgroundColour(aio.grey)
bPos = (
bPos[0] - (btn_w + 10) * sW, bPos[1])
self.btnSearch = RoundButton(self, -1, '', bPos, bSize, type='search', longPressCallback=self.searchLongPressCallback)
self.btnSearch.SetFontToFitLabel()
self.btnSearch.Bind(wx.EVT_BUTTON, self.OnSearch)
self.btnSearch.SetBackgroundColour(aio.grey)
stxt_w = disp_w - 225
stxt_h = btn_w
stxt_pos_w = bPos[0] - (stxt_w + 10) * sW
stxt_pos_h = bPos[1]
spos = (stxt_pos_w, stxt_pos_h)
ssize = aio.scalePos(stxt_w, stxt_h)
self.searchText = wx.TextCtrl(self, -1, self.searchInstruct, spos, ssize)
font = wx.Font(40 * sW, wx.DECORATIVE, wx.ITALIC, wx.NORMAL)
self.searchText.SetFont(font)
self.searchText.Bind(wx.EVT_SET_FOCUS, self.OnType)
cpos_h = btn_top * sH
cpos_w = 25 * sW
c_w = stxt_pos_w - cpos_w - 10 * sW
c_h = btn_w * sH
self.repos = []
self.repos.append('Thingiverse')
self.repos.append('MyMiniFactory')
cpos = (cpos_w, cpos_h)
csize = (c_w, c_h)
self.vendorChoice = PanelCombobox(self, -1, cpos, csize, self.repos)
self.vendorChoice.SetFont(font)
self.vendorChoice.Bind(wx.EVT_CHOICE, self.OnChoice)
self.Bind(EVT_SEARCH, self.OnFinishSearch)
self.Bind(EVT_SELECT_SEARCH, self.OnFinishSelectSearch)
self.Bind(EVT_CONTINUE_SEARCH, self.OnFinishContinueSearch)
self.Bind(EVT_INFO, self.OnInfo)
self._thingObj = Thingiverse()
self._mmfObj = MyMiniFactory()
self._searchResults = SearchResults(None)
self.cleanResultsFolder()
return
def OnChoice(self, event):
try:
self.choiceProc(event)
except Exception as e:
aioClientLog.exception(e)
def choiceProc(self, event):
if self.vendorChoice.GetStringSelection() == 'Thingiverse':
text = _('Note that not all\nThingiverse files are printable')
dial = AioDialog(self, text, self.pos, self.size, doNotShowAgainTag='ThingiverseWarning')
def getSearchObject(self):
searchObj = self._thingObj
if self.vendorChoice.GetStringSelection() == 'MyMiniFactory':
searchObj = self._mmfObj
return searchObj
def setupSearchObject(self):
query = self.searchText.GetValue()
if self.vendorChoice.GetStringSelection() == 'MyMiniFactory':
self._mmfObj.setQuery(query)
def cleanResultsFolder(self):
oldResults = aio.SearchResultFolder
if not os.path.exists(oldResults):
os.makedirs(oldResults)
aioClientLog.info('Created path ' + oldResults)
return
for result in os.listdir(oldResults):
path = os.path.join(oldResults, result)
try:
if os.path.isdir(path):
shutil.rmtree(path)
elif os.path.isfile(path):
os.unlink(path)
except Exception as e:
aioClientLog.error(e)
def makePopup(self, text, cancelCallback=None):
self.showPopup = True
self.Disable()
self.popup = LoadingPopup(self.parent, text, cancelCallback)
def closePopup(self):
self.showPopup = False
self.Enable()
if self.popup:
self.popup.Destroy()
def Show(self):
wx.Panel.Show(self)
if self.showPopup:
self.popup.Hide()
self.popup.Show()
if self.preview:
self.preview.Show()
def OnInfo(self, event):
if self.popup:
result = event.GetInfo()
fileList = []
imgList = []
fileList.append(result.getTitle())
imgList.append(result.getThumbPath())
self.fileView.addFileList(fileList, [''])
self.popup.Show()
self.Update()
else:
print 'no popup'
def selectCallback(self, resultName):
self.selectedItemName = resultName
index = self.resultSearchIDs[self.fileView.lastPressedID]
aioSearchLog.info('SELECT start item %s | index %s', resultName, str(index))
if self.checkSearchItemCache(resultName):
return
txt = _('Loading...')
self.makePopup(txt, self.cancelPreview)
self.setupSearchObject()
selected = self._searchResults.getItemIndex(self.fileView.lastPressedID)
self._selectSearchThread = SelectSearchThread(self, selected)
self.popup.setCurrentProcessInput(self._selectSearchThread.getCurrentProcess)
self._selectSearchThread.start()
def checkSearchItemCache(self, resultName):
index = self.resultSearchIDs[self.fileView.lastPressedID]
path = self.currentSearchCache + '/' + str(index)
datafile = path + '/item.json'
found_query = False
if os.path.isfile(datafile):
with open(datafile) as (f):
try:
self.data = json.load(f)
aioClientLog.info('Load search item cache %s', datafile)
title = self.data.get('title')
author = self.data.get('author')
date_pub = self.data.get('date-published')
description = self.data.get('description')
fileList = self.data.get('file-list')
tpath = self.data.get('image-list')
imgPath = []
found_query = True
for i, file in enumerate(tpath):
fname = path + '/' + str(i) + '_' + file['name']
imgPath.append(fname)
if not os.path.isfile(fname):
found_query = False
except ValueError:
pass
except Exception as e:
aioClientLog.exception(e)
if found_query:
searchObj = self.getSearchObject()
selected = SearchItem('term', index, resultName, imgPath, searchObj)
event = SelectSearchEvent(myEVT_SELECT_SEARCH, -1, selected, description, author, date_pub, fileList)
if self.preview:
self.preview.Hide()
self.preview.Destroy()
self.preview = PSearch_preview(self, (0, 0), self.size, event, path)
return found_query
def OnFinishSelectSearch(self, event):
try:
self.finishSelectSearchProc(event)
except Exception as e:
aioClientLog.exception(e)
def finishSelectSearchProc(self, event):
if event.Cancelled():
self.closePopup()
return
else:
selected = event.GetSearchItem()
author = None
if selected is not None:
author = selected.author
if selected is None or author is None:
self.closePopup()
text = _("Can't get result.\nCheck connection.")
dial = AioDialog(self, text, self.pos, self.size)
else:
self.cacheSearchItem(event)
if self.preview:
self.preview.Hide()
self.preview.Destroy()
self.preview = PSearch_preview(self, (0, 0), self.size, event)
self.closePopup()
aioSearchLog.info('SELECT finish %s', self.selectedItemName)
return
def cacheSearchItem(self, event):
path = self.currentSearchCache
if path is None:
return
else:
selected = event.GetSearchItem()
index = selected.getSearchID()
si_folder = path + '/' + str(index)
if os.path.isdir(si_folder):
return
os.makedirs(si_folder)
aioClientLog.info('New search item cache %s', si_folder)
filename = si_folder + '/item.json'
with open(filename, 'w') as (f):
os.chmod(filename, 511)
tdata = {}
tdata['title'] = selected.getTitle()
tdata['author'] = selected.getAuthor()
tdata['date-published'] = selected.getDatePublished()
tdata['description'] = selected.getDescription()
tdata['file-list'] = selected.getFileList()
tdata['image-list'] = selected.getImageList()
json.dump(tdata, f)
imgDir = aio.SearchResultFolder + '/' + str(selected.getTimeStamp())
imgPath = []
for root, dirs, files in os.walk(imgDir, topdown=True):
for name in files:
imgPath.append(name)
for bname in imgPath:
filename = imgDir + '/' + bname
if os.path.exists(filename):
shutil.copy2(filename, si_folder)
return
def cancelPreview(self):
self._selectSearchThread.cancel()
def OnCancel(self, event):
try:
self.cancelProc()
except Exception as e:
aioClientLog.exception(e)
def cancelProc(self):
self.searchText.SetValue('')
def searchLongPressCallback(self):
text = _('Clear search cache?')
dial = AioDialog(self, text, self.pos, self.size, type='yes/no', callback=self.clearCacheCallback)
def clearCacheCallback(self, result):
if result == wx.ID_NO:
return
self._searchResults.clearResults()
self._totalResults = 0
self.resultSearchIDs = []
self.refreshFileView([], [])
self.clearCache()
def clearCache(self):
folder = aio.SearchResultCacheFolder
for search in os.listdir(folder):
path = os.path.join(folder, search)
aioClientLog.info('clear all cache %s', path)
shutil.rmtree(path)
def OnSearch(self, event=None):
try:
self.searchProc()
except Exception as e:
aioClientLog.exception(e)
def searchProc(self):
query = self.searchText.GetValue()
if query == self.searchInstruct:
return
self._query = query
vendor = self.vendorChoice.GetStringSelection()
self._searchResults.clearResults()
self._totalResults = 0
self.resultSearchIDs = []
initial = 1
amount = 16
aioSearchLog.info('SEARCH start %s | vendor %s', query, vendor)
if aio.checkInternet():
self.cleanCache(self._query)
else:
aioClientLog.info('internet not working during search')
aioSearchLog.info('internet not working during search')
if self.checkSearchCache(self._query):
return
if len(self._searchResults) > 0:
initial = len(self._searchResults)
amount = 16 - initial % 16
initial += 1
txt = _('Searching...')
self.makePopup(txt, self.cancelSearch)
searchObj = self.getSearchObject()
self._searchThread = SearchingThread(self, self._query, searchObj, initial, amount)
self.popup.setCurrentProcessInput(self._searchThread.getCurrentProcess)
self.refreshFileView([], [])
self._searchThread.start()
def cancelSearch(self):
self._searchThread.cancel()
def cleanCache(self, query):
self.currentSearchCache = None
vendor = self.vendorChoice.GetStringSelection()
folder = aio.SearchResultCacheFolder
for search in os.listdir(folder):
path = os.path.join(folder, search)
datafile = path + '/data.json'
if os.path.isfile(datafile):
with open(datafile) as (f):
try:
self.data = json.load(f)
cquery = self.data.get('search-term')
cvendor = self.data.get('vendor')
ctime = os.path.getctime(datafile)
now = time.time()
diff = math.fabs(now - ctime)
m, s = divmod(diff, 60)
h, m = divmod(m, 60)
d, h = divmod(h, 24)
if cquery == query and cvendor == vendor:
if d >= CACHE_CLEAR_TIME:
aioClientLog.info('clear cache item %s %s %s', path, cquery, cvendor)
aioSearchLog.info('clear cache item %s %s %s', path, cquery, cvendor)
shutil.rmtree(path)
except ValueError:
pass
except Exception as e:
aioClientLog.exception(e)
return
def checkSearchCache(self, query):
self.currentSearchCache = None
vendor = self.vendorChoice.GetStringSelection()
folder = aio.SearchResultCacheFolder
found_query = False
for search in os.listdir(folder):
path = os.path.join(folder, search)
datafile = path + '/data.json'
if os.path.isfile(datafile):
with open(datafile) as (f):
try:
self.data = json.load(f)
cquery = self.data.get('search-term')
cvendor = self.data.get('vendor')
clresult = self.data.get('label-results')
ciresult = self.data.get('image-results')
temp = []
for file in ciresult:
temp.append(path + '/' + file)
ciresult = temp
if cquery == query and cvendor == vendor:
self.currentSearchCache = path
aioClientLog.info('Found query cache %s %s %s', datafile, cquery, cvendor)
aioSearchLog.info('Found query cache %s %s %s', datafile, cquery, cvendor)
self._totalResults = self.data.get('total-results')
aioClientLog.info('Total Result %s', str(self._totalResults))
self.resultSearchIDs = self.data.get('search-id')
found_query = True
break
except ValueError:
pass
except Exception as e:
aioClientLog.exception(e)
if found_query:
self.refreshFileView(clresult, ciresult)
searchObj = self.getSearchObject()
self._searchResults = SearchResults(None)
for i, file in enumerate(ciresult):
index = self.resultSearchIDs[i]
label = clresult[i]
image = ciresult[i]
item = SearchItem(query, index, label, image, searchObj)
self._searchResults.addItem(item)
sr_size = len(self._searchResults)
if sr_size < self._totalResults and sr_size % 16 != 0:
found_query = False
return found_query
def OnFinishSearch(self, event):
try:
self.finishSearchProc(event)
except Exception as e:
aioClientLog.exception(e)
def finishSearchProc(self, event):
self.closePopup()
results = event.GetResults()
if results is None:
text = _('Search failed.\nCheck connection.')
dial = AioDialog(self, text, self.pos, self.size)
self.refreshFileView([], [])
return
else:
self._searchResults += results
self._totalResults = event.GetTotalResults()
self.cacheSearchResult()
items = self._searchResults.getAllItems()
self.resultSearchIDs = [ i.getSearchID() for i in items ]
fileList = list(self._searchResults.getAllTitles())
idir = list(self._searchResults.getThumbnailList())
self.refreshFileView(fileList, idir)
aioSearchLog.info('SEARCH finish %s', self._query)
return
def cacheSearchResult(self):
fileList = list(self._searchResults.getAllTitles())
idir = list(self._searchResults.getThumbnailList())
if self.currentSearchCache is None:
now = datetime.now()
dt = now.strftime('search_result_%Y_%m_%d__%H_%M_%S')
path = aio.SearchResultCacheFolder + '/' + dt
os.makedirs(path)
self.currentSearchCache = path
else:
path = self.currentSearchCache
temp_idir = []
for file in idir:
temp_idir.append(os.path.basename(file))
search_ids = []
items = self._searchResults.getAllItems()
for i, item in enumerate(items):
search_ids.append(item.getSearchID())
filename = path + '/data.json'
with open(filename, 'w') as (f):
aioClientLog.info('New search result cache %s', path)
aioSearchLog.info('New search result cache %s', path)
os.chmod(filename, 511)
tdata = {}
tdata['search-term'] = self.searchText.GetValue()
tdata['vendor'] = self.vendorChoice.GetStringSelection()
tdata['label-results'] = fileList
tdata['image-results'] = temp_idir
tdata['search-id'] = search_ids
tdata['total-results'] = self._totalResults
json.dump(tdata, f)
for file in idir:
if os.path.exists(file):
dirname = os.path.dirname(file)
if dirname != path:
shutil.copy2(file, path)
return
def searchMore(self, initial, amount):
searchObj = self.getSearchObject()
if len(self._searchResults) == searchObj.totalResults():
return
txt = _('Searching...')
self.makePopup(txt, self.cancelSearch)
if initial % 16 != 0:
amount = 16 - (initial - 1) % 16
self._searchThread = SearchingThread(self, self._query, searchObj, initial, amount, cont=True)
self.popup.setCurrentProcessInput(self._searchThread.getCurrentProcess)
self._searchThread.start()
def OnFinishContinueSearch(self, event):
try:
self.finishContinueSearchProc(event)
except Exception as e:
aioClientLog.exception(e)
def finishContinueSearchProc(self, event):
newResults = event.GetResults()
if newResults is None:
self.closePopup()
text = _('Search failed.\nCheck connection.')
dial = AioDialog(self, text, self.pos, self.size)
else:
self._searchResults += newResults
self.cacheSearchResult()
for item in newResults.getAllItems():
self.resultSearchIDs.append(item.getSearchID())
fileList = list(self._searchResults.getAllTitles())
idir = list(self._searchResults.getThumbnailList())
self.fileView.setInput(idir, fileList)
self.fileView.nextPageProc(search=False)
self.closePopup()
return
def OnType(self, event):
try:
self.typeProc()
except Exception as e:
aioClientLog.exception(e)
def typeProc(self):
text = _('Enter a search term:')
m_size = (aio.page_width * self.sW, aio.main_height * self.sH)
self.vkeyboardSearch = AioVirtualKeyboard(self.parent, text, pos=self.pos, size=m_size, callback=self.setSearchTerm)
def setSearchTerm(self, term):
if term is not None:
self.searchText.SetValue(term)
self.OnSearch()
else:
self.btnSearch.SetFocus()
return
def refreshFileView(self, lst, idir=None):
if self.fileView:
self.fileView.Destroy()
x, y = self.size
a, b = self.pos
fSize = (x - a, y - b)
fPos = (a, b + 4)
self.fileView = PSearch_selection(self, fPos, fSize, self.thumbnailThread, lst, callback=self.selectCallback, idirname=idir, buttonColour=aio.pink, totalResults=self._totalResults)
class LoadingPopup(wx.Panel):
def __init__(self, parent, text, cancelCallback=None):
self.parent = parent
self.sW = sW = parent.sW
self.sH = sH = parent.sH
self.title = text
self.callback = cancelCallback
self.main_w = main_w = aio.main_width
self.main_h = main_h = aio.main_height
border = 25
btn_w = 100
self.size = w, h = aio.scalePos(self.main_w / 2, self.main_h / 2)
self.pos = (self.size[0] / 2, self.size[1] / 2 + aio.top_height * sH)
wx.Panel.__init__(self, parent, wx.ID_ANY, self.pos, self.size)
self.SetBackgroundColour(aio.darkgrey)
font = wx.Font(50 * sW, wx.FONTFAMILY_DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
dc = wx.ScreenDC()
dc.SetFont(font)
tsize = dc.GetTextExtent(self.title)
tpos = (w / 2 - tsize[0] / 2, (border + 5) * sH)
self.titleField = wx.StaticText(self, -1, self.title, tpos, tsize)
self.titleField.SetFont(font)
self.titleField.SetForegroundColour(aio.white)
self.gspeed = 80
gleft = 50
gheight = 70
gSize = (w - 100 * sW, gheight * sH)
gPos = (gleft * sW, h - (border + gheight) * sH)
self.gauge = wx.Gauge(self, -1, 50, pos=gPos, size=gSize)
self.timer = wx.Timer(self)
self.timer.Start(self.gspeed)
self.Bind(wx.EVT_TIMER, self.OnGaugePulse)
font = wx.Font(20 * sW, wx.FONTFAMILY_DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
dheight = 70
dsize = (w - 100 * sW, dheight * sH)
dpos = ((gleft + 5) * sW, h - (border + 1.6 * gheight) * sH)
self.descField = wx.StaticText(self, -1, '', dpos, dsize)
self.descField.SetFont(font)
self.descField.SetForegroundColour(aio.white)
bSize = aio.scalePos(btn_w, btn_w)
bPos = aio.scalePos(600, 25)
self.btnCancel = RoundButton(self, -1, '', bPos, bSize, type='cancel')
self.btnCancel.SetBackgroundColour(aio.darkgrey)
self.btnCancel.Bind(wx.EVT_LEFT_DOWN, self.OnCancel)
if self.callback is None:
self.btnCancel.Hide()
self.currentProcessInput = None
return
def setCurrentProcessInput(self, cpinput):
self.currentProcessInput = cpinput
def getCurrentProcess(self):
desc = None
if self.currentProcessInput:
desc = self.currentProcessInput()
return desc
def OnCancel(self, event):
try:
self.cancelProc()
except Exception as e:
aioClientLog.error(e)
def cancelProc(self):
self.btnCancel.Disable()
self.setCurrentProcessInput(lambda : 'cancelling . . .')
if self.callback:
self.callback()
def OnGaugePulse(self, event):
try:
self.gaugePulseProc()
except Exception as e:
aioClientLog.exception(e)
def gaugePulseProc(self):
self.gauge.Pulse()
desc = self.getCurrentProcess()
if desc:
if len(desc) > MAX_DISPLAY_STRING_LEN:
desc = desc[0:MAX_DISPLAY_STRING_LEN - 19] + ' ... ' + desc[-14:]
self.descField.SetLabel(desc)
def __del__(self):
self.timer.Stop()
Code: Alles auswählen
# uncompyle6 version 3.5.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.5 (default, Aug 7 2019, 00:51:29)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
# Embedded file name: aio_ui/search_result.py
# Compiled at: 2016-10-17 13:06:37
import aio, os, time, requests, wx, shutil
try:
import patoolib
VALIDATE_PATOOL_INSTALLED = True
except:
VALIDATE_PATOOL_INSTALLED = False
print 'please install patool'
print 'sudo pip install --upgrade patool'
print 'sudo apt-get -y install unrar'
from zipfile import ZipFile
from search_threads import *
class SearchItem():
def __init__(self, term, sID, sTitle, sThumb, searchObj, fillInfo=False):
"""
Parameters:
term = the search query used to return this item (string)
sID = the ID of the search result (int)
sTitle = the title of the search result (string)
sThumb = the URL for the thumbnail of the result (string)
searchObj = the vendor object (Vendor)
"""
self._term = term
self._timeStamp = None
self._searchID = sID
self._title = sTitle.replace('/', '\\')
self._thumbnail = sThumb
self._searchObj = searchObj
self._resultPath = aio.SearchResultFolder
self._thumbPath = aio.SearchThumbnailFolder
self._myThumbPath = ''
self.currentProcess = None
self.author = None
self.datePublished = None
self.description = None
self.imageList = None
self.fileList = None
self.vendor = None
self._imgDir = None
self._nameUrls = None
self._imgDetails = None
if fillInfo:
self.fillInfo()
return
def fillInfo(self):
self.author = self._searchObj.author(self._searchID)
self.datePublished = self._searchObj.datePublished(self._searchID)
self.description = self._searchObj.description(self._searchID)
self.fileList = self._searchObj.fileList(self._searchID)
self.imageList = self._searchObj.imageList(self._searchID)
self.vendor = self._searchObj.getVendor()
def isInfoFilled(self):
return self.author and self.datePublished and self.description and self.imageList and self.fileList
def setAuthor(self, thumbnail):
self._thumbnail = thumbnail
def setAuthor(self, author):
self.author = author
def setFileList(self, fileList):
self.fileList = fileList
def setImageList(self, imageList):
self.imageList = imageList
def setDescription(self, description):
self.description = description
def setDatePublished(self, datePublished):
self.datePublished = datePublished
def setVendor(self, vendor):
self.vendor = vendor
def getTitle(self):
return self._title
def getSearchID(self):
return self._searchID
def getTimeStamp(self):
"""
Return time stamp (string) which will be used to name the folder
containing the images of this search item.
"""
if self._timeStamp is None:
self._setTimeStamp()
return self._timeStamp
def _setTimeStamp(self):
self._ID = time.strftime('%m%d%y%H%M%S')
def getTerm(self):
return self._term
def getImageList(self):
if not self.imageList:
self.imageList = self._searchObj.imageList(self._searchID)
return self.imageList
def getFileList(self):
if not self.fileList:
self.fileList = self._searchObj.fileList(self._searchID)
return self.fileList
def getDescription(self):
if not self.description:
self.description = self._searchObj.description(self._searchID)
return self.description
def getDatePublished(self):
if not self.datePublished:
self.datePublished = self._searchObj.datePublished(self._searchID)
return self.datePublished
def getAuthor(self):
if not self.author:
self.author = self._searchObj.author(self._searchID)
return self.author
def getVendor(self):
return self._searchObj.getVendor()
def getThumbPath(self):
return self._myThumbPath
def downloadImages(self):
success = True
end = self.indexImages()
for i in range(0, end):
if not self.downloadImage(i):
success = False
return success
def indexImages(self):
self._imgDir = self._resultPath + '/' + str(self.getTimeStamp()) + '/'
if not os.path.exists(self._imgDir):
os.makedirs(self._imgDir)
self._nameUrls = self.getImageList()
self._imgDetails = 'picture for ' + self._title
return len(self._nameUrls)
def downloadImage(self, index):
nameUrls = self._nameUrls
path = self._imgDir + str(index) + '_' + nameUrls[index]['name']
return self._download(nameUrls[index]['url'], path, self._imgDetails)
def downloadThumbnail(self, caller=None):
if self._thumbnail == '':
return
path = self._thumbPath + '/' + str(self._searchID) + '.png'
self._myThumbPath = path
details = 'thumbnail for ' + self._title
self._download(self._thumbnail, path, details, caller)
self._sizeImageForThumbnail(path)
def downloadFile(self, nameUrl, unzipStl=True):
path = aio.SearchResultFolder + '/' + nameUrl['name']
details = 'File for ' + self._title
downloaded = self._download(nameUrl['url'], path, details)
if nameUrl['name'].endswith(('.zip', '.ZIP')) and unzipStl:
if downloaded:
downloaded = self._unzipFile(path)
elif nameUrl['name'].endswith(('.rar', '.RAR')) and unzipStl:
if downloaded:
if VALIDATE_PATOOL_INSTALLED:
downloaded = self._unrarFile(path)
else:
os.remove(path)
else:
fname = aio.storage_dir['Local'] + '/' + nameUrl['name']
os.rename(path, fname)
return downloaded
def _unzipFile(self, zippath):
if not os.path.exists(zippath):
return False
with ZipFile(zippath) as (zfile):
for filename in zfile.namelist():
if filename.endswith(('.stl', '.STL', '.gcode', '.GCODE')):
path = aio.SearchResultFolder
zfile.extract(filename, path)
aioClientLog.info('Unzipped ' + filename)
basename = os.path.basename(filename)
fname = aio.SearchResultFolder + '/' + filename
fname2 = aio.storage_dir['Local'] + '/' + basename
os.rename(fname, fname2)
os.remove(zippath)
return True
def _unrarFile(self, rarpath):
if not os.path.exists(rarpath):
return False
rarname = os.path.basename(rarpath) + '_tmp'
extr_dir = aio.storage_dir['Local'] + '/' + rarname
try:
os.makedirs(extr_dir)
except OSError as e:
aioClientLog.error(str(e) + '\nAttempting to remove...')
try:
shutil.rmtree(extr_dir)
os.makedirs(extr_dir)
except Exception as e:
aioClientLog.error(e)
return False
else:
try:
patoolib.extract_archive(rarpath, outdir=extr_dir)
except Exception as e:
aioClientLog.error(e)
return False
goodfiles = [ f for f in os.listdir(extr_dir) if os.path.isfile(os.path.join(extr_dir, f)) and f.endswith(('.stl',
'.STL',
'.gcode',
'.GCODE'))
]
try:
for f in goodfiles:
dest = os.path.join(aio.storage_dir['Local'], f)
src = os.path.join(extr_dir, f)
shutil.copy(src, dest)
shutil.rmtree(extr_dir)
os.remove(rarpath)
except Exception as e:
aioClientLog.error(e)
return False
return True
def _download(self, url, path, details=None, caller=None):
self.currentProcess = url
if url is None:
return False
else:
info = 'Downloading'
if details is not None:
info += ' ' + details
info += ': ' + url
aioClientLog.info(info)
success = True
request = requests.get(url, verify=False, stream=True)
try:
with open(path, 'w') as (f):
fileLength = 0
for chunk in request.iter_content(chunk_size=1024):
if chunk:
fileLength += len(chunk)
f.write(chunk)
f.flush()
aioClientLog.info(str(fileLength / 1024) + ' KB Downloaded')
except Exception as e:
aioClientLog.info('Failed to download ' + path)
aioClientLog.error(e)
success = False
if caller is not None:
event = InfoEvent(myEVT_INFO, -1, self)
wx.PostEvent(caller, event)
return success
def _sizeImageForThumbnail(self, imgPath):
nolog = wx.LogNull()
img = wx.Image(imgPath, type=wx.BITMAP_TYPE_ANY)
del nolog
x, y = img.GetSize()
if x > y:
a = x
w = 0
h = (x - y) / 2.0
else:
a = y
w = (y - x) / 2.0
h = 0
img = img.Size((a, a), (w, h), -1, -1, -1)
b = 5 * a / 4.0
img.Resize((a, b), (0, 0), -1, -1, -1)
os.remove(imgPath)
img.SaveFile(imgPath, type=wx.BITMAP_TYPE_PNG)
def removeImages(self):
folder = self._resultPath + '/' + str(self.getTimeStamp())
if os.path.isdir(folder):
shutil.rmtree(folder)
class SearchResults():
def __init__(self, vendor=None):
self._vendor = vendor
self._results = []
self._thumbFolder = aio.SearchThumbnailFolder
self._thumbPaths = []
def addItem(self, searchItem):
self._results.append(searchItem)
def getItem(self, title):
print 'Warning: can be incorrect if there are items with same name', title
for item in self._results:
if item.getTitle() == title:
return item
return
def getItemSearchID(self, searchID):
for item in self._results:
if item.getSearchID() == searchID:
return item
return
def getItemIndex(self, index):
size = len(self._results)
if size > 0 and index >= -1 and index <= size:
return self._results[index]
else:
return
def getAllItems(self):
return self._results
def getAllTitles(self):
return [ result.getTitle() for result in self._results ]
def downloadThumbnails(self, amt=None, caller=None):
pathList = []
if amt and amt < len(self._results):
for i in range(0, amt):
self._results[i].downloadThumbnail(caller=caller)
pathList.append(self._results[i].getThumbPath())
else:
for result in self._results:
result.downloadThumbnail(caller=caller)
pathList.append(result.getThumbPath())
self._thumbPaths = pathList
def getThumbnailList(self):
if len(self._thumbPaths) != len(self._results):
self._thumbPaths = []
for item in self._results:
self._thumbPaths.append(item._thumbnail)
return self._thumbPaths
def setThumbnailList(self, thumbs):
self._thumbPaths = thumbs
def clearResults(self):
folder = self._thumbFolder
if not os.path.exists(folder):
os.makedirs(folder)
aioClientLog.info('Created path ' + folder)
for thumb in os.listdir(folder):
path = os.path.join(folder, thumb)
try:
if os.path.isfile(path):
os.unlink(path)
except Exception as e:
aioClientLog.error(e)
self._results = []
def __add__(self, other):
results = self.getAllItems() + other.getAllItems()
thumbs = self.getThumbnailList() + other.getThumbnailList()
search = SearchResults()
for result in results:
search.addItem(result)
search.setThumbnailList(thumbs)
return search
def __len__(self):
return len(self._results)
Code: Alles auswählen
# uncompyle6 version 3.5.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.5 (default, Aug 7 2019, 00:51:29)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
# Embedded file name: aio_ui/ThingiVendor.py
# Compiled at: 2017-03-08 13:35:51
import aio, aioLog, logging, math, os, string, time
from search_vendor import Vendor
from search_result import SearchResults, SearchItem
from HTMLParser import HTMLParser
try:
from BeautifulSoup import BeautifulSoup
except ImportError:
print 'Please install BeautifulSoup'
print 'sudo pip install BeautifulSoup'
res = os.system('sudo python -m pip install BeautifulSoup')
print res
try:
from BeautifulSoup import BeautifulSoup
except:
print 'No Internet, pip install BeautifulSoup fail'
import re
BASE_URL = 'http://www.thingiverse.com'
SEARCH_URL = 'http://www.thingiverse.com/search/page:#?q='
THING_URL = 'http://www.thingiverse.com/thing:'
THING_PAGE_LENGTH = 12
THUMB_PAGE_LENGTH = 8
TOTAL_RESULT_FRONT_TAG = '<h1>Search Results</h1>'
TOTAL_RESULT_BACK_TAG = 'results matching'
ITEM_TAG = 'thing thing-interaction-parent item-card'
FLAG_TAG = 'thing-img limited-view'
class Thingiverse(Vendor):
def __init__(self):
self._totalResults = 0
self.prevRequest = None
self.prevProcResponse = None
return
def getVendor(self):
return 'Thingiverse'
def totalResults(self):
return self._totalResults
def connect(self):
pass
def search(self, query, initial=1, numResults=16):
numThingPages = math.ceil(float(numResults) / THING_PAGE_LENGTH)
startingThingPage = (initial - 1) / THING_PAGE_LENGTH + 1
results = SearchResults()
endingThingPage = int(startingThingPage + numThingPages)
totalResults = 0
resultsSoFar = 0
for i in range(startingThingPage, endingThingPage):
url = SEARCH_URL.replace('#', str(i)) + query.replace(' ', '+')
pageSrc = self._getPageSource(url)
if pageSrc is None:
return
firstResult = 1
if i == startingThingPage:
firstResult = initial - THING_PAGE_LENGTH * (startingThingPage - 1)
totalResults = self._findTotalResults(pageSrc)
if initial + numResults > totalResults:
numResults = totalResults - initial + 1
if numResults < 1:
break
nPageResults = THING_PAGE_LENGTH + 1 - firstResult
if numResults - resultsSoFar < nPageResults:
nPageResults = numResults - resultsSoFar
lastResult = firstResult - 1 + nPageResults
itemPar = ItemParser(firstResult, lastResult)
itemPar.feed(pageSrc)
tresults = itemPar.get_items()
newResults = SearchResults()
for item in tresults:
newResults.addItem(SearchItem(query, item['id'], item['name'], item['thumbnail'], self))
if len(tresults) == 0:
break
elif len(results) == 0:
results = newResults
elif tresults[(-1)]['id'] != results.getItemIndex(-1).getSearchID():
results += newResults
else:
break
resultsSoFar += nPageResults
return results
def _findTotalResults(self, webpage):
self._totalResults = 0
try:
parsed_html = BeautifulSoup(webpage)
search_status = parsed_html.body.find('div', attrs={'class': 'search-status'})
match = re.findall('(\\d+)', search_status.text)
self._totalResults = int(match[0])
except Exception as e:
aioClientLog.exception(e)
aioClientLog.info('Find total result number fail ')
return self._totalResults
def description(self, resultID):
pageSrc = self._getPageSource(THING_URL + resultID)
descriptString = '<div id="description" class="thing-info-content">'
descriptLen = len(descriptString)
start = pageSrc.find(descriptString)
if start == -1:
return 'No description available.'
start += descriptLen
endString = '</div>'
end = pageSrc.find(endString, start)
desPar = DescriptionParser()
desPar.feed(pageSrc[start:end])
return desPar.get_data()
def author(self, resultID):
pageSrc = self._getPageSource(THING_URL + resultID)
descriptString = '<div class="thing-header-data">'
descriptLen = len(descriptString)
initial = pageSrc.find(descriptString)
initial += descriptLen
if initial == -1:
return 'Unknown'
nextDescript = '<a href="'
temp = pageSrc.find(nextDescript, initial)
start = pageSrc.find('>', temp) + 1
end = pageSrc.find('<', start)
return pageSrc[start:end]
def datePublished(self, resultID):
pageSrc = self._getPageSource(THING_URL + resultID)
dateString = '<time datetime="'
dateStrLen = len(dateString)
initial = pageSrc.find(dateString)
if initial == -1:
return None
else:
initial += dateStrLen
start = pageSrc.find('>', initial) + 1
end = pageSrc.find('<', start)
return pageSrc[start:end].strip()
def imageList(self, resultID):
pageSrc = self._getPageSource(THING_URL + resultID)
if pageSrc is None:
return []
else:
start = pageSrc.find('<div class="thing-page-slider card-slider">')
end = pageSrc.find('<div class="thing-about">', start)
pageSrc = pageSrc[start:end]
imgURLs = []
imgString = '<div class="thing-page-image featured">'
imgStrLen = len(imgString)
start = pageSrc.find(imgString)
if start > -1:
start += imgStrLen
imgURLs.append(self._findImage(pageSrc[start:], True))
while start > -1:
start = pageSrc.find(imgString, start)
if start == -1:
break
start += imgStrLen
imgURLs.append(self._findImage(pageSrc[start:]))
return imgURLs
def _findImage(self, pageSrc, firstImage=False):
if firstImage:
imgString = '<img src="'
else:
imgString = '<img data-img="'
imgStrLen = len(imgString)
low = pageSrc.find(imgString) + imgStrLen
high = pageSrc.find('"', low)
url = pageSrc[low:high]
return {'name': url.split('/')[(-1)], 'url': url}
def fileList(self, resultID):
pageSrc = self._getPageSource(THING_URL + resultID)
results = []
fileString = '<div class="thing-file">'
fileStrLen = len(fileString)
start = 0
while True:
start = pageSrc.find(fileString, start)
if start == -1:
break
start += fileStrLen
nameUrl = self._getNameAndUrl(pageSrc[start:])
if self.isValidFile(nameUrl['name']):
results.append(nameUrl)
return results
def isValidFile(self, filename):
exclusions = ('.stl', '.STL', '.gcode', '.GCODE', '.zip', '.ZIP')
if filename.endswith(exclusions):
return True
return False
def _getNameAndUrl(self, pageSrc):
downloadStr = '<a href="'
downStrLen = len(downloadStr)
low = pageSrc.find(downloadStr) + downStrLen
high = pageSrc.find('"', low)
url = BASE_URL + pageSrc[low:high]
nameStr = 'data-file-name="'
nameLen = len(nameStr)
low = pageSrc.find(nameStr, high) + nameLen
high = pageSrc.find('"', low)
name = pageSrc[low:high]
return {'name': name, 'url': url}
def _getPageSource(self, url):
st = time.time()
if self.prevRequest == url and self.prevProcResponse is not None:
procResponse = self.prevProcResponse
else:
response = aio.sendRequest(url)
if response is None:
return
procResponse = None
if not isinstance(response, bool):
procResponse = response.content.decode('utf-8')
self.prevProcResponse = procResponse
self.prevRequest = url
return procResponse
class DescriptionParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.inP = False
self.inUl = False
self.inLi = False
self.description = ''
def handle_starttag(self, tag, attrs):
if tag == 'p':
self.inP = True
elif tag == 'ul':
self.inUl = True
elif tag == 'li':
self.inLi = True
def handle_endtag(self, tag):
if tag == 'p':
self.inP = False
self.description += '\n\n'
elif tag == 'ul':
self.inUl = False
self.description += '\n'
elif tag == 'li':
self.inLi = False
self.description += '\n'
def handle_data(self, data):
if self.inP:
self.description += data
if self.inUl:
if self.inLi:
self.description += '-' + data
def get_data(self):
return self.description
class ItemParser(HTMLParser):
"""
This class is used to get the name, thumbnail url, and thing id
for each of the search results
"""
def __init__(self, initial, final):
HTMLParser.__init__(self)
self.titles = []
self.ids = []
self.imgs = []
self.itemTxt = ITEM_TAG
self.flagTxt = FLAG_TAG
self.getImg = False
self.counter = 0
self.initial = initial
self.final = final
def get_items(self):
items = []
end = len(self.titles)
for i in range(0, end):
item = [
self.ids[i], self.titles[i], self.imgs[i]]
items.append({'name': item[1], 'id': item[0], 'thumbnail': item[2]})
return items
def handle_starttag(self, tag, attrs):
if tag == 'div':
if self.getImg:
if len(attrs) == 1:
if attrs[0][0] == 'class' and attrs[0][1] == self.flagTxt:
self.imgs.append('')
self.getImg = False
elif len(attrs) == 5:
if attrs[0][0] == 'class' and attrs[0][1] == self.itemTxt:
if self.counter < self.initial - 1 or self.counter > self.final - 1:
self.counter += 1
return
self.ids.append(attrs[3][1])
self.titles.append(attrs[4][1])
self.counter += 1
self.getImg = True
elif tag == 'img' and self.getImg and len(attrs) == 2:
if attrs[0][0] == 'class' and attrs[0][1] == 'thing-img':
self.imgs.append(attrs[1][1])
self.getImg = False
if __name__ == '__main__':
import aioLog, logging
logpath = os.path.join('/tmp', 'logs', 'basename')
clientLog = aioLog.AioClientLog()
__builtins__.aioClientLog = clientLog
logHandler = aioLog.AioRotatingFileHandler(filename=logpath)
aioClientLog.addHandler(logHandler)
thing = Thingiverse()
result = thing.search('tortilla')
print thing.totalResults()
descript = thing.description('967877')
print descript + '\n\n\n'
print thing.author('967877')
Code: Alles auswählen
# uncompyle6 version 3.5.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.5 (default, Aug 7 2019, 00:51:29)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
# Embedded file name: aio_ui/thingiverseApi.py
# Compiled at: 2016-03-20 15:10:40
from rauth import OAuth2Service
import json, os, sys, webbrowser, requests
class Thingiverse:
def __init__(self, appinfo):
self._appinfo = appinfo
self._access_code = ''
self._session = None
self._auth = None
return
def connect(self):
if not self._auth:
self._auth = OAuth2Service(name='AIOTEST', client_id=self._appinfo['client_id'], client_secret=self._appinfo['client_secret'], access_token_url='https://www.thingiverse.com/login/oauth/access_token', authorize_url='https://www.thingiverse.com/login/oauth/authorize', base_url='https://api.thingiverse.com')
self._access_code = ''
self._get_access_token()
def _get_access_token(self):
params = {'client_id': self._appinfo['client_id'], 'client_secret': self._appinfo['client_secret'], 'code': self._access_code}
try:
self._session = self._auth.get_auth_session(data=params)
except KeyError as e:
self._get_access_code()
print e
sys.exit(1)
def _get_access_code(self):
params = {'redirect_uri': self._appinfo['redirect_uri'], 'response_type': 'code'}
url = self._auth.get_authorize_url(**params)
webbrowser.open_new(url)
def _api_get(self, suffix_url, params):
try:
response = self._session.get(self._auth.base_url + suffix_url, params=params)
return response.json()
except Exception as error:
print error
sys.exit()
def search(self, keyword, data=None):
suffix_url = '/search/' + keyword + '/'
print 'keyword:', keyword
return self._api_get(suffix_url, data)
def file_List(self, thingID, data=None):
suffix_url = '/things/' + thingID + '/files/'
return self._api_get(suffix_url, data)
def tags(self, thingID, data=None):
suffix_url = '/things/' + thingID + '/tags'
raw_tags = self._api_get(suffix_url, data)
tags = []
for d in raw_tags:
tags.append(d['name'])
return tags
def image_List(self, thingID, data=None):
suffix_url = '/things/' + thingID + '/images/'
return self._api_get(suffix_url, data)
Da wird es eine Änderung im Thingieverse-API gegeben haben. Die musst du eben nachvollziehen.
Und der Grund wird sehr einfach sein: Geld. So ein System ist aufwändig in der Pflege (wie du gerade bemerkst), und keiner bezahlt einem das. Und der Markt ist klein: nur in sehr wenigen Fällen wird man mit solchen auto slicing Geschichten auf Dauer glücklich.
Und der Grund wird sehr einfach sein: Geld. So ein System ist aufwändig in der Pflege (wie du gerade bemerkst), und keiner bezahlt einem das. Und der Markt ist klein: nur in sehr wenigen Fällen wird man mit solchen auto slicing Geschichten auf Dauer glücklich.
Ich sehe da leider keine Fehler im API, sobald ich was ändere bekomme ich Fehler: keine Verbindung möglich. Komischerweise das beide ihre API’s geändert haben (Thingiverse und Myminifactory) halte ich für unwahrscheinlich.
Ich dachte, evtl. findet ihr etwas?
Geld ist kein Problem, leider finde ich gegen Bezahlung keinen Programmierer, weil ich ein kleiner Fisch bin wo man daran zuwenig verdient.
Habe mich seit 2020 mit Python beschäftigt und kleinere Dinge kann ich auch selbst erledigen. Das macht mir Spass. Aber alles kann ich nicht …
Man kann auch mit einem anderen Programm slicen und dann damit Drucken. Scannen kann man sogar auch damit. Wenn der Druck beendet wird, erhalte ich ein Mail mit dem Finischfoto vom Druck.
Meine Anmerkung bezüglich Geld war auf den Hersteller des Druckers bezogen.
Und das man den anders benutzen *kann* glaube ich sofort. Es ist nur kein Alleinstellungsmerkmal, das kann ja jeder Drucker.
Was das “findet ihr was” angeht: unwahrscheinlich, denn da muss man schon ein paar Stunden ins debugging stecken. Und das ist Arbeit, von der nur der was hat, der auch den Drucker hat
Wenn thingieverse und die andere Plattform nicht zusammen gehören (KA, kenne nur TV), dann ist eine andere Erklärung ein Service des Druckerherstellers, der abgeschaltet wurde. Oder vielleicht der API-Key? Wenn der von denen war, und ungültig wurde.
Ich würde versuchen, die Hardware eher auf sowas wie octoprint oder Ähnliches umzustellen. Diese spezielle Funktion mag nicht mehr gehen, aber dann ist wenigstens der verbaute Rechner & Touch Screen noch genutzt. Und die Features werden gepflegt.
Und das man den anders benutzen *kann* glaube ich sofort. Es ist nur kein Alleinstellungsmerkmal, das kann ja jeder Drucker.
Was das “findet ihr was” angeht: unwahrscheinlich, denn da muss man schon ein paar Stunden ins debugging stecken. Und das ist Arbeit, von der nur der was hat, der auch den Drucker hat
Wenn thingieverse und die andere Plattform nicht zusammen gehören (KA, kenne nur TV), dann ist eine andere Erklärung ein Service des Druckerherstellers, der abgeschaltet wurde. Oder vielleicht der API-Key? Wenn der von denen war, und ungültig wurde.
Ich würde versuchen, die Hardware eher auf sowas wie octoprint oder Ähnliches umzustellen. Diese spezielle Funktion mag nicht mehr gehen, aber dann ist wenigstens der verbaute Rechner & Touch Screen noch genutzt. Und die Features werden gepflegt.
Ok sorry.
Das ist mir klar.
Aber evtl erkennt einer und sagt html parser beautifulsoup sei veraltet und da muss ein anderer parser verwenden werden … unrar nicht zeitgemäss , patool closed etc …
Muss ja nicht unbedingt ein API problem sein.
Octoprint kenne ich vom hören, ich benutze meine kleinen 3D drucker über Raspi 3 und Astroprint.__deets__ hat geschrieben: ↑Mittwoch 21. Juli 2021, 14:02 Ich würde versuchen, die Hardware eher auf sowas wie octoprint oder Ähnliches umzustellen. Diese spezielle Funktion mag nicht mehr gehen, aber dann ist wenigstens der verbaute Rechner & Touch Screen noch genutzt. Und die Features werden gepflegt.
Ich schaue mir das mal an, denke aber das mein Drucker zu gross und Touchscreen zu klein ist.
Danke
Es ist unwahrscheinlich, das ein Update von BS da etwas ändern würde. Denn die Bibliothek ändert sich zwar gelegentlich, aber die Server sind hier das Problem. Letztlich kann man da aber nur rum spekulieren. Du musst das debuggen. Kommen da die Informationen, passt der Code zum HTML, etc. Das kannst du durch logging versuchen zu ermitteln. Es ist aber kein Code, den hier jemand ohne massive Umarbeitung testen kann, denn der ist ja in deren Framework eingebettet.