flexxui / flexx

Write desktop and web apps in pure Python
http://flexx.readthedocs.io
BSD 2-Clause "Simplified" License
3.27k stars 259 forks source link

File browser widget #723

Closed rwang0417 closed 2 years ago

rwang0417 commented 2 years ago

Is it possible to add a button beside the path so that the user can browse in the file explorer?

almarklein commented 2 years ago

Sure! A PR is very welcome :)

jrversteegh commented 2 years ago

Excerpt from code of mine (your warranty is now void):

from flexx import flx

class EditLabel(flx.Label):

    CSS = """
    .flx-EditLabel {
      padding-top: 4px;
      padding-right: 8px;
    }"""

class FileInput(flx.LineEdit):
    def _create_dom(self):
        global document, FileReader
        node = super()._create_dom()
        self.file = document.createElement("input")
        self.file.type = "file"
        self.file.style = "display: none"
        self.file.addEventListener("change", self._handle_file)
        node.appendChild(self.file)
        self.reader = FileReader()
        self.reader.onload = self.file_loaded
        return node

    def _handle_file(self):
        self.node.value = self.file.files[0].name
        self.file_selected()

    def select_file(self):
        self.file.click()

    def load(self):
        if self.file.files.length > 0:
            self.reader.readAsText(self.file.files[0])

    @flx.emitter
    def file_loaded(self, event):
        return {"new_value": event.target.result}

    @flx.emitter
    def file_selected(self):
        return {"new_value": self.node.value}

class FileUploader(flx.HBox):
    CSS = """
    .flx-Button[disabled="disabled"] {
      color: #888;
    }
    .flx-Button[disabled="disabled"]:hover {
      background: #e8e8e8;
    }
    """

    def init(self):
        EditLabel(text="File:")
        self.file_input = FileInput(flex=1, style="max-height: 28px", disabled=True)
        self.pick_file = flx.Button(text="...")
        self.do_upload = flx.Button(text="Upload", disabled=True)

    @flx.emitter
    def file_loaded(self, value):
        return {"new_value": value}

    @flx.reaction("pick_file.pointer_click")
    def on_pick_file(self, *events):
        self.file_input.select_file()

    @flx.reaction("do_upload.pointer_click")
    def on_do_upload(self, *events):
        self.file_input.load()

    @flx.reaction("file_input.file_selected")
    def on_file_selected(self, *events):
        self.enable_do_upload(bool(events[-1]["new_value"]))

    @flx.action
    def enable_do_upload(self, value):
        self.do_upload._mutate_disabled(not value)

    @flx.reaction("file_input.file_loaded")
    def on_file_loaded(self, *events):
        self.file_loaded(events[-1]["new_value"])

class App(flx.PyComponent):
    def init(self):
        with flx.Widget():
            with flx.VBox():
                self.uploader = FileUploader()
                self.textbox = flx.MultiLineEdit(flex=1)

    @flx.reaction("uploader.file_loaded")
    def on_file_loaded(self, *events):
        self.textbox.set_text(events[-1]["new_value"])

if __name__ == "__main__":
    app = flx.App(App)
    app.serve("")
    flx.start()
rwang0417 commented 2 years ago

This is super helpful. Thanks a lot for sharing Jaap!