3DDrucker mit Python und API Thingiverse

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
MadisonCH
User
Beiträge: 4
Registriert: Dienstag 13. Juli 2021, 19:20

Dienstag 20. Juli 2021, 07:41

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
einfachTobi
User
Beiträge: 332
Registriert: Mittwoch 13. November 2019, 08:38

Dienstag 20. Juli 2021, 08:58

Da hilft es nur den Code zu sehen, um schauen zu können wo es klemmt. Die API ist ja hinreichend dokumentiert.
__deets__
User
Beiträge: 10055
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 20. Juli 2021, 09:10

Sowas liebe ich ja. Überkandidelte Features, die dann mangels Support b rachliegen. Für ein Bruchteil des Geldes hätte man einen normalen, guten Drucker bekommen, der mit Community Software ewig support hat.
MadisonCH
User
Beiträge: 4
Registriert: Dienstag 13. Juli 2021, 19:20

Dienstag 20. Juli 2021, 22:30

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 :evil:
Mir hat das Ding einfach gefallen, da man keinen Rechner braucht zum slicen, dass macht das Ding ja selbst. 8)

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
Leider wird nach diesen Meldungen nichts gefunden. Komischerweise aber auch keine Fehlermeldungen.

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() 
search_result.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/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) 
ThingiVendor.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/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')
ThingiverseApi.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/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) 
__deets__
User
Beiträge: 10055
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 21. Juli 2021, 11:38

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.
MadisonCH
User
Beiträge: 4
Registriert: Dienstag 13. Juli 2021, 19:20

Mittwoch 21. Juli 2021, 13:49

__deets__ hat geschrieben:
Mittwoch 21. Juli 2021, 11:38
Da wird es eine Änderung im Thingieverse-API gegeben haben. Die musst du eben nachvollziehen.
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?
__deets__ hat geschrieben:
Mittwoch 21. Juli 2021, 11:38
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.
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 …
__deets__ hat geschrieben:
Mittwoch 21. Juli 2021, 11:38
Und der Markt ist klein: nur in sehr wenigen Fällen wird man mit solchen auto slicing Geschichten auf Dauer glücklich.
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.
__deets__
User
Beiträge: 10055
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 21. Juli 2021, 14:02

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.
MadisonCH
User
Beiträge: 4
Registriert: Dienstag 13. Juli 2021, 19:20

Mittwoch 21. Juli 2021, 14:59

__deets__ hat geschrieben:
Mittwoch 21. Juli 2021, 14:02
Meine Anmerkung bezüglich Geld war auf den Hersteller des Druckers bezogen.
Ok sorry. :)
__deets__ hat geschrieben:
Mittwoch 21. Juli 2021, 14:02

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 ;)
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.
__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.
Octoprint kenne ich vom hören, ich benutze meine kleinen 3D drucker über Raspi 3 und Astroprint.
Ich schaue mir das mal an, denke aber das mein Drucker zu gross und Touchscreen zu klein ist.

Danke :)
__deets__
User
Beiträge: 10055
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 21. Juli 2021, 16:24

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.
Antworten